clean code ™️
This commit is contained in:
+40
-42
@@ -2,7 +2,7 @@
|
||||
|
||||
in layout(location = 0) vec3 normal;
|
||||
in layout(location = 1) vec2 textureCoordinates;
|
||||
in layout(location = 2) vec3 worldPositions;
|
||||
in layout(location = 2) vec3 worldPosition;
|
||||
|
||||
struct LightSource {
|
||||
vec3 position;
|
||||
@@ -10,75 +10,73 @@ struct LightSource {
|
||||
};
|
||||
|
||||
uniform LightSource lights[3];
|
||||
|
||||
uniform vec3 cameraPosition;
|
||||
uniform vec3 ballPosition;
|
||||
|
||||
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;
|
||||
}
|
||||
vec3 reject(vec3 from, vec3 onto) {
|
||||
return from - onto * dot(from, onto) / dot(onto, onto);
|
||||
}
|
||||
|
||||
const vec3 objectColor = vec3(1.0, 1.0, 1.0);
|
||||
out vec4 fragColor;
|
||||
|
||||
const vec3 baseColor = vec3(1.0);
|
||||
const float ambientStrength = 0.1;
|
||||
const float specularStrength = 0.5;
|
||||
const float shininess = 32.0;
|
||||
|
||||
const float la = 0.1, lb = 0.01, lc = 0.001;
|
||||
// attenuation: 1 / (la + lb*d + lc*d²)
|
||||
const float la = 0.1;
|
||||
const float lb = 0.01;
|
||||
const float lc = 0.001;
|
||||
|
||||
// soft shadow radii
|
||||
const float ballRadius = 1.0;
|
||||
|
||||
const float hardRadius = ballRadius;
|
||||
const float softRadius = ballRadius * 2.0;
|
||||
|
||||
float rand(vec2 co) {
|
||||
return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453);
|
||||
}
|
||||
float dither(vec2 uv) {
|
||||
return (rand(uv) * 2.0 - 1.0) / 256.0;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 norm = normalize(normal);
|
||||
vec3 viewDir = normalize(cameraPosition - worldPositions);
|
||||
vec3 N = normalize(normal);
|
||||
vec3 V = normalize(cameraPosition - worldPosition);
|
||||
|
||||
vec3 ambient = ambientStrength * vec3(1.0, 1.0, 1.0) * objectColor;
|
||||
vec3 result = ambient;
|
||||
// start with ambience
|
||||
vec3 result = ambientStrength * baseColor;
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
vec3 fragToLight = lights[i].position - worldPositions;
|
||||
vec3 lightDir = normalize(fragToLight);
|
||||
vec3 toLight = lights[i].position - worldPosition;
|
||||
vec3 L = normalize(toLight);
|
||||
float dist = length(toLight);
|
||||
|
||||
float d = length(fragToLight);
|
||||
float attenuation = 1.0 / (la + d * lb + d * d * lc);
|
||||
float attenuation = 1.0 / (la + lb * dist + lc * dist * dist);
|
||||
|
||||
float diff = max(dot(norm, lightDir), 0.0);
|
||||
float diff = max(dot(N, L), 0.0);
|
||||
vec3 diffuse = diff * lights[i].color;
|
||||
|
||||
vec3 halfwayDir = normalize(lightDir + viewDir); // little optimization (blinn-phong)
|
||||
float spec = pow(max(dot(norm, halfwayDir), 0.0), shininess);
|
||||
// blinn-phong half-vector
|
||||
vec3 H = normalize(L + V);
|
||||
float spec = pow(max(dot(N, H), 0.0), shininess);
|
||||
vec3 specular = specularStrength * spec * lights[i].color;
|
||||
|
||||
// shadow stuff
|
||||
vec3 fragToBall = ballPosition - worldPositions;
|
||||
float projDist = dot(fragToBall, lightDir);
|
||||
float r = length(fragToBall - projDist * lightDir);
|
||||
bool inShadowRegion = (projDist > 0.0) && (projDist < d);
|
||||
float shadowFactor = 1.0;
|
||||
vec3 toBall = ballPosition - worldPosition;
|
||||
float projDist = dot(toBall, L); // distance along light ray
|
||||
float perpDist = length(toBall - projDist * L); // perpendicular distance to ray
|
||||
bool inShadowCone = (projDist > 0.0) && (projDist < dist);
|
||||
|
||||
// exclude ball light to avoid casting shadows everywhere
|
||||
if (i != 2 && inShadowRegion) {
|
||||
if (r <= hardRadius) {
|
||||
shadowFactor = 0.0;
|
||||
} else if (r < softRadius) {
|
||||
shadowFactor = (r - hardRadius) / (softRadius - hardRadius);
|
||||
float shadow = 1.0;
|
||||
// skip ball's own light (index 2) to avoid self-shadowing the whole scene
|
||||
if (i != 2 && inShadowCone) {
|
||||
if (perpDist <= hardRadius) {
|
||||
shadow = 0.0;
|
||||
} else if (perpDist < softRadius) {
|
||||
shadow = (perpDist - hardRadius) / (softRadius - hardRadius);
|
||||
}
|
||||
}
|
||||
|
||||
result += shadowFactor * attenuation * (diffuse + specular) * objectColor;
|
||||
result += shadow * attenuation * (diffuse + specular) * baseColor;
|
||||
}
|
||||
|
||||
color = vec4(result + dither(textureCoordinates), 1.0);
|
||||
fragColor = vec4(result + dither(textureCoordinates), 1.0);
|
||||
}
|
||||
|
||||
+186
-284
@@ -2,70 +2,60 @@
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <glad/glad.h>
|
||||
#include <SFML/Audio/SoundBuffer.hpp>
|
||||
#include <string>
|
||||
#include <utilities/shader.hpp>
|
||||
#include <SFML/Audio/Sound.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/gtx/transform.hpp>
|
||||
#include <fmt/format.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <utilities/shader.hpp>
|
||||
#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 <fmt/format.h>
|
||||
#include <vector>
|
||||
#include <utilities/imageLoader.hpp>
|
||||
#include <utilities/glfont.h>
|
||||
|
||||
enum KeyFrameAction { BOTTOM, TOP };
|
||||
|
||||
#include "gamelogic.h"
|
||||
#include "glm/detail/qualifier.hpp"
|
||||
#include "glm/ext/vector_float3.hpp"
|
||||
#include "glm/matrix.hpp"
|
||||
#include "sceneGraph.hpp"
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include "utilities/imageLoader.hpp"
|
||||
#include "utilities/glfont.h"
|
||||
|
||||
enum KeyFrameAction {
|
||||
BOTTOM, TOP
|
||||
};
|
||||
|
||||
#include <timestamps.h>
|
||||
|
||||
double padPositionX = 0;
|
||||
double padPositionZ = 0;
|
||||
struct LightSource {
|
||||
glm::vec3 position;
|
||||
glm::vec3 color;
|
||||
};
|
||||
|
||||
unsigned int currentKeyFrame = 0;
|
||||
unsigned int previousKeyFrame = 0;
|
||||
const glm::vec3 boxDimensions(180, 90, 90);
|
||||
const glm::vec3 padDimensions(30, 3, 40);
|
||||
const double ballRadius = 3.0;
|
||||
|
||||
// modify to start music further into the track (seconds)
|
||||
const float debugStartTime = 0;
|
||||
|
||||
SceneNode* rootNode;
|
||||
SceneNode* boxNode;
|
||||
SceneNode* ballNode;
|
||||
SceneNode* padNode;
|
||||
|
||||
struct LightSource {
|
||||
glm::vec3 position;
|
||||
glm::vec3 color;
|
||||
};
|
||||
std::vector<SceneNode*> lightNodes;
|
||||
std::vector<LightSource> lights;
|
||||
|
||||
glm::vec3 cameraPosition;
|
||||
|
||||
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, 90);
|
||||
const glm::vec3 padDimensions(30, 3, 40);
|
||||
|
||||
glm::vec3 ballPosition(0, ballRadius + padDimensions.y, boxDimensions.z / 2);
|
||||
glm::vec3 ballDirection(1, 1, 0.2f);
|
||||
|
||||
CommandLineOptions options;
|
||||
double padPositionX = 0;
|
||||
double padPositionZ = 0;
|
||||
|
||||
unsigned int currentKeyFrame = 0;
|
||||
unsigned int previousKeyFrame = 0;
|
||||
|
||||
bool hasStarted = false;
|
||||
bool hasLost = false;
|
||||
@@ -77,41 +67,38 @@ 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 totalElapsedTime = debugStartTime;
|
||||
double gameElapsedTime = debugStartTime;
|
||||
|
||||
sf::SoundBuffer* buffer;
|
||||
sf::Sound* sound;
|
||||
Gloom::Shader* shader;
|
||||
CommandLineOptions options;
|
||||
|
||||
double mouseSensitivity = 1.0;
|
||||
double lastMouseX = windowWidth / 2;
|
||||
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);
|
||||
int w, h;
|
||||
glfwGetWindowSize(window, &w, &h);
|
||||
glViewport(0, 0, w, h);
|
||||
|
||||
double deltaX = x - lastMouseX;
|
||||
double deltaY = y - lastMouseY;
|
||||
double dx = x - lastMouseX;
|
||||
double dy = y - lastMouseY;
|
||||
|
||||
padPositionX -= mouseSensitivity * deltaX / windowWidth;
|
||||
padPositionZ -= mouseSensitivity * deltaY / windowHeight;
|
||||
padPositionX = glm::clamp(padPositionX - mouseSensitivity * dx / w, 0.0, 1.0);
|
||||
padPositionZ = glm::clamp(padPositionZ - mouseSensitivity * dy / h, 0.0, 1.0);
|
||||
|
||||
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);
|
||||
glfwSetCursorPos(window, w / 2, h / 2);
|
||||
}
|
||||
|
||||
void initGame(GLFWwindow* window, CommandLineOptions gameOptions) {
|
||||
buffer = new sf::SoundBuffer();
|
||||
if (!buffer->loadFromFile("../res/Hall of the Mountain King.ogg")) {
|
||||
return;
|
||||
}
|
||||
|
||||
options = gameOptions;
|
||||
|
||||
buffer = new sf::SoundBuffer();
|
||||
if (!buffer->loadFromFile("../res/Hall of the Mountain King.ogg")) return;
|
||||
|
||||
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
|
||||
glfwSetCursorPosCallback(window, mouseCallback);
|
||||
|
||||
@@ -119,32 +106,42 @@ void initGame(GLFWwindow* window, CommandLineOptions gameOptions) {
|
||||
shader->makeBasicShader("../res/shaders/simple.vert", "../res/shaders/simple.frag");
|
||||
shader->activate();
|
||||
|
||||
// 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);
|
||||
Mesh padMesh = cube(padDimensions, glm::vec2(30, 40), true);
|
||||
Mesh boxMesh = cube(boxDimensions, glm::vec2(90), true, true); // inverted for interior view
|
||||
Mesh ballMesh = generateSphere(1.0, 40, 40);
|
||||
|
||||
// Fill buffers
|
||||
unsigned int ballVAO = generateBuffer(sphere);
|
||||
unsigned int boxVAO = generateBuffer(box);
|
||||
unsigned int padVAO = generateBuffer(pad);
|
||||
unsigned int padVAO = generateBuffer(padMesh);
|
||||
unsigned int boxVAO = generateBuffer(boxMesh);
|
||||
unsigned int ballVAO = generateBuffer(ballMesh);
|
||||
|
||||
// Construct scene
|
||||
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 = boxMesh.indices.size();
|
||||
|
||||
padNode->vertexArrayObjectID = padVAO;
|
||||
padNode->VAOIndexCount = padMesh.indices.size();
|
||||
|
||||
ballNode->vertexArrayObjectID = ballVAO;
|
||||
ballNode->VAOIndexCount = ballMesh.indices.size();
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
auto lightNode = new SceneNode();
|
||||
lightNode->nodeType = POINT_LIGHT;
|
||||
lightNode->lightIdx = i;
|
||||
lightNodes.push_back(lightNode);
|
||||
auto* light = new SceneNode();
|
||||
light->nodeType = POINT_LIGHT;
|
||||
light->lightIdx = i;
|
||||
lightNodes.push_back(light);
|
||||
}
|
||||
|
||||
auto ceilingLight = lightNodes.at(0);
|
||||
auto padLight = lightNodes.at(1);
|
||||
auto ballLight = lightNodes.at(2);
|
||||
auto* ceilingLight = lightNodes[0];
|
||||
auto* padLight = lightNodes[1];
|
||||
auto* ballLight = lightNodes[2];
|
||||
|
||||
boxNode->children.push_back(ceilingLight);
|
||||
ceilingLight->position = { 0, 10, 0 };
|
||||
@@ -155,301 +152,206 @@ void initGame(GLFWwindow* window, CommandLineOptions gameOptions) {
|
||||
ballNode->children.push_back(ballLight);
|
||||
ballLight->position = { 0, 0, 0 };
|
||||
|
||||
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();
|
||||
getTimeDeltaSeconds(); // prime the timer
|
||||
|
||||
std::cout << fmt::format("Initialized scene with {} SceneNodes.", totalChildren(rootNode)) << std::endl;
|
||||
|
||||
std::cout << "Ready. Click to start!" << std::endl;
|
||||
}
|
||||
|
||||
void updateFrame(GLFWwindow* window) {
|
||||
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
||||
double dt = getTimeDeltaSeconds();
|
||||
|
||||
double timeDelta = getTimeDeltaSeconds();
|
||||
const float cameraWallOffset = 30;
|
||||
const float ballBottomY = boxNode->position.y - boxDimensions.y/2 + ballRadius + padDimensions.y;
|
||||
const float ballTopY = boxNode->position.y + boxDimensions.y/2 - ballRadius;
|
||||
const float travelY = ballTopY - ballBottomY;
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
const float cameraWallOffset = 30; // Arbitrary addition to prevent ball from going too much into camera
|
||||
mouseLeftReleased = !glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1) && mouseLeftPressed;
|
||||
mouseLeftPressed = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1);
|
||||
mouseRightReleased = !glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_2) && mouseRightPressed;
|
||||
mouseRightPressed = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_2);
|
||||
|
||||
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 (!hasStarted) {
|
||||
if (mouseLeftPressed) {
|
||||
if (options.enableMusic) {
|
||||
sound = new sf::Sound();
|
||||
sound->setBuffer(*buffer);
|
||||
sf::Time startTime = sf::seconds(debug_startTime);
|
||||
sound->setPlayingOffset(startTime);
|
||||
sound->setPlayingOffset(sf::seconds(debugStartTime));
|
||||
sound->play();
|
||||
}
|
||||
totalElapsedTime = debug_startTime;
|
||||
gameElapsedTime = debug_startTime;
|
||||
totalElapsedTime = debugStartTime;
|
||||
gameElapsedTime = debugStartTime;
|
||||
hasStarted = true;
|
||||
}
|
||||
|
||||
ballPosition.x = ballMinX + (1 - padPositionX) * (ballMaxX - ballMinX);
|
||||
ballPosition.y = ballBottomY;
|
||||
ballPosition.z = ballMinZ + (1 - padPositionZ) * ((ballMaxZ+cameraWallOffset) - ballMinZ);
|
||||
ballPosition.z = ballMinZ + (1 - padPositionZ) * (ballMaxZ + cameraWallOffset - ballMinZ);
|
||||
|
||||
} else {
|
||||
totalElapsedTime += timeDelta;
|
||||
if(hasLost) {
|
||||
totalElapsedTime += dt;
|
||||
|
||||
if (hasLost) {
|
||||
if (mouseLeftReleased) {
|
||||
hasLost = false;
|
||||
hasStarted = false;
|
||||
currentKeyFrame = 0;
|
||||
previousKeyFrame = 0;
|
||||
hasLost = hasStarted = false;
|
||||
currentKeyFrame = previousKeyFrame = 0;
|
||||
}
|
||||
} else if (isPaused) {
|
||||
if (mouseRightReleased) {
|
||||
isPaused = false;
|
||||
if (options.enableMusic) {
|
||||
sound->play();
|
||||
}
|
||||
if (options.enableMusic) sound->play();
|
||||
}
|
||||
} else {
|
||||
gameElapsedTime += timeDelta;
|
||||
gameElapsedTime += dt;
|
||||
if (mouseRightReleased) {
|
||||
isPaused = true;
|
||||
if (options.enableMusic) {
|
||||
sound->pause();
|
||||
}
|
||||
if (options.enableMusic) sound->pause();
|
||||
}
|
||||
// Get the timing for the beat of the song
|
||||
|
||||
for (unsigned int i = currentKeyFrame; i < keyFrameTimeStamps.size(); i++) {
|
||||
if (gameElapsedTime < keyFrameTimeStamps.at(i)) {
|
||||
continue;
|
||||
}
|
||||
currentKeyFrame = i;
|
||||
if (gameElapsedTime >= keyFrameTimeStamps[i]) currentKeyFrame = i;
|
||||
}
|
||||
jumpedToNextFrame = (currentKeyFrame != previousKeyFrame);
|
||||
previousKeyFrame = currentKeyFrame;
|
||||
|
||||
jumpedToNextFrame = currentKeyFrame != previousKeyFrame;
|
||||
previousKeyFrame = currentKeyFrame;
|
||||
double t0 = keyFrameTimeStamps[currentKeyFrame];
|
||||
double t1 = keyFrameTimeStamps[currentKeyFrame + 1];
|
||||
double frac = (gameElapsedTime - t0) / (t1 - t0);
|
||||
|
||||
double frameStart = keyFrameTimeStamps.at(currentKeyFrame);
|
||||
double frameEnd = keyFrameTimeStamps.at(currentKeyFrame + 1); // Assumes last keyframe at infinity
|
||||
KeyFrameAction from = keyFrameDirections[currentKeyFrame];
|
||||
KeyFrameAction to = keyFrameDirections[currentKeyFrame + 1];
|
||||
|
||||
double elapsedTimeInFrame = gameElapsedTime - frameStart;
|
||||
double frameDuration = frameEnd - frameStart;
|
||||
double fractionFrameComplete = elapsedTimeInFrame / frameDuration;
|
||||
double ballY = ballBottomY;
|
||||
if (from == BOTTOM && to == TOP) ballY = ballBottomY + travelY * frac;
|
||||
else if (from == TOP && to == BOTTOM) ballY = ballBottomY + travelY * (1 - frac);
|
||||
else if (from == TOP && to == TOP) ballY = ballTopY;
|
||||
|
||||
double ballYCoord;
|
||||
|
||||
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) {
|
||||
ballYCoord = ballBottomY + BallVerticalTravelDistance;
|
||||
} else if (currentDestination == BOTTOM) {
|
||||
ballYCoord = ballBottomY + BallVerticalTravelDistance * (1 - fractionFrameComplete);
|
||||
} else if (currentDestination == TOP) {
|
||||
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;
|
||||
ballPosition.x += dt * ballSpeed * ballDirection.x;
|
||||
ballPosition.y = ballY;
|
||||
ballPosition.z += dt * ballSpeed * ballDirection.z;
|
||||
|
||||
// Make ball bounce
|
||||
if (ballPosition.x < ballMinX) {
|
||||
ballPosition.x = ballMinX;
|
||||
ballDirection.x *= -1;
|
||||
} else if (ballPosition.x > ballMaxX) {
|
||||
ballPosition.x = ballMaxX;
|
||||
ballDirection.x *= -1;
|
||||
}
|
||||
if (ballPosition.z < ballMinZ) {
|
||||
ballPosition.z = ballMinZ;
|
||||
ballDirection.z *= -1;
|
||||
} else if (ballPosition.z > ballMaxZ) {
|
||||
ballPosition.z = ballMaxZ;
|
||||
ballDirection.z *= -1;
|
||||
if (ballPosition.x < ballMinX) { ballPosition.x = ballMinX; ballDirection.x *= -1; }
|
||||
if (ballPosition.x > ballMaxX) { ballPosition.x = ballMaxX; ballDirection.x *= -1; }
|
||||
if (ballPosition.z < ballMinZ) { ballPosition.z = ballMinZ; ballDirection.z *= -1; }
|
||||
if (ballPosition.z > ballMaxZ) { ballPosition.z = ballMaxZ; ballDirection.z *= -1; }
|
||||
|
||||
if (options.enableAutoplay) {
|
||||
padPositionX = 1 - (ballPosition.x - ballMinX) / (ballMaxX - ballMinX);
|
||||
padPositionZ = 1 - (ballPosition.z - ballMinZ) / (ballMaxZ + cameraWallOffset - ballMinZ);
|
||||
}
|
||||
|
||||
if(options.enableAutoplay) {
|
||||
padPositionX = 1-(ballPosition.x - ballMinX) / (ballMaxX - ballMinX);
|
||||
padPositionZ = 1-(ballPosition.z - ballMinZ) / ((ballMaxZ+cameraWallOffset) - ballMinZ);
|
||||
}
|
||||
if (jumpedToNextFrame && from == BOTTOM && to == TOP) {
|
||||
double padL = boxNode->position.x - boxDimensions.x/2 + (1 - padPositionX) * (boxDimensions.x - padDimensions.x);
|
||||
double padR = padL + padDimensions.x;
|
||||
double padF = boxNode->position.z - boxDimensions.z/2 + (1 - padPositionZ) * (boxDimensions.z - padDimensions.z);
|
||||
double padB = padF + padDimensions.z;
|
||||
|
||||
// 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 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;
|
||||
|
||||
if ( ballPosition.x < padLeftX
|
||||
|| ballPosition.x > padRightX
|
||||
|| ballPosition.z < padFrontZ
|
||||
|| ballPosition.z > padBackZ
|
||||
) {
|
||||
if (ballPosition.x < padL || ballPosition.x > padR ||
|
||||
ballPosition.z < padF || ballPosition.z > padB) {
|
||||
hasLost = true;
|
||||
if (options.enableMusic) {
|
||||
sound->stop();
|
||||
delete sound;
|
||||
}
|
||||
if (options.enableMusic) { sound->stop(); delete sound; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glm::mat4 projection = glm::perspective(glm::radians(80.0f), float(windowWidth) / float(windowHeight), 0.1f, 350.f);
|
||||
|
||||
cameraPosition = glm::vec3(0, 2, -20);
|
||||
float lookRot = -0.6f / (1 + exp(-5 * (padPositionX - 0.5))) + 0.3f;
|
||||
|
||||
// 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 view = glm::rotate(0.3f + 0.2f * float(-padPositionZ * padPositionZ), glm::vec3(1,0,0))
|
||||
* glm::rotate(lookRot, glm::vec3(0,1,0))
|
||||
* glm::translate(-cameraPosition);
|
||||
|
||||
glm::mat4 VP = projection * cameraTransform;
|
||||
glm::mat4 proj = glm::perspective(glm::radians(80.0f),
|
||||
float(windowWidth) / float(windowHeight),
|
||||
0.1f, 350.f);
|
||||
glm::mat4 VP = proj * view;
|
||||
|
||||
// Move and rotate various SceneNodes
|
||||
boxNode->position = { 0, -10, -80 };
|
||||
|
||||
ballNode->position = ballPosition;
|
||||
ballNode->scale = glm::vec3(ballRadius);
|
||||
ballNode->rotation = { 0, totalElapsedTime*2, 0 };
|
||||
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)
|
||||
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, glm::mat4(1.0f)); // Pass identity for initial model matrix
|
||||
updateNodeTransformations(rootNode, VP, glm::mat4(1.0f));
|
||||
|
||||
// update lights
|
||||
lights.clear();
|
||||
for (auto* lightNode : lightNodes) {
|
||||
LightSource light;
|
||||
light.position = lightNode->worldPosition;
|
||||
for (auto* node : lightNodes) {
|
||||
LightSource l;
|
||||
l.position = node->worldPosition;
|
||||
|
||||
if (lightNode->lightIdx == 0) {
|
||||
light.color = glm::vec3(1.0f, 0.0f, 0.0f); // Warm white (ceiling)
|
||||
} else if (lightNode->lightIdx == 1) {
|
||||
light.color = glm::vec3(0.0f, 1.0f, 0.0f); // Cool white (pad)
|
||||
} else {
|
||||
light.color = glm::vec3(0.0f, 0.0f, 1.0f); // Orange (ball)
|
||||
switch (node->lightIdx) {
|
||||
case 0: l.color = glm::vec3(1, 0, 0); break; // red (ceiling)
|
||||
case 1: l.color = glm::vec3(0, 1, 0); break; // green (pad)
|
||||
case 2: l.color = glm::vec3(0, 0, 1); break; // blue (ball)
|
||||
}
|
||||
lights.push_back(light);
|
||||
lights.push_back(l);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void updateNodeTransformations(SceneNode* node, glm::mat4 transformationThusFar, glm::mat4 modelMatrixThusFar) {
|
||||
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);
|
||||
void updateNodeTransformations(SceneNode* node, glm::mat4 vpSoFar, glm::mat4 modelSoFar) {
|
||||
glm::mat4 local = 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; // MVP
|
||||
node->modelMatrix = modelMatrixThusFar * transformationMatrix; // Accumulated world transform
|
||||
node->currentTransformationMatrix = vpSoFar * local; // MVP
|
||||
node->modelMatrix = modelSoFar * local; // world transform
|
||||
node->normalMatrix = glm::transpose(glm::inverse(glm::mat3(node->modelMatrix)));
|
||||
|
||||
switch(node->nodeType) {
|
||||
case GEOMETRY: break;
|
||||
case POINT_LIGHT:
|
||||
node->worldPosition = glm::vec3(node->modelMatrix * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
break;
|
||||
case SPOT_LIGHT: break;
|
||||
if (node->nodeType == POINT_LIGHT) {
|
||||
node->worldPosition = glm::vec3(node->modelMatrix * glm::vec4(0, 0, 0, 1));
|
||||
}
|
||||
|
||||
for(SceneNode* child : node->children) {
|
||||
for (auto* child : node->children) {
|
||||
updateNodeTransformations(child, node->currentTransformationMatrix, node->modelMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void renderNode(SceneNode* node) {
|
||||
glUniformMatrix4fv(3, 1, GL_FALSE, glm::value_ptr(node->currentTransformationMatrix));
|
||||
glUniformMatrix4fv(4, 1, GL_FALSE, glm::value_ptr(node->modelMatrix));
|
||||
glUniformMatrix3fv(5, 1, GL_FALSE, glm::value_ptr(node->normalMatrix));
|
||||
|
||||
GLint cameraPosLoc = shader->getUniformFromName("cameraPosition");
|
||||
glUniform3fv(cameraPosLoc, 1, glm::value_ptr(cameraPosition));
|
||||
glUniform3fv(shader->getUniformFromName("cameraPosition"), 1, glm::value_ptr(cameraPosition));
|
||||
glUniform3fv(shader->getUniformFromName("ballPosition"), 1, glm::value_ptr(ballPosition));
|
||||
|
||||
GLint ballPosLoc = shader->getUniformFromName("ballPosition");
|
||||
glUniform3fv(ballPosLoc, 1, glm::value_ptr(ballPosition));
|
||||
|
||||
for (int i = 0; i < lights.size(); i++) {
|
||||
std::string posName = "lights[" + std::to_string(i) + "].position";
|
||||
std::string colorName = "lights[" + std::to_string(i) + "].color";
|
||||
|
||||
GLint posLoc = shader->getUniformFromName(posName.c_str());
|
||||
GLint colorLoc = shader->getUniformFromName(colorName.c_str());
|
||||
|
||||
glUniform3fv(posLoc, 1, glm::value_ptr(lights[i].position));
|
||||
glUniform3fv(colorLoc, 1, glm::value_ptr(lights[i].color));
|
||||
for (size_t i = 0; i < lights.size(); i++) {
|
||||
std::string base = "lights[" + std::to_string(i) + "]";
|
||||
glUniform3fv(shader->getUniformFromName((base + ".position").c_str()), 1, glm::value_ptr(lights[i].position));
|
||||
glUniform3fv(shader->getUniformFromName((base + ".color").c_str()), 1, glm::value_ptr(lights[i].color));
|
||||
}
|
||||
|
||||
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;
|
||||
if (node->nodeType == GEOMETRY && node->vertexArrayObjectID != -1) {
|
||||
glBindVertexArray(node->vertexArrayObjectID);
|
||||
glDrawElements(GL_TRIANGLES, node->VAOIndexCount, GL_UNSIGNED_INT, nullptr);
|
||||
}
|
||||
|
||||
for(SceneNode* child : node->children) {
|
||||
for (auto* child : node->children) {
|
||||
renderNode(child);
|
||||
}
|
||||
}
|
||||
|
||||
void renderFrame(GLFWwindow* window) {
|
||||
int windowWidth, windowHeight;
|
||||
glfwGetWindowSize(window, &windowWidth, &windowHeight);
|
||||
glViewport(0, 0, windowWidth, windowHeight);
|
||||
int w, h;
|
||||
glfwGetWindowSize(window, &w, &h);
|
||||
glViewport(0, 0, w, h);
|
||||
|
||||
renderNode(rootNode);
|
||||
}
|
||||
|
||||
+1
-2
@@ -1,11 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include <utilities/window.hpp>
|
||||
#include "sceneGraph.hpp"
|
||||
|
||||
void updateNodeTransformations(SceneNode* node, glm::mat4 transformationThusFar, glm::mat4 modelMatrixThusFar);
|
||||
void updateNodeTransformations(SceneNode* node, glm::mat4 vpSoFar, glm::mat4 modelSoFar);
|
||||
void initGame(GLFWwindow* window, CommandLineOptions options);
|
||||
void updateFrame(GLFWwindow* window);
|
||||
void renderFrame(GLFWwindow* window);
|
||||
|
||||
Reference in New Issue
Block a user