Improved assignments for 2020
This commit is contained in:
committed by
Michael H. Gimle
parent
bb8c4d8625
commit
fca107c75b
@@ -30,3 +30,9 @@
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
# Build directory
|
||||
/build/*
|
||||
!/build/.emptydirectory
|
||||
|
||||
/.vscode/*
|
||||
@@ -28,3 +28,6 @@
|
||||
[submodule "lib/lodepng"]
|
||||
path = lib/lodepng
|
||||
url = https://github.com/lvandeve/lodepng
|
||||
[submodule "lib/fmt"]
|
||||
path = lib/fmt
|
||||
url = https://github.com/fmtlib/fmt.git
|
||||
|
||||
@@ -42,6 +42,11 @@ option (SFML_BUILD_WINDOW OFF)
|
||||
option (SFML_BUILD_NETWORK OFF)
|
||||
add_subdirectory(lib/SFML)
|
||||
|
||||
#
|
||||
# Add FMT
|
||||
#
|
||||
add_subdirectory (lib/fmt)
|
||||
|
||||
#
|
||||
# GLAD
|
||||
#
|
||||
@@ -75,6 +80,7 @@ include_directories (src/
|
||||
lib/arrrgh/
|
||||
lib/SFML/include/)
|
||||
|
||||
|
||||
#
|
||||
# Add files
|
||||
#
|
||||
@@ -114,5 +120,6 @@ add_executable (${PROJECT_NAME} ${PROJECT_SOURCES} ${PROJECT_HEADERS}
|
||||
target_link_libraries (${PROJECT_NAME}
|
||||
glfw
|
||||
sfml-audio
|
||||
fmt::fmt
|
||||
${GLFW_LIBRARIES}
|
||||
${GLAD_LIBRARIES})
|
||||
|
||||
Submodule
+1
Submodule lib/fmt added at b55ea58705
@@ -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
|
||||
@@ -1,12 +1,14 @@
|
||||
#version 430 core
|
||||
|
||||
in layout(location = 0) vec3 normal;
|
||||
in layout(location = 1) vec2 textureCoordinates;
|
||||
|
||||
out vec4 color;
|
||||
|
||||
float rand(vec2 co) { return fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453); }
|
||||
float dither(vec2 uv) { return (rand(uv)*2.0-1.0) / 256.0; }
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,16 @@
|
||||
|
||||
in layout(location = 0) vec3 position;
|
||||
in layout(location = 1) vec3 normal_in;
|
||||
in layout(location = 2) vec2 textureCoordinates_in;
|
||||
|
||||
uniform layout(location = 3) mat4 MVP;
|
||||
|
||||
out layout(location = 0) vec3 normal_out;
|
||||
out layout(location = 1) vec2 textureCoordinates_out;
|
||||
|
||||
void main()
|
||||
{
|
||||
normal_out = normal_in;
|
||||
textureCoordinates_out = textureCoordinates_in;
|
||||
gl_Position = MVP * vec4(position, 1.0f);
|
||||
}
|
||||
|
||||
+172
-127
@@ -12,9 +12,11 @@
|
||||
#include <SFML/Audio/Sound.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <fmt/format.h>
|
||||
#include "gamelogic.h"
|
||||
#include "sceneGraph.hpp"
|
||||
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
enum KeyFrameAction {
|
||||
BOTTOM, TOP
|
||||
@@ -23,7 +25,7 @@ enum KeyFrameAction {
|
||||
#include <timestamps.h>
|
||||
|
||||
double padPositionX = 0;
|
||||
double padPositionY = 0;
|
||||
double padPositionZ = 0;
|
||||
|
||||
unsigned int currentKeyFrame = 0;
|
||||
unsigned int previousKeyFrame = 0;
|
||||
@@ -40,50 +42,57 @@ sf::SoundBuffer* buffer;
|
||||
Gloom::Shader* shader;
|
||||
sf::Sound* sound;
|
||||
|
||||
const glm::vec3 boxDimensions(180, 90, 50);
|
||||
const glm::vec3 boxDimensions(180, 90, 90);
|
||||
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;
|
||||
glm::vec3 ballDirection(1, 1, 0.2f);
|
||||
|
||||
CommandLineOptions options;
|
||||
|
||||
bool hasStarted = false;
|
||||
bool hasLost = false;
|
||||
bool jumpedToNextFrame = false;
|
||||
bool isPaused = false;
|
||||
|
||||
bool mouseLeftPressed = false;
|
||||
bool mouseLeftReleased = false;
|
||||
bool mouseRightPressed = false;
|
||||
bool mouseRightReleased = 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;
|
||||
double gameElapsedTime = debug_startTime;
|
||||
|
||||
|
||||
|
||||
double mouseSensitivity = 1.0;
|
||||
double lastMouseX = windowWidth / 2;
|
||||
double lastMouseY = windowHeight / 2;
|
||||
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);
|
||||
double deltaX = x - lastMouseX;
|
||||
double deltaY = y - lastMouseY;
|
||||
|
||||
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);
|
||||
}
|
||||
padPositionX -= mouseSensitivity * deltaX / windowWidth;
|
||||
padPositionZ -= mouseSensitivity * deltaY / windowHeight;
|
||||
|
||||
if (padPositionX > 1) padPositionX = 1;
|
||||
if (padPositionX < 0) padPositionX = 0;
|
||||
if (padPositionZ > 1) padPositionZ = 1;
|
||||
if (padPositionZ < 0) padPositionZ = 0;
|
||||
|
||||
glfwSetCursorPos(window, windowWidth / 2, windowHeight / 2);
|
||||
}
|
||||
|
||||
//// A few lines to help you if you've never used c++ structs
|
||||
// struct LightSource {
|
||||
// bool a_placeholder_value;
|
||||
// };
|
||||
// LightSource lightSources[/*Put number of light sources you want here*/];
|
||||
|
||||
void initGame(GLFWwindow* window, CommandLineOptions gameOptions) {
|
||||
buffer = new sf::SoundBuffer();
|
||||
if (!buffer->loadFromFile("../res/Hall of the Mountain King.ogg")) {
|
||||
@@ -99,17 +108,20 @@ void initGame(GLFWwindow* window, CommandLineOptions gameOptions) {
|
||||
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);
|
||||
// Create meshes
|
||||
Mesh pad = cube(padDimensions, glm::vec2(30, 40), true);
|
||||
Mesh box = cube(boxDimensions, glm::vec2(90), true, true);
|
||||
Mesh sphere = generateSphere(1.0, 40, 40);
|
||||
|
||||
// Fill buffers
|
||||
unsigned int ballVAO = generateBuffer(sphere);
|
||||
unsigned int boxVAO = generateBuffer(box);
|
||||
unsigned int padVAO = generateBuffer(pad);
|
||||
unsigned int boxVAO = generateBuffer(box);
|
||||
unsigned int padVAO = generateBuffer(pad);
|
||||
|
||||
// Construct scene
|
||||
rootNode = createSceneNode();
|
||||
boxNode = createSceneNode();
|
||||
padNode = createSceneNode();
|
||||
boxNode = createSceneNode();
|
||||
padNode = createSceneNode();
|
||||
ballNode = createSceneNode();
|
||||
|
||||
rootNode->children.push_back(boxNode);
|
||||
@@ -125,49 +137,51 @@ void initGame(GLFWwindow* window, CommandLineOptions gameOptions) {
|
||||
ballNode->vertexArrayObjectID = ballVAO;
|
||||
ballNode->VAOIndexCount = sphere.indices.size();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
getTimeDeltaSeconds();
|
||||
|
||||
std::cout << fmt::format("Initialized scene with {} SceneNodes.", totalChildren(rootNode)) << std::endl;
|
||||
|
||||
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) {
|
||||
const float ballBottomY = boxNode->position.y - (boxDimensions.y/2) + ballRadius + padDimensions.y;
|
||||
const float ballTopY = boxNode->position.y + (boxDimensions.y/2) - ballRadius;
|
||||
const float BallVerticalTravelDistance = ballTopY - ballBottomY;
|
||||
|
||||
if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1)) {
|
||||
const float cameraWallOffset = 30; // Arbitrary addition to prevent ball from going too much into camera
|
||||
|
||||
const float ballMinX = boxNode->position.x - (boxDimensions.x/2) + ballRadius;
|
||||
const float ballMaxX = boxNode->position.x + (boxDimensions.x/2) - ballRadius;
|
||||
const float ballMinZ = boxNode->position.z - (boxDimensions.z/2) + ballRadius;
|
||||
const float ballMaxZ = boxNode->position.z + (boxDimensions.z/2) - ballRadius - cameraWallOffset;
|
||||
|
||||
if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1)) {
|
||||
mouseLeftPressed = true;
|
||||
mouseLeftReleased = false;
|
||||
} else {
|
||||
mouseLeftReleased = mouseLeftPressed;
|
||||
mouseLeftPressed = false;
|
||||
}
|
||||
if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_2)) {
|
||||
mouseRightPressed = true;
|
||||
mouseRightReleased = false;
|
||||
} else {
|
||||
mouseRightReleased = mouseRightPressed;
|
||||
mouseRightPressed = false;
|
||||
}
|
||||
|
||||
if(!hasStarted) {
|
||||
if (mouseLeftPressed) {
|
||||
if (options.enableMusic) {
|
||||
sound = new sf::Sound();
|
||||
sound->setBuffer(*buffer);
|
||||
@@ -176,26 +190,40 @@ void updateFrame(GLFWwindow* window) {
|
||||
sound->play();
|
||||
}
|
||||
totalElapsedTime = debug_startTime;
|
||||
gameElapsedTime = debug_startTime;
|
||||
hasStarted = true;
|
||||
}
|
||||
|
||||
ballPosition.x = (1 - padPositionX) * (boxDimensions.x - padDimensions.x) + padDimensions.x / 2.0;
|
||||
ballPosition.y = ballRadius + padDimensions.y;
|
||||
ballPosition.x = ballMinX + (1 - padPositionX) * (ballMaxX - ballMinX);
|
||||
ballPosition.y = ballBottomY;
|
||||
ballPosition.z = ballMinZ + (1 - padPositionZ) * ((ballMaxZ+cameraWallOffset) - ballMinZ);
|
||||
} 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;
|
||||
if (mouseLeftReleased) {
|
||||
hasLost = false;
|
||||
hasStarted = false;
|
||||
currentKeyFrame = 0;
|
||||
previousKeyFrame = 0;
|
||||
}
|
||||
} else if (isPaused) {
|
||||
if (mouseRightReleased) {
|
||||
isPaused = false;
|
||||
if (options.enableMusic) {
|
||||
sound->play();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
gameElapsedTime += timeDelta;
|
||||
if (mouseRightReleased) {
|
||||
isPaused = true;
|
||||
if (options.enableMusic) {
|
||||
sound->pause();
|
||||
}
|
||||
}
|
||||
// Get the timing for the beat of the song
|
||||
for (unsigned int i = currentKeyFrame; i < keyFrameTimeStamps.size(); i++) {
|
||||
if (totalElapsedTime < keyFrameTimeStamps.at(i)) {
|
||||
if (gameElapsedTime < keyFrameTimeStamps.at(i)) {
|
||||
continue;
|
||||
}
|
||||
currentKeyFrame = i;
|
||||
@@ -207,17 +235,16 @@ void updateFrame(GLFWwindow* window) {
|
||||
double frameStart = keyFrameTimeStamps.at(currentKeyFrame);
|
||||
double frameEnd = keyFrameTimeStamps.at(currentKeyFrame + 1); // Assumes last keyframe at infinity
|
||||
|
||||
double elapsedTimeInFrame = totalElapsedTime - frameStart;
|
||||
double elapsedTimeInFrame = gameElapsedTime - 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);
|
||||
|
||||
// Synchronize ball with music
|
||||
if (currentOrigin == BOTTOM && currentDestination == BOTTOM) {
|
||||
ballYCoord = ballBottomY;
|
||||
} else if (currentOrigin == TOP && currentDestination == TOP) {
|
||||
@@ -228,88 +255,110 @@ void updateFrame(GLFWwindow* window) {
|
||||
ballYCoord = ballBottomY + BallVerticalTravelDistance * fractionFrameComplete;
|
||||
}
|
||||
|
||||
|
||||
// Make ball move
|
||||
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;
|
||||
// Make ball bounce
|
||||
if (ballPosition.x < ballMinX) {
|
||||
ballPosition.x = ballMinX;
|
||||
ballDirection.x *= -1;
|
||||
} else if (ballPosition.x - ballRadius < 0) {
|
||||
ballPosition.x = ballRadius;
|
||||
} else if (ballPosition.x > ballMaxX) {
|
||||
ballPosition.x = ballMaxX;
|
||||
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;
|
||||
if (ballPosition.z < ballMinZ) {
|
||||
ballPosition.z = ballMinZ;
|
||||
ballDirection.z *= -1;
|
||||
} else if (ballPosition.z - ballRadius < 0) {
|
||||
ballPosition.z = ballRadius;
|
||||
} else if (ballPosition.z > ballMaxZ) {
|
||||
ballPosition.z = ballMaxZ;
|
||||
ballDirection.z *= -1;
|
||||
}
|
||||
|
||||
if(options.enableAutoplay) {
|
||||
padPositionX = 1 - (ballPosition.x / (boxDimensions.x - 2 * ballRadius));
|
||||
padPositionY = 1 - (ballPosition.z / (boxDimensions.z - 2 * ballRadius));
|
||||
padPositionX = 1-(ballPosition.x - ballMinX) / (ballMaxX - ballMinX);
|
||||
padPositionZ = 1-(ballPosition.z - ballMinZ) / ((ballMaxZ+cameraWallOffset) - ballMinZ);
|
||||
}
|
||||
|
||||
// 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 padLeftX = boxNode->position.x - (boxDimensions.x/2) + (1 - padPositionX) * (boxDimensions.x - padDimensions.x);
|
||||
double padRightX = padLeftX + padDimensions.x;
|
||||
double padFrontZ = boxNode->position.z - (boxDimensions.z/2) + (1 - padPositionZ) * (boxDimensions.z - padDimensions.z);
|
||||
double padBackZ = padFrontZ + padDimensions.z;
|
||||
|
||||
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) {
|
||||
if ( ballPosition.x < padLeftX
|
||||
|| ballPosition.x > padRightX
|
||||
|| ballPosition.z < padFrontZ
|
||||
|| ballPosition.z > padBackZ) {
|
||||
hasLost = true;
|
||||
if (options.enableMusic) {
|
||||
sound->stop();
|
||||
delete sound;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glm::mat4 projection = glm::perspective(glm::radians(90.0f), float(windowWidth) / float(windowHeight), 0.1f,
|
||||
120.f);
|
||||
glm::mat4 projection = glm::perspective(glm::radians(80.0f), float(windowWidth) / float(windowHeight), 0.1f, 350.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::vec3 cameraPosition = glm::vec3(0, 2, -20);
|
||||
|
||||
// Some math to make the camera move in a nice way
|
||||
float lookRotation = -0.6 / (1 + exp(-5 * (padPositionX-0.5))) + 0.3;
|
||||
glm::mat4 cameraTransform =
|
||||
glm::rotate(0.3f + 0.2f * float(-padPositionZ*padPositionZ), glm::vec3(1, 0, 0)) *
|
||||
glm::rotate(lookRotation, glm::vec3(0, 1, 0)) *
|
||||
glm::translate(-cameraPosition);
|
||||
|
||||
glm::mat4 VP = projection * cameraTransform;
|
||||
|
||||
// Move and rotate various SceneNodes
|
||||
boxNode->position = { 0, -10, -80 };
|
||||
|
||||
ballNode->position = ballPosition;
|
||||
ballNode->scale = glm::vec3(ballRadius);
|
||||
ballNode->rotation = { 0, totalElapsedTime*2, 0 };
|
||||
|
||||
padNode->position = {
|
||||
boxNode->position.x - (boxDimensions.x/2) + (padDimensions.x/2) + (1 - padPositionX) * (boxDimensions.x - padDimensions.x),
|
||||
boxNode->position.y - (boxDimensions.y/2) + (padDimensions.y/2),
|
||||
boxNode->position.z - (boxDimensions.z/2) + (padDimensions.z/2) + (1 - padPositionZ) * (boxDimensions.z - padDimensions.z)
|
||||
};
|
||||
|
||||
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 updateNodeTransformations(SceneNode* node, glm::mat4 transformationThusFar) {
|
||||
glm::mat4 transformationMatrix =
|
||||
glm::translate(node->position)
|
||||
* glm::translate(node->referencePoint)
|
||||
* glm::rotate(node->rotation.y, glm::vec3(0,1,0))
|
||||
* glm::rotate(node->rotation.x, glm::vec3(1,0,0))
|
||||
* glm::rotate(node->rotation.z, glm::vec3(0,0,1))
|
||||
* glm::scale(node->scale)
|
||||
* glm::translate(-node->referencePoint);
|
||||
|
||||
node->currentTransformationMatrix = transformationThusFar * transformationMatrix;
|
||||
|
||||
switch(node->nodeType) {
|
||||
case GEOMETRY: break;
|
||||
case POINT_LIGHT: break;
|
||||
case SPOT_LIGHT: break;
|
||||
}
|
||||
|
||||
for(SceneNode* child : node->children) {
|
||||
updateNodeTransformations(child, node->currentTransformationMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
void renderNode(SceneNode* node) {
|
||||
glUniformMatrix4fv(3, 1, GL_FALSE, glm::value_ptr(node->currentTransformationMatrix));
|
||||
@@ -321,12 +370,8 @@ void renderNode(SceneNode* node) {
|
||||
glDrawElements(GL_TRIANGLES, node->VAOIndexCount, GL_UNSIGNED_INT, nullptr);
|
||||
}
|
||||
break;
|
||||
case POINT_LIGHT:
|
||||
|
||||
break;
|
||||
case SPOT_LIGHT:
|
||||
|
||||
break;
|
||||
case POINT_LIGHT: break;
|
||||
case SPOT_LIGHT: break;
|
||||
}
|
||||
|
||||
for(SceneNode* child : node->children) {
|
||||
|
||||
+2
-1
@@ -1,8 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <utilities/window.hpp>
|
||||
#include "sceneGraph.hpp"
|
||||
|
||||
void updateNodeTransformations(SceneNode* node, glm::mat4 transformationThusFar);
|
||||
void initGame(GLFWwindow* window, CommandLineOptions options);
|
||||
void updateFrame(GLFWwindow* window);
|
||||
void renderFrame(GLFWwindow* window);
|
||||
+3
-1
@@ -24,6 +24,9 @@ void runProgram(GLFWwindow* window, CommandLineOptions options)
|
||||
// Configure miscellaneous OpenGL settings
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
// Disable built-in dithering
|
||||
glDisable(GL_DITHER);
|
||||
|
||||
// Set default colour after clearing the colour buffer
|
||||
glClearColor(0.3f, 0.5f, 0.8f, 1.0f);
|
||||
|
||||
@@ -43,7 +46,6 @@ void runProgram(GLFWwindow* window, CommandLineOptions options)
|
||||
|
||||
|
||||
|
||||
|
||||
// Handle other events
|
||||
glfwPollEvents();
|
||||
handleKeyboardInput(window);
|
||||
|
||||
@@ -10,6 +10,14 @@ void addChild(SceneNode* parent, SceneNode* child) {
|
||||
parent->children.push_back(child);
|
||||
}
|
||||
|
||||
int totalChildren(SceneNode* parent) {
|
||||
int count = parent->children.size();
|
||||
for (SceneNode* child : parent->children) {
|
||||
count += totalChildren(child);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// Pretty prints the current values of a SceneNode instance to stdout
|
||||
void printNode(SceneNode* node) {
|
||||
printf(
|
||||
|
||||
+2
-7
@@ -17,10 +17,6 @@ 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);
|
||||
@@ -32,6 +28,7 @@ struct SceneNode {
|
||||
VAOIndexCount = 0;
|
||||
|
||||
nodeType = GEOMETRY;
|
||||
|
||||
}
|
||||
|
||||
// A list of all children that belong to this node.
|
||||
@@ -57,11 +54,9 @@ struct SceneNode {
|
||||
SceneNodeType nodeType;
|
||||
};
|
||||
|
||||
// Struct for keeping track of 2D coordinates
|
||||
|
||||
SceneNode* createSceneNode();
|
||||
void addChild(SceneNode* parent, SceneNode* child);
|
||||
void printNode(SceneNode* node);
|
||||
|
||||
int totalChildren(SceneNode* parent);
|
||||
|
||||
// For more details, see SceneGraph.cpp.
|
||||
+17
-14
@@ -1,31 +1,34 @@
|
||||
#include <glad/glad.h>
|
||||
#include <program.hpp>
|
||||
#include "glutils.h"
|
||||
#include <vector>
|
||||
|
||||
template <class T>
|
||||
unsigned int generateAttribute(int id, int elementsPerEntry, std::vector<T> data, bool normalize) {
|
||||
unsigned int bufferID;
|
||||
glGenBuffers(1, &bufferID);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, bufferID);
|
||||
glBufferData(GL_ARRAY_BUFFER, data.size() * sizeof(T), data.data(), GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(id, elementsPerEntry, GL_FLOAT, normalize ? GL_TRUE : GL_FALSE, elementsPerEntry * sizeof(float), 0);
|
||||
glEnableVertexAttribArray(id);
|
||||
return bufferID;
|
||||
}
|
||||
|
||||
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);
|
||||
generateAttribute(0, 3, mesh.vertices, false);
|
||||
generateAttribute(1, 3, mesh.normals, true);
|
||||
if (mesh.textureCoordinates.size() > 0) {
|
||||
generateAttribute(2, 2, mesh.textureCoordinates, false);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -95,6 +95,12 @@ namespace Gloom
|
||||
link();
|
||||
}
|
||||
|
||||
/* Convenience function to get a uniforms ID from a string
|
||||
containing its name */
|
||||
GLint getUniformFromName(std::string const &uniformName) {
|
||||
return glGetUniformLocation(this->get(), uniformName.c_str());
|
||||
}
|
||||
|
||||
|
||||
/* Used for debugging shader programs (expensive to run) */
|
||||
bool isValid()
|
||||
|
||||
+78
-184
@@ -1,205 +1,89 @@
|
||||
#include <iostream>
|
||||
#include "shapes.h"
|
||||
|
||||
Mesh generateBox(float width, float height, float depth, bool flipFaces) {
|
||||
// Hardcoded. Sue me.
|
||||
Mesh cube(glm::vec3 scale, glm::vec2 textureScale, bool tilingTextures, bool inverted, glm::vec3 textureScale3d) {
|
||||
glm::vec3 points[8];
|
||||
int indices[36];
|
||||
|
||||
// Edit: well, that backfired..
|
||||
for (int y = 0; y <= 1; y++)
|
||||
for (int z = 0; z <= 1; z++)
|
||||
for (int x = 0; x <= 1; x++) {
|
||||
points[x+y*4+z*2] = glm::vec3(x*2-1, y*2-1, z*2-1) * 0.5f * scale;
|
||||
}
|
||||
|
||||
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, depth},
|
||||
{width, 0, depth},
|
||||
|
||||
{width, 0, 0},
|
||||
{width, height, 0},
|
||||
{width, height, 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},
|
||||
int faces[6][4] = {
|
||||
{2,3,0,1}, // Bottom
|
||||
{4,5,6,7}, // Top
|
||||
{7,5,3,1}, // Right
|
||||
{4,6,0,2}, // Left
|
||||
{5,4,1,0}, // Back
|
||||
{6,7,2,3}, // Front
|
||||
};
|
||||
|
||||
// These are technically inverted relative to the vertex coordinates.
|
||||
// But for some strange reason the faces are rendered inverted.
|
||||
// So to make the assignment work this is the best I can do.
|
||||
scale = scale * textureScale3d;
|
||||
glm::vec2 faceScale[6] = {
|
||||
{-scale.x,-scale.z}, // Bottom
|
||||
{-scale.x,-scale.z}, // Top
|
||||
{ scale.z, scale.y}, // Right
|
||||
{ scale.z, scale.y}, // Left
|
||||
{ scale.x, scale.y}, // Back
|
||||
{ scale.x, scale.y}, // Front
|
||||
};
|
||||
|
||||
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, 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, 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},
|
||||
glm::vec3 normals[6] = {
|
||||
{ 0,-1, 0}, // Bottom
|
||||
{ 0, 1, 0}, // Top
|
||||
{ 1, 0, 0}, // Right
|
||||
{-1, 0, 0}, // Left
|
||||
{ 0, 0,-1}, // Back
|
||||
{ 0, 0, 1}, // Front
|
||||
};
|
||||
|
||||
float texScaleFactorX = depth / height;
|
||||
float texScaleFactorY = width / depth;
|
||||
float texScaleFactorZ = width / height;
|
||||
|
||||
std::vector<glm::vec2> textureCoordinates = {
|
||||
{0, 0},
|
||||
{texScaleFactorX, 0},
|
||||
{texScaleFactorX, 1},
|
||||
|
||||
{0, 0},
|
||||
{texScaleFactorX, 1},
|
||||
{0, 1},
|
||||
|
||||
{0, 0},
|
||||
{texScaleFactorX, 1},
|
||||
{texScaleFactorX, 0},
|
||||
|
||||
{0, 0},
|
||||
{0, 1},
|
||||
{texScaleFactorX, 1},
|
||||
|
||||
{0, 0},
|
||||
{texScaleFactorZ, 0},
|
||||
{texScaleFactorZ, 1},
|
||||
|
||||
{0, 0},
|
||||
{texScaleFactorZ, 1},
|
||||
{0, 1},
|
||||
|
||||
{0, 0},
|
||||
{texScaleFactorZ, 0},
|
||||
{texScaleFactorZ, 1},
|
||||
|
||||
{0, 0},
|
||||
{texScaleFactorZ, 1},
|
||||
{0, 1},
|
||||
|
||||
{0, 0},
|
||||
{texScaleFactorY, 0},
|
||||
{texScaleFactorY, 1},
|
||||
|
||||
{0, 0},
|
||||
{texScaleFactorY, 1},
|
||||
{0, 1},
|
||||
|
||||
{0, 0},
|
||||
{texScaleFactorY, 0},
|
||||
{texScaleFactorY, 1},
|
||||
|
||||
{0, 0},
|
||||
{texScaleFactorY, 1},
|
||||
{0, 1},
|
||||
|
||||
glm::vec2 UVs[4] = {
|
||||
{0, 0},
|
||||
{0, 1},
|
||||
{1, 0},
|
||||
{1, 1},
|
||||
};
|
||||
|
||||
Mesh m;
|
||||
for (int face = 0; face < 6; face++) {
|
||||
int offset = face * 6;
|
||||
indices[offset + 0] = faces[face][0];
|
||||
indices[offset + 3] = faces[face][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 (!inverted) {
|
||||
indices[offset + 1] = faces[face][3];
|
||||
indices[offset + 2] = faces[face][1];
|
||||
indices[offset + 4] = faces[face][2];
|
||||
indices[offset + 5] = faces[face][3];
|
||||
} else {
|
||||
indices[offset + 1] = faces[face][1];
|
||||
indices[offset + 2] = faces[face][3];
|
||||
indices[offset + 4] = faces[face][3];
|
||||
indices[offset + 5] = faces[face][2];
|
||||
}
|
||||
|
||||
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;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
m.vertices.push_back(points[indices[offset + i]]);
|
||||
m.indices.push_back(offset + i);
|
||||
m.normals.push_back(normals[face] * (inverted ? -1.f : 1.f));
|
||||
}
|
||||
|
||||
normals[i + 0] *= -1;
|
||||
normals[i + 1] *= -1;
|
||||
normals[i + 2] *= -1;
|
||||
glm::vec2 textureScaleFactor = tilingTextures ? (faceScale[face] / textureScale) : glm::vec2(1);
|
||||
|
||||
if (!inverted) {
|
||||
for (int i : {1,2,3,1,0,2}) {
|
||||
m.textureCoordinates.push_back(UVs[i] * textureScaleFactor);
|
||||
}
|
||||
} else {
|
||||
for (int i : {3,1,0,3,0,2}) {
|
||||
m.textureCoordinates.push_back(UVs[i] * textureScaleFactor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Mesh mesh;
|
||||
mesh.vertices = vertices;
|
||||
mesh.normals = normals;
|
||||
mesh.textureCoordinates = textureCoordinates;
|
||||
mesh.indices = indices;
|
||||
|
||||
return mesh;
|
||||
return m;
|
||||
}
|
||||
|
||||
Mesh generateSphere(float sphereRadius, int slices, int layers) {
|
||||
@@ -208,6 +92,7 @@ Mesh generateSphere(float sphereRadius, int slices, int layers) {
|
||||
std::vector<glm::vec3> vertices;
|
||||
std::vector<glm::vec3> normals;
|
||||
std::vector<unsigned int> indices;
|
||||
std::vector<glm::vec2> uvs;
|
||||
|
||||
vertices.reserve(3 * triangleCount);
|
||||
normals.reserve(3 * triangleCount);
|
||||
@@ -297,6 +182,14 @@ Mesh generateSphere(float sphereRadius, int slices, int layers) {
|
||||
indices.emplace_back(i + 4);
|
||||
indices.emplace_back(i + 5);
|
||||
|
||||
for (int j = 0; j < 6; j++) {
|
||||
glm::vec3 vertex = vertices.at(i+j);
|
||||
uvs.emplace_back(
|
||||
0.5 + (glm::atan(vertex.z, vertex.y)/(2.0*M_PI)),
|
||||
0.5 - (glm::asin(vertex.y)/M_PI)
|
||||
);
|
||||
}
|
||||
|
||||
i += 6;
|
||||
}
|
||||
}
|
||||
@@ -305,5 +198,6 @@ Mesh generateSphere(float sphereRadius, int slices, int layers) {
|
||||
mesh.vertices = vertices;
|
||||
mesh.normals = normals;
|
||||
mesh.indices = indices;
|
||||
mesh.textureCoordinates = uvs;
|
||||
return mesh;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "mesh.h"
|
||||
|
||||
Mesh cube(glm::vec3 scale = glm::vec3(1), glm::vec2 textureScale = glm::vec2(1), bool tilingTextures = false, bool inverted = false, glm::vec3 textureScale3d = glm::vec3(1));
|
||||
Mesh generateBox(float width, float height, float depth, bool flipFaces = false);
|
||||
Mesh generateSphere(float radius, int slices, int layers);
|
||||
@@ -10,7 +10,7 @@
|
||||
const int windowWidth = 1366;
|
||||
const int windowHeight = 768;
|
||||
const std::string windowTitle = "Glowbox";
|
||||
const GLint windowResizable = GL_TRUE;
|
||||
const GLint windowResizable = GL_FALSE;
|
||||
const int windowSamples = 4;
|
||||
|
||||
struct CommandLineOptions {
|
||||
|
||||
Reference in New Issue
Block a user