Initial release
This commit is contained in:
commit
a430863701
|
@ -0,0 +1,32 @@
|
|||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
|
@ -0,0 +1,27 @@
|
|||
[submodule "lib/glad"]
|
||||
path = lib/glad
|
||||
url = https://github.com/Dav1dde/glad.git
|
||||
branch = c
|
||||
[submodule "lib/glfw"]
|
||||
path = lib/glfw
|
||||
url = https://github.com/glfw/glfw.git
|
||||
branch = master
|
||||
[submodule "lib/glm"]
|
||||
path = lib/glm
|
||||
url = https://github.com/g-truc/glm.git
|
||||
branch = master
|
||||
[submodule "lib/stb"]
|
||||
path = lib/stb
|
||||
url = https://github.com/nothings/stb.git
|
||||
branch = master
|
||||
[submodule "lib/arrrgh"]
|
||||
path = lib/arrrgh
|
||||
url = https://github.com/ElectricToy/arrrgh.git
|
||||
branch = master
|
||||
[submodule "lib/SFML"]
|
||||
path = lib/SFML
|
||||
url = https://github.com/SFML/SFML.git
|
||||
branch = master
|
||||
[submodule "arrrgh"]
|
||||
path = arrrgh
|
||||
url = https://github.com/ElectricToy/arrrgh.git
|
|
@ -0,0 +1,90 @@
|
|||
#
|
||||
# Specify minimum CMake version and project name
|
||||
#
|
||||
cmake_minimum_required (VERSION 3.0)
|
||||
project (glowbox)
|
||||
|
||||
#
|
||||
# CMake setup
|
||||
#
|
||||
set (CMAKE_CXX_STANDARD 17)
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
set (CMAKE_VERBOSE_MAKEFILE 0) # 1 should be used for debugging
|
||||
set (CMAKE_SUPPRESS_REGENERATION TRUE) # Suppresses ZERO_CHECK
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic")
|
||||
if(NOT WIN32)
|
||||
set(GLAD_LIBRARIES dl)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#
|
||||
# GLFW options
|
||||
#
|
||||
option (GLFW_INSTALL OFF)
|
||||
option (GLFW_BUILD_DOCS OFF)
|
||||
option (GLFW_BUILD_EXAMPLES OFF)
|
||||
option (GLFW_BUILD_TESTS OFF)
|
||||
add_subdirectory (lib/glfw)
|
||||
|
||||
|
||||
# SFML options
|
||||
# We only need sound for this project, so everything else can be turned off
|
||||
option (SFML_BUILD_GRAPHICS OFF)
|
||||
option (SFML_BUILD_WINDOW OFF)
|
||||
option (SFML_BUILD_NETWORK OFF)
|
||||
add_subdirectory(lib/SFML)
|
||||
|
||||
#
|
||||
# Set include paths
|
||||
#
|
||||
include_directories (src/
|
||||
lib/glad/include/
|
||||
lib/glfw/include/
|
||||
lib/glm/
|
||||
lib/stb/
|
||||
lib/arrrgh/
|
||||
lib/SFML/include/)
|
||||
|
||||
#
|
||||
# Add files
|
||||
#
|
||||
file (GLOB VENDORS_SOURCES lib/glad/src/glad.c)
|
||||
file (GLOB_RECURSE PROJECT_HEADERS src/*.hpp
|
||||
src/*.h)
|
||||
file (GLOB_RECURSE PROJECT_SOURCES src/*.cpp
|
||||
src/*.cxx
|
||||
src/*.cc
|
||||
src/*.c)
|
||||
file (GLOB_RECURSE PROJECT_SHADERS res/shaders/*.comp
|
||||
res/shaders/*.frag
|
||||
res/shaders/*.geom
|
||||
res/shaders/*.vert)
|
||||
file (GLOB PROJECT_CONFIGS CMakeLists.txt
|
||||
README.rst
|
||||
.gitignore
|
||||
.gitmodules)
|
||||
|
||||
#
|
||||
# Organizing files
|
||||
#
|
||||
source_group ("headers" FILES ${PROJECT_HEADERS})
|
||||
source_group ("shaders" FILES ${PROJECT_SHADERS})
|
||||
source_group ("sources" FILES ${PROJECT_SOURCES})
|
||||
source_group ("libraries" FILES ${VENDORS_SOURCES})
|
||||
|
||||
#
|
||||
# Set executable and target link libraries
|
||||
#
|
||||
add_definitions (-DGLFW_INCLUDE_NONE
|
||||
-DPROJECT_SOURCE_DIR=\"${PROJECT_SOURCE_DIR}\")
|
||||
add_executable (${PROJECT_NAME} ${PROJECT_SOURCES} ${PROJECT_HEADERS}
|
||||
${PROJECT_SHADERS} ${PROJECT_CONFIGS}
|
||||
${VENDORS_SOURCES})
|
||||
target_link_libraries (${PROJECT_NAME}
|
||||
glfw
|
||||
sfml-audio
|
||||
${GLFW_LIBRARIES}
|
||||
${GLAD_LIBRARIES})
|
|
@ -0,0 +1 @@
|
|||
Subproject commit b516a3ae2bd4b673e7e61c8fe3fd3deba79ebcc0
|
|
@ -0,0 +1 @@
|
|||
Subproject commit a4c3a105fb2c718556a5b71fe61f67c75eaa8bc1
|
|
@ -0,0 +1,3 @@
|
|||
!#/bin/bash
|
||||
cd glad
|
||||
python3 -m glad --profile core --out-path . --generator c --spec gl
|
|
@ -0,0 +1,3 @@
|
|||
cd glad
|
||||
python -m glad --profile core --out-path . --generator c --spec gl
|
||||
pause
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 8dcc73c4f5095362c44ca7f8b7e39374849d8e05
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 29d8ca4ce4aef8e0afca62b8ace9ee18a24a9ffd
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 8cd6db11cd8d2bf68aaad86097f15a9f94604dc4
|
|
@ -0,0 +1 @@
|
|||
Subproject commit e6afb9cbae4064da8c3e69af3ff5c4629579c1d2
|
|
@ -0,0 +1 @@
|
|||
sudo apt install libopenal-dev libvorbis-dev libflac-dev xorg-dev
|
|
@ -0,0 +1,5 @@
|
|||
I borrowed the Hall of the Mountain King track from here:
|
||||
|
||||
Hall of the Mountain King Kevin MacLeod (incompetech.com)
|
||||
Licensed under Creative Commons: By Attribution 3.0 License
|
||||
http://creativecommons.org/licenses/by/3.0/
|
Binary file not shown.
|
@ -0,0 +1,12 @@
|
|||
#version 430 core
|
||||
|
||||
in layout(location = 0) vec3 normal;
|
||||
|
||||
out vec4 color;
|
||||
|
||||
void main()
|
||||
{
|
||||
float intensity = dot(normalize(normal), vec3(0, 0, 1));
|
||||
color = vec4(0.5 * normal + 0.5, 1.0);
|
||||
//color = vec4(intensity, intensity, intensity, 1.0f);
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
#version 430 core
|
||||
|
||||
in layout(location = 0) vec3 position;
|
||||
in layout(location = 1) vec3 normal_in;
|
||||
|
||||
uniform layout(location = 3) mat4 MVP;
|
||||
|
||||
out layout(location = 0) vec3 normal_out;
|
||||
|
||||
void main()
|
||||
{
|
||||
normal_out = normal_in;
|
||||
gl_Position = MVP * vec4(position, 1.0f);
|
||||
}
|
|
@ -0,0 +1,343 @@
|
|||
#include <chrono>
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <glad/glad.h>
|
||||
#include <SFML/Audio/SoundBuffer.hpp>
|
||||
#include <utilities/shader.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
#include <iostream>
|
||||
#include <utilities/timeutils.h>
|
||||
#include <utilities/mesh.h>
|
||||
#include <utilities/shapes.h>
|
||||
#include <utilities/glutils.h>
|
||||
#include <SFML/Audio/Sound.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include "gamelogic.h"
|
||||
#include "sceneGraph.hpp"
|
||||
|
||||
|
||||
enum KeyFrameAction {
|
||||
BOTTOM, TOP
|
||||
};
|
||||
|
||||
#include <timestamps.h>
|
||||
|
||||
double padPositionX = 0;
|
||||
double padPositionY = 0;
|
||||
|
||||
unsigned int currentKeyFrame = 0;
|
||||
unsigned int previousKeyFrame = 0;
|
||||
|
||||
SceneNode* rootNode;
|
||||
SceneNode* boxNode;
|
||||
SceneNode* ballNode;
|
||||
SceneNode* padNode;
|
||||
|
||||
double ballRadius = 3.0f;
|
||||
|
||||
// These are heap allocated, because they should not be initialised at the start of the program
|
||||
sf::SoundBuffer* buffer;
|
||||
Gloom::Shader* shader;
|
||||
sf::Sound* sound;
|
||||
|
||||
const glm::vec3 boxDimensions(180, 90, 50);
|
||||
const glm::vec3 padDimensions(30, 3, 40);
|
||||
|
||||
glm::vec3 ballPosition(0, ballRadius + padDimensions.y, boxDimensions.z / 2);
|
||||
glm::vec3 ballDirection(1, 1, 0.02f);
|
||||
|
||||
const float BallVerticalTravelDistance = boxDimensions.y - 2.0 * ballRadius - padDimensions.y;
|
||||
|
||||
CommandLineOptions options;
|
||||
|
||||
bool hasStarted = false;
|
||||
bool hasLost = false;
|
||||
bool jumpedToNextFrame = false;
|
||||
|
||||
// Modify if you want the music to start further on in the track. Measured in seconds.
|
||||
const float debug_startTime = 0;
|
||||
double totalElapsedTime = debug_startTime;
|
||||
|
||||
|
||||
|
||||
void mouseCallback(GLFWwindow* window, double x, double y) {
|
||||
int windowWidth, windowHeight;
|
||||
glfwGetWindowSize(window, &windowWidth, &windowHeight);
|
||||
glViewport(0, 0, windowWidth, windowHeight);
|
||||
|
||||
padPositionX = x / double(windowWidth);
|
||||
padPositionY = y / double(windowHeight);
|
||||
|
||||
if(padPositionX > 1) {
|
||||
padPositionX = 1;
|
||||
glfwSetCursorPos(window, windowWidth, y);
|
||||
} else if(padPositionX < 0) {
|
||||
padPositionX = 0;
|
||||
glfwSetCursorPos(window, 0, y);
|
||||
}
|
||||
if(padPositionY > 1) {
|
||||
padPositionY = 1;
|
||||
glfwSetCursorPos(window, x, windowHeight);
|
||||
} else if(padPositionY < 0) {
|
||||
padPositionY = 0;
|
||||
glfwSetCursorPos(window, x, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void initGame(GLFWwindow* window, CommandLineOptions gameOptions) {
|
||||
buffer = new sf::SoundBuffer();
|
||||
if (!buffer->loadFromFile("../res/Hall of the Mountain King.ogg")) {
|
||||
return;
|
||||
}
|
||||
|
||||
options = gameOptions;
|
||||
|
||||
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
|
||||
glfwSetCursorPosCallback(window, mouseCallback);
|
||||
|
||||
shader = new Gloom::Shader();
|
||||
shader->makeBasicShader("../res/shaders/simple.vert", "../res/shaders/simple.frag");
|
||||
shader->activate();
|
||||
|
||||
Mesh box = generateBox(boxDimensions.x, boxDimensions.y, boxDimensions.z, true);
|
||||
Mesh pad = generateBox(padDimensions.x, padDimensions.y, padDimensions.z, false);
|
||||
Mesh sphere = generateSphere(1.0, 40, 40);
|
||||
|
||||
unsigned int ballVAO = generateBuffer(sphere);
|
||||
unsigned int boxVAO = generateBuffer(box);
|
||||
unsigned int padVAO = generateBuffer(pad);
|
||||
|
||||
rootNode = createSceneNode();
|
||||
boxNode = createSceneNode();
|
||||
padNode = createSceneNode();
|
||||
ballNode = createSceneNode();
|
||||
|
||||
rootNode->children.push_back(boxNode);
|
||||
rootNode->children.push_back(padNode);
|
||||
rootNode->children.push_back(ballNode);
|
||||
|
||||
boxNode->vertexArrayObjectID = boxVAO;
|
||||
boxNode->VAOIndexCount = box.indices.size();
|
||||
|
||||
padNode->vertexArrayObjectID = padVAO;
|
||||
padNode->VAOIndexCount = pad.indices.size();
|
||||
|
||||
ballNode->vertexArrayObjectID = ballVAO;
|
||||
ballNode->VAOIndexCount = sphere.indices.size();
|
||||
|
||||
getTimeDeltaSeconds();
|
||||
|
||||
std::cout << "Ready. Click to start!" << std::endl;
|
||||
}
|
||||
|
||||
void updateNodeTransformations(SceneNode* node, glm::mat4 transformationThusFar) {
|
||||
|
||||
glm::mat4 transformationMatrix(1.0);
|
||||
|
||||
switch(node->nodeType) {
|
||||
case GEOMETRY:
|
||||
transformationMatrix =
|
||||
glm::translate(glm::mat4(1.0), node->position)
|
||||
* glm::translate(glm::mat4(1.0), node->referencePoint)
|
||||
* glm::rotate(glm::mat4(1.0), node->rotation.z, glm::vec3(0,0,1))
|
||||
* glm::rotate(glm::mat4(1.0), node->rotation.y, glm::vec3(0,1,0))
|
||||
* glm::rotate(glm::mat4(1.0), node->rotation.x, glm::vec3(1,0,0))
|
||||
* glm::translate(glm::mat4(1.0), -node->referencePoint)
|
||||
* glm::scale(glm::mat4(1.0), node->scale);
|
||||
break;
|
||||
case POINT_LIGHT:
|
||||
|
||||
break;
|
||||
case SPOT_LIGHT:
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
node->currentTransformationMatrix = transformationThusFar * transformationMatrix;
|
||||
|
||||
for(SceneNode* child : node->children) {
|
||||
updateNodeTransformations(child, node->currentTransformationMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
void updateFrame(GLFWwindow* window) {
|
||||
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
||||
|
||||
double timeDelta = getTimeDeltaSeconds();
|
||||
|
||||
if(!hasStarted) {
|
||||
|
||||
if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1)) {
|
||||
if (options.enableMusic) {
|
||||
sound = new sf::Sound();
|
||||
sound->setBuffer(*buffer);
|
||||
sf::Time startTime = sf::seconds(debug_startTime);
|
||||
sound->setPlayingOffset(startTime);
|
||||
sound->play();
|
||||
}
|
||||
totalElapsedTime = debug_startTime;
|
||||
hasStarted = true;
|
||||
}
|
||||
|
||||
ballPosition.x = (1 - padPositionX) * (boxDimensions.x - padDimensions.x) + padDimensions.x / 2.0;
|
||||
ballPosition.y = ballRadius + padDimensions.y;
|
||||
} else {
|
||||
|
||||
// I really should calculate this using the std::chrono timestamp for this
|
||||
// You definitely end up with a cumulative error when doing lots of small additions like this
|
||||
// However, for a game that lasts only a few minutes this is fine.
|
||||
totalElapsedTime += timeDelta;
|
||||
|
||||
if(hasLost) {
|
||||
ballRadius += 200 * timeDelta;
|
||||
if(ballRadius > 999999) {
|
||||
ballRadius = 999999;
|
||||
}
|
||||
} else {
|
||||
for (unsigned int i = currentKeyFrame; i < keyFrameTimeStamps.size(); i++) {
|
||||
if (totalElapsedTime < keyFrameTimeStamps.at(i)) {
|
||||
continue;
|
||||
}
|
||||
currentKeyFrame = i;
|
||||
}
|
||||
|
||||
jumpedToNextFrame = currentKeyFrame != previousKeyFrame;
|
||||
previousKeyFrame = currentKeyFrame;
|
||||
|
||||
double frameStart = keyFrameTimeStamps.at(currentKeyFrame);
|
||||
double frameEnd = keyFrameTimeStamps.at(currentKeyFrame + 1); // Assumes last keyframe at infinity
|
||||
|
||||
double elapsedTimeInFrame = totalElapsedTime - frameStart;
|
||||
double frameDuration = frameEnd - frameStart;
|
||||
double fractionFrameComplete = elapsedTimeInFrame / frameDuration;
|
||||
|
||||
double ballYCoord;
|
||||
|
||||
const float ballBottomY = ballRadius + padDimensions.y;
|
||||
|
||||
KeyFrameAction currentOrigin = keyFrameDirections.at(currentKeyFrame);
|
||||
KeyFrameAction currentDestination = keyFrameDirections.at(currentKeyFrame + 1);
|
||||
|
||||
if (currentOrigin == BOTTOM && currentDestination == BOTTOM) {
|
||||
ballYCoord = ballBottomY;
|
||||
} else if (currentOrigin == TOP && currentDestination == TOP) {
|
||||
ballYCoord = ballBottomY + BallVerticalTravelDistance;
|
||||
} else if (currentDestination == BOTTOM) {
|
||||
ballYCoord = ballBottomY + BallVerticalTravelDistance * (1 - fractionFrameComplete);
|
||||
} else if (currentDestination == TOP) {
|
||||
ballYCoord = ballBottomY + BallVerticalTravelDistance * fractionFrameComplete;
|
||||
}
|
||||
|
||||
|
||||
const float ballSpeed = 60.0f;
|
||||
|
||||
ballPosition.x += timeDelta * ballSpeed * ballDirection.x;
|
||||
ballPosition.y = ballYCoord;
|
||||
ballPosition.z += timeDelta * ballSpeed * ballDirection.z;
|
||||
|
||||
if (ballPosition.x + ballRadius > boxDimensions.x) {
|
||||
// Crude approximation, because it does not compute the intersection with the wall
|
||||
// Not doing it causes the ball to get stuck in the wall though
|
||||
ballPosition.x = boxDimensions.x - ballRadius;
|
||||
ballDirection.x *= -1;
|
||||
} else if (ballPosition.x - ballRadius < 0) {
|
||||
ballPosition.x = ballRadius;
|
||||
ballDirection.x *= -1;
|
||||
}
|
||||
|
||||
if (ballPosition.y + ballRadius > boxDimensions.y) {
|
||||
ballPosition.y = boxDimensions.y - ballRadius;
|
||||
ballDirection.y *= -1;
|
||||
} else if (ballPosition.y - ballRadius < 0) {
|
||||
ballPosition.y = ballRadius;
|
||||
ballDirection.y *= -1;
|
||||
}
|
||||
|
||||
if (ballPosition.z + ballRadius > boxDimensions.z) {
|
||||
ballPosition.z = boxDimensions.z - ballRadius;
|
||||
ballDirection.z *= -1;
|
||||
} else if (ballPosition.z - ballRadius < 0) {
|
||||
ballPosition.z = ballRadius;
|
||||
ballDirection.z *= -1;
|
||||
}
|
||||
|
||||
if(options.enableAutoplay) {
|
||||
padPositionX = 1 - (ballPosition.x / (boxDimensions.x - 2 * ballRadius));
|
||||
padPositionY = 1 - (ballPosition.z / (boxDimensions.z - 2 * ballRadius));
|
||||
}
|
||||
|
||||
// Check if the ball is hitting the pad when the ball is at the bottom.
|
||||
// If not, you just lost the game! (hehe)
|
||||
if (jumpedToNextFrame && currentOrigin == BOTTOM && currentDestination == TOP) {
|
||||
double padLeftXCoordinate = (1 - padPositionX) * (boxDimensions.x - padDimensions.x);
|
||||
double padRightXCoordinate = padLeftXCoordinate + padDimensions.x;
|
||||
|
||||
double padFrontZCoordinate = (1 - padPositionY) * (boxDimensions.z - padDimensions.z);
|
||||
double padBackZCoordinate = padFrontZCoordinate + padDimensions.z;
|
||||
|
||||
if (ballPosition.x < padLeftXCoordinate
|
||||
|| ballPosition.x > padRightXCoordinate
|
||||
|| ballPosition.z < padFrontZCoordinate
|
||||
|| ballPosition.z > padBackZCoordinate) {
|
||||
hasLost = true;
|
||||
if (options.enableMusic) {
|
||||
sound->stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glm::mat4 projection = glm::perspective(glm::radians(90.0f), float(windowWidth) / float(windowHeight), 0.1f,
|
||||
120.f);
|
||||
|
||||
glm::mat4 cameraTransform = glm::translate(glm::mat4(1), glm::vec3(0, 0, 0))
|
||||
* glm::rotate(glm::mat4(1.0), 0.2f, glm::vec3(1, 0, 0))
|
||||
* glm::rotate(glm::mat4(1.0), float(M_PI), glm::vec3(0, 1, 0));
|
||||
|
||||
glm::mat4 VP = projection * cameraTransform;
|
||||
|
||||
updateNodeTransformations(rootNode, VP);
|
||||
|
||||
boxNode->position = {-boxDimensions.x / 2, -boxDimensions.y / 2 - 15, boxDimensions.z - 10};
|
||||
padNode->position = {-boxDimensions.x / 2 + (1 - padPositionX) * (boxDimensions.x - padDimensions.x),
|
||||
-boxDimensions.y / 2 - 15,
|
||||
boxDimensions.z - 10 + (1 - padPositionY) * (boxDimensions.z - padDimensions.z)};
|
||||
ballNode->position = {-boxDimensions.x / 2 + ballPosition.x,
|
||||
-boxDimensions.y / 2 - 15 + ballPosition.y,
|
||||
boxDimensions.z - 10 + ballPosition.z};
|
||||
|
||||
ballNode->scale = {ballRadius, ballRadius, ballRadius};
|
||||
}
|
||||
|
||||
|
||||
void renderNode(SceneNode* node) {
|
||||
glUniformMatrix4fv(3, 1, GL_FALSE, glm::value_ptr(node->currentTransformationMatrix));
|
||||
|
||||
switch(node->nodeType) {
|
||||
case GEOMETRY:
|
||||
if(node->vertexArrayObjectID != -1) {
|
||||
glBindVertexArray(node->vertexArrayObjectID);
|
||||
glDrawElements(GL_TRIANGLES, node->VAOIndexCount, GL_UNSIGNED_INT, nullptr);
|
||||
}
|
||||
break;
|
||||
case POINT_LIGHT:
|
||||
|
||||
break;
|
||||
case SPOT_LIGHT:
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
for(SceneNode* child : node->children) {
|
||||
renderNode(child);
|
||||
}
|
||||
}
|
||||
|
||||
void renderFrame(GLFWwindow* window) {
|
||||
int windowWidth, windowHeight;
|
||||
glfwGetWindowSize(window, &windowWidth, &windowHeight);
|
||||
glViewport(0, 0, windowWidth, windowHeight);
|
||||
|
||||
renderNode(rootNode);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
#include <utilities/window.hpp>
|
||||
|
||||
void initGame(GLFWwindow* window, CommandLineOptions options);
|
||||
void updateFrame(GLFWwindow* window);
|
||||
void renderFrame(GLFWwindow* window);
|
|
@ -0,0 +1,112 @@
|
|||
// Local headers
|
||||
#include "utilities/window.hpp"
|
||||
#include "program.hpp"
|
||||
|
||||
// System headers
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
// Standard headers
|
||||
#include <cstdlib>
|
||||
#include <arrrgh.hpp>
|
||||
|
||||
|
||||
// A callback which allows GLFW to report errors whenever they occur
|
||||
static void glfwErrorCallback(int error, const char *description)
|
||||
{
|
||||
fprintf(stderr, "GLFW returned an error:\n\t%s (%i)\n", description, error);
|
||||
}
|
||||
|
||||
|
||||
GLFWwindow* initialise()
|
||||
{
|
||||
// Initialise GLFW
|
||||
if (!glfwInit())
|
||||
{
|
||||
fprintf(stderr, "Could not start GLFW\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Set core window options (adjust version numbers if needed)
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
|
||||
// Enable the GLFW runtime error callback function defined previously.
|
||||
glfwSetErrorCallback(glfwErrorCallback);
|
||||
|
||||
// Set additional window options
|
||||
glfwWindowHint(GLFW_RESIZABLE, windowResizable);
|
||||
glfwWindowHint(GLFW_SAMPLES, windowSamples); // MSAA
|
||||
|
||||
// Create window using GLFW
|
||||
GLFWwindow* window = glfwCreateWindow(windowWidth,
|
||||
windowHeight,
|
||||
windowTitle.c_str(),
|
||||
nullptr,
|
||||
nullptr);
|
||||
|
||||
// Ensure the window is set up correctly
|
||||
if (!window)
|
||||
{
|
||||
fprintf(stderr, "Could not open GLFW window\n");
|
||||
glfwTerminate();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Let the window be the current OpenGL context and initialise glad
|
||||
glfwMakeContextCurrent(window);
|
||||
gladLoadGL();
|
||||
|
||||
// Print various OpenGL information to stdout
|
||||
printf("%s: %s\n", glGetString(GL_VENDOR), glGetString(GL_RENDERER));
|
||||
printf("GLFW\t %s\n", glfwGetVersionString());
|
||||
printf("OpenGL\t %s\n", glGetString(GL_VERSION));
|
||||
printf("GLSL\t %s\n\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, const char* argb[])
|
||||
{
|
||||
arrrgh::parser parser("glowbox", "Small breakout like juggling game");
|
||||
const auto& showHelp = parser.add<bool>("help", "Show this help message.", 'h', arrrgh::Optional, false);
|
||||
const auto& enableMusic = parser.add<bool>("enable-music", "Play background music while the game is playing", 'm', arrrgh::Optional, false);
|
||||
const auto& enableAutoplay = parser.add<bool>("autoplay", "Let the game play itself automatically. Useful for testing.", 'a', arrrgh::Optional, false);
|
||||
|
||||
// If you want to add more program arguments, define them here,
|
||||
// but do not request their value here (they have not been parsed yet at this point).
|
||||
|
||||
try
|
||||
{
|
||||
parser.parse(argc, argb);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cerr << "Error parsing arguments: " << e.what() << std::endl;
|
||||
parser.show_usage(std::cerr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Show help if desired
|
||||
if(showHelp.value())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
CommandLineOptions options;
|
||||
options.enableMusic = enableMusic.value();
|
||||
options.enableAutoplay = enableAutoplay.value();
|
||||
|
||||
// Initialise window using GLFW
|
||||
GLFWwindow* window = initialise();
|
||||
|
||||
// Run an OpenGL application using this window
|
||||
runProgram(window, options);
|
||||
|
||||
// Terminate GLFW (no need to call glfwDestroyWindow)
|
||||
glfwTerminate();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
// Local headers
|
||||
#include "program.hpp"
|
||||
#include "utilities/window.hpp"
|
||||
#include "gamelogic.h"
|
||||
#include <glm/glm.hpp>
|
||||
// glm::translate, glm::rotate, glm::scale, glm::perspective
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <iostream>
|
||||
#include <SFML/Audio.hpp>
|
||||
#include <SFML/System/Time.hpp>
|
||||
#include <utilities/shapes.h>
|
||||
#include <utilities/glutils.h>
|
||||
#include <utilities/shader.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <utilities/timeutils.h>
|
||||
|
||||
|
||||
void runProgram(GLFWwindow* window, CommandLineOptions options)
|
||||
{
|
||||
// Enable depth (Z) buffer (accept "closest" fragment)
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_LESS);
|
||||
|
||||
// Configure miscellaneous OpenGL settings
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
// Set default colour after clearing the colour buffer
|
||||
glClearColor(0.3f, 0.5f, 0.8f, 1.0f);
|
||||
|
||||
initGame(window, options);
|
||||
|
||||
// Rendering Loop
|
||||
while (!glfwWindowShouldClose(window))
|
||||
{
|
||||
// Clear colour and depth buffers
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
|
||||
updateFrame(window);
|
||||
renderFrame(window);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Handle other events
|
||||
glfwPollEvents();
|
||||
handleKeyboardInput(window);
|
||||
|
||||
// Flip buffers
|
||||
glfwSwapBuffers(window);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void handleKeyboardInput(GLFWwindow* window)
|
||||
{
|
||||
// Use escape key for terminating the GLFW window
|
||||
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
|
||||
{
|
||||
glfwSetWindowShouldClose(window, GL_TRUE);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
#ifndef PROGRAM_HPP
|
||||
#define PROGRAM_HPP
|
||||
#pragma once
|
||||
|
||||
|
||||
// System headers
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <glad/glad.h>
|
||||
#include <string>
|
||||
#include <utilities/window.hpp>
|
||||
|
||||
|
||||
// Main OpenGL program
|
||||
void runProgram(GLFWwindow* window, CommandLineOptions options);
|
||||
|
||||
|
||||
// Function for handling keypresses
|
||||
void handleKeyboardInput(GLFWwindow* window);
|
||||
|
||||
|
||||
// Checks for whether an OpenGL error occurred. If one did,
|
||||
// it prints out the error type and ID
|
||||
inline void printGLError() {
|
||||
int errorID = glGetError();
|
||||
|
||||
if(errorID != GL_NO_ERROR) {
|
||||
std::string errorString;
|
||||
|
||||
switch(errorID) {
|
||||
case GL_INVALID_ENUM:
|
||||
errorString = "GL_INVALID_ENUM";
|
||||
break;
|
||||
case GL_INVALID_OPERATION:
|
||||
errorString = "GL_INVALID_OPERATION";
|
||||
break;
|
||||
case GL_INVALID_FRAMEBUFFER_OPERATION:
|
||||
errorString = "GL_INVALID_FRAMEBUFFER_OPERATION";
|
||||
break;
|
||||
case GL_OUT_OF_MEMORY:
|
||||
errorString = "GL_OUT_OF_MEMORY";
|
||||
break;
|
||||
case GL_STACK_UNDERFLOW:
|
||||
errorString = "GL_STACK_UNDERFLOW";
|
||||
break;
|
||||
case GL_STACK_OVERFLOW:
|
||||
errorString = "GL_STACK_OVERFLOW";
|
||||
break;
|
||||
default:
|
||||
errorString = "[Unknown error ID]";
|
||||
break;
|
||||
}
|
||||
|
||||
fprintf(stderr, "An OpenGL error occurred (%i): %s.\n",
|
||||
errorID, errorString.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,29 @@
|
|||
#include "sceneGraph.hpp"
|
||||
#include <iostream>
|
||||
|
||||
SceneNode* createSceneNode() {
|
||||
return new SceneNode();
|
||||
}
|
||||
|
||||
// Add a child node to its parent's list of children
|
||||
void addChild(SceneNode* parent, SceneNode* child) {
|
||||
parent->children.push_back(child);
|
||||
}
|
||||
|
||||
// Pretty prints the current values of a SceneNode instance to stdout
|
||||
void printNode(SceneNode* node) {
|
||||
printf(
|
||||
"SceneNode {\n"
|
||||
" Child count: %i\n"
|
||||
" Rotation: (%f, %f, %f)\n"
|
||||
" Location: (%f, %f, %f)\n"
|
||||
" Reference point: (%f, %f, %f)\n"
|
||||
" VAO ID: %i\n"
|
||||
"}\n",
|
||||
int(node->children.size()),
|
||||
node->rotation.x, node->rotation.y, node->rotation.z,
|
||||
node->position.x, node->position.y, node->position.z,
|
||||
node->referencePoint.x, node->referencePoint.y, node->referencePoint.z,
|
||||
node->vertexArrayObjectID);
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#include <stack>
|
||||
#include <vector>
|
||||
#include <cstdio>
|
||||
#include <stdbool.h>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
|
||||
enum SceneNodeType {
|
||||
GEOMETRY, POINT_LIGHT, SPOT_LIGHT
|
||||
};
|
||||
|
||||
// In case you haven't got much experience with C or C++, let me explain this "typedef" you see below.
|
||||
// The point of a typedef is that you it, as its name implies, allows you to define arbitrary data types based upon existing ones. For instance, "typedef float typeWhichMightBeAFloat;" allows you to define a variable such as this one: "typeWhichMightBeAFloat variableName = 5.0;". The C/C++ compiler translates this type into a float.
|
||||
// What is the point of using it here? A smrt person, while designing the C language, thought it would be a good idea for various reasons to force you to explicitly state that you are using a data structure datatype (struct). So, when defining a variable, you'd have to type "struct SceneNode node = ..." in the case of a SceneNode. Which can get in the way of readability.
|
||||
// If we just use typedef to define a new type called "SceneNode", which really is the type "struct SceneNode", we can omit the "struct" part when creating an instance of SceneNode.
|
||||
struct SceneNode {
|
||||
SceneNode() {
|
||||
position = glm::vec3(0, 0, 0);
|
||||
rotation = glm::vec3(0, 0, 0);
|
||||
scale = glm::vec3(1, 1, 1);
|
||||
|
||||
referencePoint = glm::vec3(0, 0, 0);
|
||||
vertexArrayObjectID = -1;
|
||||
VAOIndexCount = 0;
|
||||
|
||||
nodeType = GEOMETRY;
|
||||
}
|
||||
|
||||
// A list of all children that belong to this node.
|
||||
// For instance, in case of the scene graph of a human body shown in the assignment text, the "Upper Torso" node would contain the "Left Arm", "Right Arm", "Head" and "Lower Torso" nodes in its list of children.
|
||||
std::vector<SceneNode*> children;
|
||||
|
||||
// The node's position and rotation relative to its parent
|
||||
glm::vec3 position;
|
||||
glm::vec3 rotation;
|
||||
glm::vec3 scale;
|
||||
|
||||
// A transformation matrix representing the transformation of the node's location relative to its parent. This matrix is updated every frame.
|
||||
glm::mat4 currentTransformationMatrix;
|
||||
|
||||
// The location of the node's reference point
|
||||
glm::vec3 referencePoint;
|
||||
|
||||
// The ID of the VAO containing the "appearance" of this SceneNode.
|
||||
int vertexArrayObjectID;
|
||||
unsigned int VAOIndexCount;
|
||||
|
||||
// Node type is used to determine how to handle the contents of a node
|
||||
SceneNodeType nodeType;
|
||||
};
|
||||
|
||||
// Struct for keeping track of 2D coordinates
|
||||
|
||||
SceneNode* createSceneNode();
|
||||
void addChild(SceneNode* parent, SceneNode* child);
|
||||
void printNode(SceneNode* node);
|
||||
|
||||
|
||||
// For more details, see SceneGraph.cpp.
|
|
@ -0,0 +1,361 @@
|
|||
#pragma once
|
||||
|
||||
// I recommend closing this file right now.
|
||||
// You'll only find despair here
|
||||
// And cries of "WHY"
|
||||
// Proceed at your own risk.
|
||||
|
||||
const std::vector<double> keyFrameTimeStamps =
|
||||
{0, 0.98,
|
||||
|
||||
1.570, 2.102, // block 0
|
||||
2.658, 3.229,
|
||||
3.781, 4.349,
|
||||
4.926, 5.562,
|
||||
6.083, 6.630,
|
||||
7.177, 7.733,
|
||||
8.343, 8.838,
|
||||
9.436, 9.974,
|
||||
10.506, 11.086,
|
||||
11.660, 12.210,
|
||||
|
||||
12.729, 13.333,
|
||||
13.916, 14.402,
|
||||
14.979, 15.526,
|
||||
16.102, 16.611,
|
||||
17.158, 17.725,
|
||||
18.285, 18.864,
|
||||
19.408, 19.963,
|
||||
20.482, 21.041,
|
||||
21.585, 22.141,
|
||||
22.697, 23.286,
|
||||
|
||||
23.808, 24.340,
|
||||
24.750, 25.447,
|
||||
26.000, 26.575,
|
||||
27.134, 27.684,
|
||||
28.227, 28.775,
|
||||
29.359, 29.928,
|
||||
30.485, 31.076,
|
||||
31.603, 32.176,
|
||||
32.721, 33.284,
|
||||
33.808, 34.388,
|
||||
|
||||
34.962, 35.514,
|
||||
36.125, 36.704,
|
||||
37.281, 37.849,
|
||||
38.370, 38.948,
|
||||
39.500, 40.009,
|
||||
40.551, 41.198,
|
||||
41.692, 42.274,
|
||||
42.840, 43.383,
|
||||
43.940, 44.516,
|
||||
45.062, 45.610,
|
||||
|
||||
46.189, 46.732,
|
||||
47.314, 47.875,
|
||||
48.441, 49.023,
|
||||
49.589, 50.138,
|
||||
50.675, 51.237,
|
||||
51.767, 52.312,
|
||||
52.882, 53.439,
|
||||
53.970, 54.578,
|
||||
55.121, 55.592,
|
||||
56.112, 56.691,
|
||||
|
||||
57.179, 57.663, // block 5
|
||||
58.223, 58.735,
|
||||
59.293, 59.790,
|
||||
60.327, 60.822,
|
||||
61.328, 61.862,
|
||||
62.375, 62.869,
|
||||
63.362, 63.830,
|
||||
64.365, 64.880,
|
||||
65.342, 65.900,
|
||||
66.386, 66.883,
|
||||
|
||||
67.375, 67.860,
|
||||
68.364, 68.835,
|
||||
69.343, 69.970,
|
||||
70.340, 70.857,
|
||||
71.301, 71.860,
|
||||
72.301, 72.768,
|
||||
73.257, 73.732,
|
||||
74.213, 74.685,
|
||||
75.148, 75.649,
|
||||
76.127, 76.573,
|
||||
|
||||
77.049, 77.514,
|
||||
77.986, 78.433,
|
||||
78.898, 79.384,
|
||||
79.850, 80.309,
|
||||
80.746, 81.208,
|
||||
81.638, 82.057,
|
||||
82.503, 82.946,
|
||||
83.410, 83.847,
|
||||
84.263, 84.683,
|
||||
85.169, 85.551,
|
||||
|
||||
85.979, 86.417,
|
||||
86.862, 87.272,
|
||||
87.715, 88.121,
|
||||
88.538, 88.944,
|
||||
89.404, 89.761,
|
||||
90.214, 90.610,
|
||||
91.034, 91.427,
|
||||
91.842, 92.239,
|
||||
92.645, 92.991,
|
||||
93.359, 93.722,
|
||||
|
||||
94.095, 94.495,
|
||||
94.874, 95.242,
|
||||
95.605, 95.978,
|
||||
96.351, 96.714,
|
||||
97.098, 97.461,
|
||||
97.883, 98.240,
|
||||
98.602, 98.986,
|
||||
99.468, 99.760,
|
||||
100.123, 100.442,
|
||||
100.831, 101.189,
|
||||
|
||||
101.611, 102.011, // Block 10
|
||||
102.336, 102.612,
|
||||
102.953, 103.331,
|
||||
103.710, 104.046,
|
||||
104.408, 104.787,
|
||||
105.187, 105.479,
|
||||
105.847, 106.188,
|
||||
106.475, 106.854,
|
||||
107.162, 107.536,
|
||||
107.968, 108.239,
|
||||
|
||||
108.661, 108.953,
|
||||
109.262, 109.581,
|
||||
109.927, 110.273,
|
||||
110.652, 110.988,
|
||||
111.312, 111.642,
|
||||
112.000, 112.330,
|
||||
112.660, 113.033,
|
||||
113.314, 113.639,
|
||||
113.958, 114.310,
|
||||
114.656, 115.040,
|
||||
|
||||
115.365, 115.690,
|
||||
116.031, 116.350,
|
||||
116.669, 116.999,
|
||||
117.302, 117.638,
|
||||
117.962, 118.287,
|
||||
118.628, 118.925,
|
||||
119.250, 119.569,
|
||||
119.937, 120.257,
|
||||
120.565, 120.868,
|
||||
121.252, 121.533,
|
||||
|
||||
121.880, 122.210,
|
||||
122.540, 122.827,
|
||||
123.146, 123.427,
|
||||
123.757, 124.060,
|
||||
124.407, 124.715,
|
||||
125.023, 125.364,
|
||||
125.673, 125.927,
|
||||
126.273, 126.555,
|
||||
126.896, 127.204,
|
||||
127.491, 127.788,
|
||||
|
||||
128.097, 128.378,
|
||||
128.714, 128.963,
|
||||
129.325, 129.569,
|
||||
129.872, 130.164,
|
||||
|
||||
130.667, 130.877, 131.132,
|
||||
133.654, 133.842, 134.054,
|
||||
134.568, 134.768, 135.034,
|
||||
137.468, 137.707, 137.939,
|
||||
138.388, 138.648, 138.902,
|
||||
139.346, 139.590, 139.908,
|
||||
139.379, 139.590, 139.871,
|
||||
|
||||
140.098, 140.352,
|
||||
140.574, 140.850,
|
||||
141.105, 141.343,
|
||||
|
||||
144.286, 144.367, 144.595,
|
||||
|
||||
9999999};
|
||||
const std::vector<KeyFrameAction> keyFrameDirections =
|
||||
{BOTTOM, TOP,
|
||||
|
||||
BOTTOM, TOP, // Block 0
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
|
||||
BOTTOM, TOP, // Block 5
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
|
||||
BOTTOM, TOP, // Block 10
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
|
||||
TOP, BOTTOM, TOP,
|
||||
TOP, BOTTOM, TOP,
|
||||
TOP, BOTTOM, TOP,
|
||||
TOP, BOTTOM, TOP,
|
||||
TOP, BOTTOM, TOP,
|
||||
TOP, BOTTOM, TOP,
|
||||
TOP, BOTTOM, TOP,
|
||||
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
BOTTOM, TOP,
|
||||
|
||||
TOP, BOTTOM, TOP,
|
||||
|
||||
BOTTOM,};
|
|
@ -0,0 +1,200 @@
|
|||
#ifndef CAMERA_HPP
|
||||
#define CAMERA_HPP
|
||||
#pragma once
|
||||
|
||||
// System headers
|
||||
#include <glad/glad.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
|
||||
namespace Gloom
|
||||
{
|
||||
class Camera
|
||||
{
|
||||
public:
|
||||
Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 2.0f),
|
||||
GLfloat movementSpeed = 5.0f,
|
||||
GLfloat mouseSensitivity = 0.005f)
|
||||
{
|
||||
cPosition = position;
|
||||
cMovementSpeed = movementSpeed;
|
||||
cMouseSensitivity = mouseSensitivity;
|
||||
|
||||
// Set up the initial view matrix
|
||||
updateViewMatrix();
|
||||
}
|
||||
|
||||
// Public member functions
|
||||
|
||||
/* Getter for the view matrix */
|
||||
glm::mat4 getViewMatrix() { return matView; }
|
||||
|
||||
|
||||
/* Handle keyboard inputs from a callback mechanism */
|
||||
void handleKeyboardInputs(int key, int action)
|
||||
{
|
||||
// Keep track of pressed/released buttons
|
||||
if (key >= 0 && key < 512)
|
||||
{
|
||||
if (action == GLFW_PRESS)
|
||||
{
|
||||
keysInUse[key] = true;
|
||||
}
|
||||
else if (action == GLFW_RELEASE)
|
||||
{
|
||||
keysInUse[key] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Handle mouse button inputs from a callback mechanism */
|
||||
void handleMouseButtonInputs(int button, int action)
|
||||
{
|
||||
// Ensure that the camera only rotates when the left mouse button is
|
||||
// pressed
|
||||
if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS)
|
||||
{
|
||||
isMousePressed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
isMousePressed = false;
|
||||
resetMouse = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Handle cursor position from a callback mechanism */
|
||||
void handleCursorPosInput(double xpos, double ypos)
|
||||
{
|
||||
// Do nothing if the left mouse button is not pressed
|
||||
if (isMousePressed == false)
|
||||
return;
|
||||
|
||||
// There should be no movement when the mouse button is released
|
||||
if (resetMouse)
|
||||
{
|
||||
lastXPos = xpos;
|
||||
lastYPos = ypos;
|
||||
resetMouse = false;
|
||||
}
|
||||
|
||||
// Keep track of pitch and yaw for the current frame
|
||||
fYaw = xpos - lastXPos;
|
||||
fPitch = ypos - lastYPos;
|
||||
|
||||
// Update last known cursor position
|
||||
lastXPos = xpos;
|
||||
lastYPos = ypos;
|
||||
}
|
||||
|
||||
|
||||
/* Update the camera position and view matrix
|
||||
`deltaTime` is the time between the current and last frame */
|
||||
void updateCamera(GLfloat deltaTime)
|
||||
{
|
||||
// Extract movement information from the view matrix
|
||||
glm::vec3 dirX(matView[0][0], matView[1][0], matView[2][0]);
|
||||
glm::vec3 dirY(matView[0][1], matView[1][1], matView[2][1]);
|
||||
glm::vec3 dirZ(matView[0][2], matView[1][2], matView[2][2]);
|
||||
|
||||
// Alter position in the appropriate direction
|
||||
glm::vec3 fMovement(0.0f, 0.0f, 0.0f);
|
||||
|
||||
if (keysInUse[GLFW_KEY_W]) // forward
|
||||
fMovement -= dirZ;
|
||||
|
||||
if (keysInUse[GLFW_KEY_S]) // backward
|
||||
fMovement += dirZ;
|
||||
|
||||
if (keysInUse[GLFW_KEY_A]) // left
|
||||
fMovement -= dirX;
|
||||
|
||||
if (keysInUse[GLFW_KEY_D]) // right
|
||||
fMovement += dirX;
|
||||
|
||||
if (keysInUse[GLFW_KEY_E]) // vertical up
|
||||
fMovement += dirY;
|
||||
|
||||
if (keysInUse[GLFW_KEY_Q]) // vertical down
|
||||
fMovement -= dirY;
|
||||
|
||||
// Trick to balance PC speed with movement
|
||||
GLfloat velocity = cMovementSpeed * deltaTime;
|
||||
|
||||
// Update camera position using the appropriate velocity
|
||||
cPosition += fMovement * velocity;
|
||||
|
||||
// Update the view matrix based on the new information
|
||||
updateViewMatrix();
|
||||
}
|
||||
|
||||
private:
|
||||
// Disable copying and assignment
|
||||
Camera(Camera const &) = delete;
|
||||
Camera & operator =(Camera const &) = delete;
|
||||
|
||||
// Private member function
|
||||
|
||||
/* Update the view matrix based on the current information */
|
||||
void updateViewMatrix()
|
||||
{
|
||||
// Adjust cursor movement using the specified sensitivity
|
||||
fPitch *= cMouseSensitivity;
|
||||
fYaw *= cMouseSensitivity;
|
||||
|
||||
// Create quaternions given the current pitch and yaw
|
||||
glm::quat qPitch = glm::quat(glm::vec3(fPitch, 0.0f, 0.0f));
|
||||
glm::quat qYaw = glm::quat(glm::vec3(0.0f, fYaw, 0.0f));
|
||||
|
||||
// Reset pitch and yaw values for the current rotation
|
||||
fPitch = 0.0f;
|
||||
fYaw = 0.0f;
|
||||
|
||||
// Update camera quaternion and normalise
|
||||
cQuaternion = qYaw * qPitch * cQuaternion;
|
||||
cQuaternion = glm::normalize(cQuaternion);
|
||||
|
||||
// Build rotation matrix using the camera quaternion
|
||||
glm::mat4 matRotation = glm::mat4_cast(cQuaternion);
|
||||
|
||||
// Build translation matrix
|
||||
glm::mat4 matTranslate = glm::translate(glm::mat4(1.0f), -cPosition);
|
||||
|
||||
// Update view matrix
|
||||
matView = matRotation * matTranslate;
|
||||
}
|
||||
|
||||
// Private member variables
|
||||
|
||||
// Camera quaternion and frame pitch and yaw
|
||||
glm::quat cQuaternion;
|
||||
GLfloat fPitch = 0.0f;
|
||||
GLfloat fYaw = 0.0f;
|
||||
|
||||
// Camera position
|
||||
glm::vec3 cPosition;
|
||||
|
||||
// Variables used for bookkeeping
|
||||
GLboolean resetMouse = true;
|
||||
GLboolean isMousePressed = false;
|
||||
GLboolean keysInUse[512];
|
||||
|
||||
// Last cursor position
|
||||
GLfloat lastXPos = 0.0f;
|
||||
GLfloat lastYPos = 0.0f;
|
||||
|
||||
// Camera settings
|
||||
GLfloat cMovementSpeed;
|
||||
GLfloat cMouseSensitivity;
|
||||
|
||||
// View matrix
|
||||
glm::mat4 matView;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,31 @@
|
|||
#include <glad/glad.h>
|
||||
#include <program.hpp>
|
||||
#include "glutils.h"
|
||||
|
||||
unsigned int generateBuffer(Mesh &mesh) {
|
||||
unsigned int vaoID;
|
||||
glGenVertexArrays(1, &vaoID);
|
||||
glBindVertexArray(vaoID);
|
||||
|
||||
unsigned int vertexBufferID;
|
||||
glGenBuffers(1, &vertexBufferID);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);
|
||||
glBufferData(GL_ARRAY_BUFFER, mesh.vertices.size() * sizeof(glm::vec3), mesh.vertices.data(), GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
|
||||
glEnableVertexAttribArray(0);
|
||||
|
||||