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);
|
||||
|
||||
unsigned int normalBufferID;
|
||||
glGenBuffers(1, &normalBufferID);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, normalBufferID);
|
||||
glBufferData(GL_ARRAY_BUFFER, mesh.normals.size() * sizeof(glm::vec3), mesh.normals.data(), GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_TRUE, 3 * sizeof(float), 0);
|
||||
glEnableVertexAttribArray(1);
|
||||
|
||||
unsigned int indexBufferID;
|
||||
glGenBuffers(1, &indexBufferID);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferID);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.indices.size() * sizeof(unsigned int), mesh.indices.data(), GL_STATIC_DRAW);
|
||||
|
||||
|
||||
return vaoID;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include "mesh.h"
|
||||
|
||||
unsigned int generateBuffer(Mesh &mesh);
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
struct Mesh {
|
||||
std::vector<glm::vec3> vertices;
|
||||
std::vector<glm::vec3> normals;
|
||||
std::vector<glm::vec2> textureCoordinates;
|
||||
|
||||
std::vector<unsigned int> indices;
|
||||
};
|
|
@ -0,0 +1,146 @@
|
|||
#ifndef SHADER_HPP
|
||||
#define SHADER_HPP
|
||||
#pragma once
|
||||
|
||||
// System headers
|
||||
#include <glad/glad.h>
|
||||
|
||||
// Standard headers
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace Gloom
|
||||
{
|
||||
class Shader
|
||||
{
|
||||
public:
|
||||
Shader() { mProgram = glCreateProgram(); }
|
||||
|
||||
// Public member functions
|
||||
void activate() { glUseProgram(mProgram); }
|
||||
void deactivate() { glUseProgram(0); }
|
||||
GLuint get() { return mProgram; }
|
||||
void destroy() { glDeleteProgram(mProgram); }
|
||||
|
||||
/* Attach a shader to the current shader program */
|
||||
void attach(std::string const &filename)
|
||||
{
|
||||
// Load GLSL Shader from source
|
||||
std::ifstream fd(filename.c_str());
|
||||
if (fd.fail())
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Something went wrong when attaching the Shader file at \"%s\".\n"
|
||||
"The file may not exist or is currently inaccessible.\n",
|
||||
filename.c_str());
|
||||
return;
|
||||
}
|
||||
auto src = std::string(std::istreambuf_iterator<char>(fd),
|
||||
(std::istreambuf_iterator<char>()));
|
||||
|
||||
// Create shader object
|
||||
const char * source = src.c_str();
|
||||
auto shader = create(filename);
|
||||
glShaderSource(shader, 1, &source, nullptr);
|
||||
glCompileShader(shader);
|
||||
|
||||
// Display errors
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &mStatus);
|
||||
if (!mStatus)
|
||||
{
|
||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &mLength);
|
||||
std::unique_ptr<char[]> buffer(new char[mLength]);
|
||||
glGetShaderInfoLog(shader, mLength, nullptr, buffer.get());
|
||||
fprintf(stderr, "%s\n%s", filename.c_str(), buffer.get());
|
||||
}
|
||||
|
||||
assert(mStatus);
|
||||
|
||||
// Attach shader and free allocated memory
|
||||
glAttachShader(mProgram, shader);
|
||||
glDeleteShader(shader);
|
||||
}
|
||||
|
||||
|
||||
/* Links all attached shaders together into a shader program */
|
||||
void link()
|
||||
{
|
||||
// Link all attached shaders
|
||||
glLinkProgram(mProgram);
|
||||
|
||||
// Display errors
|
||||
glGetProgramiv(mProgram, GL_LINK_STATUS, &mStatus);
|
||||
if (!mStatus)
|
||||
{
|
||||
glGetProgramiv(mProgram, GL_INFO_LOG_LENGTH, &mLength);
|
||||
std::unique_ptr<char[]> buffer(new char[mLength]);
|
||||
glGetProgramInfoLog(mProgram, mLength, nullptr, buffer.get());
|
||||
fprintf(stderr, "%s\n", buffer.get());
|
||||
}
|
||||
|
||||
assert(mStatus);
|
||||
}
|
||||
|
||||
|
||||
/* Convenience function that attaches and links a vertex and a
|
||||
fragment shader in a shader program */
|
||||
void makeBasicShader(std::string const &vertexFilename,
|
||||
std::string const &fragmentFilename)
|
||||
{
|
||||
attach(vertexFilename);
|
||||
attach(fragmentFilename);
|
||||
link();
|
||||
}
|
||||
|
||||
|
||||
/* Used for debugging shader programs (expensive to run) */
|
||||
bool isValid()
|
||||
{
|
||||
// Validate linked shader program
|
||||
glValidateProgram(mProgram);
|
||||
|
||||
// Display errors
|
||||
glGetProgramiv(mProgram, GL_VALIDATE_STATUS, &mStatus);
|
||||
if (!mStatus)
|
||||
{
|
||||
glGetProgramiv(mProgram, GL_INFO_LOG_LENGTH, &mLength);
|
||||
std::unique_ptr<char[]> buffer(new char[mLength]);
|
||||
glGetProgramInfoLog(mProgram, mLength, nullptr, buffer.get());
|
||||
fprintf(stderr, "%s\n", buffer.get());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* Helper function for creating shaders */
|
||||
GLuint create(std::string const &filename)
|
||||
{
|
||||
// Extract file extension and create the correct shader type
|
||||
auto idx = filename.rfind(".");
|
||||
auto ext = filename.substr(idx + 1);
|
||||
if (ext == "comp") return glCreateShader(GL_COMPUTE_SHADER);
|
||||
else if (ext == "frag") return glCreateShader(GL_FRAGMENT_SHADER);
|
||||
else if (ext == "geom") return glCreateShader(GL_GEOMETRY_SHADER);
|
||||
else if (ext == "tcs") return glCreateShader(GL_TESS_CONTROL_SHADER);
|
||||
else if (ext == "tes") return glCreateShader(GL_TESS_EVALUATION_SHADER);
|
||||
else if (ext == "vert") return glCreateShader(GL_VERTEX_SHADER);
|
||||
else return false;
|
||||
}
|
||||
|
||||
private:
|
||||
// Disable copying and assignment
|
||||
Shader(Shader const &) = delete;
|
||||
Shader & operator =(Shader const &) = delete;
|
||||
|
||||
// Private member variables
|
||||
GLuint mProgram;
|
||||
GLint mStatus;
|
||||
GLint mLength;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,245 @@
|
|||
#include <iostream>
|
||||
#include "shapes.h"
|
||||
|
||||
Mesh generateBox(float width, float height, float depth, bool flipFaces) {
|
||||
// Hardcoded. Sue me.
|
||||
|
||||
std::vector<glm::vec3> vertices = {
|
||||
{0, 0, 0},
|
||||
{0, 0, depth},
|
||||
{0, height, depth},
|
||||
|
||||
{0, 0, 0},
|
||||
{0, height, depth},
|
||||
{0, height, 0},
|
||||
|
||||
{width, 0, 0},
|
||||
{width, height, 0},
|
||||
{width, height, depth},
|
||||
|
||||
{width, 0, 0},
|
||||
{width, height, depth},
|
||||
{width, 0, depth},
|
||||
|
||||
{0, 0, 0},
|
||||
{width, height, 0},
|
||||
{width, 0, 0},
|
||||
|
||||
{0, 0, 0},
|
||||
{0, height, 0},
|
||||
{width, height, 0},
|
||||
|
||||
{0, 0, depth},
|
||||
{width, 0, depth},
|
||||
{width, height, depth},
|
||||
|
||||
{0, 0, depth},
|
||||
{width, height, depth},
|
||||
{0, height, depth},
|
||||
|
||||
{0, 0, 0},
|
||||
{width, 0, 0},
|
||||
{width, 0, depth},
|
||||
|
||||
{0, 0, 0},
|
||||
{width, 0, depth},
|
||||
{0, 0, depth},
|
||||
|
||||
{width, height, 0},
|
||||
{0, height, 0},
|
||||
{0, height, depth},
|
||||
|
||||
{width, height, 0},
|
||||
{0, height, depth},
|
||||
{width, height, depth}};
|
||||
|
||||
std::vector<glm::vec3> normals = {
|
||||
{-1.0, 0.0, 0.0},
|
||||
{-1.0, 0.0, 0.0},
|
||||
{-1.0, 0.0, 0.0},
|
||||
|
||||
{-1.0, 0.0, 0.0},
|
||||
{-1.0, 0.0, 0.0},
|
||||
{-1.0, 0.0, 0.0},
|
||||
|
||||
{1.0, 0.0, 0.0},
|
||||
{1.0, 0.0, 0.0},
|
||||
{1.0, 0.0, 0.0},
|
||||
|
||||
{1.0, 0.0, 0.0},
|
||||
{1.0, 0.0, 0.0},
|
||||
{1.0, 0.0, 0.0},
|
||||
|
||||
{0.0, -1.0, 0.0},
|
||||
{0.0, -1.0, 0.0},
|
||||
{0.0, -1.0, 0.0},
|
||||
|
||||
{0.0, -1.0, 0.0},
|
||||
{0.0, -1.0, 0.0},
|
||||
{0.0, -1.0, 0.0},
|
||||
|
||||
{0.0, 1.0, 0.0},
|
||||
{0.0, 1.0, 0.0},
|
||||
{0.0, 1.0, 0.0},
|
||||
|
||||
{0.0, 1.0, 0.0},
|
||||
{0.0, 1.0, 0.0},
|
||||
{0.0, 1.0, 0.0},
|
||||
|
||||
{0.0, 0.0, -1.0},
|
||||
{0.0, 0.0, -1.0},
|
||||
{0.0, 0.0, -1.0},
|
||||
|
||||
{0.0, 0.0, -1.0},
|
||||
{0.0, 0.0, -1.0},
|
||||
{0.0, 0.0, -1.0},
|
||||
|
||||
{0.0, 0.0, 1.0},
|
||||
{0.0, 0.0, 1.0},
|
||||
{0.0, 0.0, 1.0},
|
||||
|
||||
{0.0, 0.0, 1.0},
|
||||
{0.0, 0.0, 1.0},
|
||||
{0.0, 0.0, 1.0},
|
||||
};
|
||||
|
||||
std::vector<unsigned int> indices = {
|
||||
0, 1, 2,
|
||||
3, 4, 5,
|
||||
6, 7, 8,
|
||||
9, 10, 11,
|
||||
12, 13, 14,
|
||||
15, 16, 17,
|
||||
18, 19, 20,
|
||||
21, 22, 23,
|
||||
24, 25, 26,
|
||||
27, 28, 29,
|
||||
30, 31, 32,
|
||||
33, 34, 35
|
||||
};
|
||||
|
||||
if(flipFaces) {
|
||||
for(int i = 0; i < 36; i += 3) {
|
||||
unsigned int temp = indices[i + 1];
|
||||
indices[i + 1] = indices[i + 2];
|
||||
indices[i + 2] = temp;
|
||||
|
||||
normals[i + 0] *= -1;
|
||||
normals[i + 1] *= -1;
|
||||
normals[i + 2] *= -1;
|
||||
}
|
||||
}
|
||||
|
||||
Mesh mesh;
|
||||
mesh.vertices = vertices;
|
||||
mesh.normals = normals;
|
||||
mesh.indices = indices;
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
Mesh generateSphere(float sphereRadius, int slices, int layers) {
|
||||
const unsigned int triangleCount = slices * layers * 2;
|
||||
|
||||
std::vector<glm::vec3> vertices;
|
||||
std::vector<glm::vec3> normals;
|
||||
std::vector<unsigned int> indices;
|
||||
|
||||
vertices.reserve(3 * triangleCount);
|
||||
normals.reserve(3 * triangleCount);
|
||||
indices.reserve(3 * triangleCount);
|
||||
|
||||
// Slices require us to define a full revolution worth of triangles.
|
||||
// Layers only requires angle varying between the bottom and the top (a layer only covers half a circle worth of angles)
|
||||
const float degreesPerLayer = 180.0 / (float) layers;
|
||||
const float degreesPerSlice = 360.0 / (float) slices;
|
||||
|
||||
unsigned int i = 0;
|
||||
|
||||
// Constructing the sphere one layer at a time
|
||||
for (int layer = 0; layer < layers; layer++) {
|
||||
int nextLayer = layer + 1;
|
||||
|
||||
// Angles between the vector pointing to any point on a particular layer and the negative z-axis
|
||||
float currentAngleZDegrees = degreesPerLayer * layer;
|
||||
float nextAngleZDegrees = degreesPerLayer * nextLayer;
|
||||
|
||||
// All coordinates within a single layer share z-coordinates.
|
||||
// So we can calculate those of the current and subsequent layer here.
|
||||
float currentZ = -cos(glm::radians(currentAngleZDegrees));
|
||||
float nextZ = -cos(glm::radians(nextAngleZDegrees));
|
||||
|
||||
// The row of vertices forms a circle around the vertical diagonal (z-axis) of the sphere.
|
||||
// These radii are also constant for an entire layer, so we can precalculate them.
|
||||
float radius = sin(glm::radians(currentAngleZDegrees));
|
||||
float nextRadius = sin(glm::radians(nextAngleZDegrees));
|
||||
|
||||
// Now we can move on to constructing individual slices within a layer
|
||||
for (int slice = 0; slice < slices; slice++) {
|
||||
|
||||
// The direction of the start and the end of the slice in the xy-plane
|
||||
float currentSliceAngleDegrees = slice * degreesPerSlice;
|
||||
float nextSliceAngleDegrees = (slice + 1) * degreesPerSlice;
|
||||
|
||||
// Determining the direction vector for both the start and end of the slice
|
||||
float currentDirectionX = cos(glm::radians(currentSliceAngleDegrees));
|
||||
float currentDirectionY = sin(glm::radians(currentSliceAngleDegrees));
|
||||
|
||||
float nextDirectionX = cos(glm::radians(nextSliceAngleDegrees));
|
||||
float nextDirectionY = sin(glm::radians(nextSliceAngleDegrees));
|
||||
|
||||
vertices.emplace_back(sphereRadius * radius * currentDirectionX,
|
||||
sphereRadius * radius * currentDirectionY,
|
||||
sphereRadius * currentZ);
|
||||
vertices.emplace_back(sphereRadius * radius * nextDirectionX,
|
||||
sphereRadius * radius * nextDirectionY,
|
||||
sphereRadius * currentZ);
|
||||
vertices.emplace_back(sphereRadius * nextRadius * nextDirectionX,
|
||||
sphereRadius * nextRadius * nextDirectionY,
|
||||
sphereRadius * nextZ);
|
||||
vertices.emplace_back(sphereRadius * radius * currentDirectionX,
|
||||
sphereRadius * radius * currentDirectionY,
|
||||
sphereRadius * currentZ);
|
||||
vertices.emplace_back(sphereRadius * nextRadius * nextDirectionX,
|
||||
sphereRadius * nextRadius * nextDirectionY,
|
||||
sphereRadius * nextZ);
|
||||
vertices.emplace_back(sphereRadius * nextRadius * currentDirectionX,
|
||||
sphereRadius * nextRadius * currentDirectionY,
|
||||
sphereRadius * nextZ);
|
||||
|
||||
normals.emplace_back(radius * currentDirectionX,
|
||||
radius * currentDirectionY,
|
||||
currentZ);
|
||||
normals.emplace_back(radius * nextDirectionX,
|
||||
radius * nextDirectionY,
|
||||
currentZ);
|
||||
normals.emplace_back(nextRadius * nextDirectionX,
|
||||
nextRadius * nextDirectionY,
|
||||
nextZ);
|
||||
normals.emplace_back(radius * currentDirectionX,
|
||||
radius * currentDirectionY,
|
||||
currentZ);
|
||||
normals.emplace_back(nextRadius * nextDirectionX,
|
||||
nextRadius * nextDirectionY,
|
||||
nextZ);
|
||||
normals.emplace_back(nextRadius * currentDirectionX,
|
||||
nextRadius * currentDirectionY,
|
||||
nextZ);
|
||||
|
||||
indices.emplace_back(i + 0);
|
||||
indices.emplace_back(i + 1);
|
||||
indices.emplace_back(i + 2);
|
||||
indices.emplace_back(i + 3);
|
||||
indices.emplace_back(i + 4);
|
||||
indices.emplace_back(i + 5);
|
||||
|
||||
i += 6;
|
||||
}
|
||||
}
|
||||
|
||||
Mesh mesh;
|
||||
mesh.vertices = vertices;
|
||||
mesh.normals = normals;
|
||||
mesh.indices = indices;
|
||||
return mesh;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
#include "mesh.h"
|
||||
|
||||
Mesh generateBox(float width, float height, float depth, bool flipFaces = false);
|
||||
Mesh generateSphere(float radius, int slices, int layers);
|
|
@ -0,0 +1,23 @@
|
|||
#include <chrono>
|
||||
#include "timeutils.h"
|
||||
|
||||
// In order to be able to calculate when the getTimeDeltaSeconds() function was last called, we need to know the point in time when that happened. This requires us to keep hold of that point in time.
|
||||
// We initialise this value to the time at the start of the program.
|
||||
static std::chrono::steady_clock::time_point _previousTimePoint = std::chrono::steady_clock::now();
|
||||
|
||||
// Calculates the elapsed time since the previous time this function was called.
|
||||
double getTimeDeltaSeconds() {
|
||||
// Determine the current time
|
||||
std::chrono::steady_clock::time_point currentTime = std::chrono::steady_clock::now();
|
||||
|
||||
// Calculate the number of nanoseconds that elapsed since the previous call to this function
|
||||
long long timeDelta = std::chrono::duration_cast<std::chrono::nanoseconds>(currentTime - _previousTimePoint).count();
|
||||
// Convert the time delta in nanoseconds to seconds
|
||||
double timeDeltaSeconds = (double)timeDelta / 1000000000.0;
|
||||
|
||||
// Store the previously measured current time
|
||||
_previousTimePoint = currentTime;
|
||||
|
||||
// Return the calculated time delta in seconds
|
||||
return timeDeltaSeconds;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
double getTimeDeltaSeconds();
|
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
// System Headers
|
||||
#include <glad/glad.h>
|
||||
|
||||
// Standard headers
|
||||
#include <string>
|
||||
|
||||
// Constants
|
||||
const int windowWidth = 1366;
|
||||
const int windowHeight = 768;
|
||||
const std::string windowTitle = "Glowbox";
|
||||
const GLint windowResizable = GL_TRUE;
|
||||
const int windowSamples = 4;
|
||||
|
||||
struct CommandLineOptions {
|
||||
bool enableMusic;
|
||||
bool enableAutoplay;
|
||||
};
|
Loading…
Reference in New Issue