diff --git a/res/shaders/simple.frag b/res/shaders/simple.frag index 5272811..6196a8c 100644 --- a/res/shaders/simple.frag +++ b/res/shaders/simple.frag @@ -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); } diff --git a/src/gamelogic.cpp b/src/gamelogic.cpp index 9216c09..8f009e0 100644 --- a/src/gamelogic.cpp +++ b/src/gamelogic.cpp @@ -2,70 +2,60 @@ #include #include #include -#include -#include +#include #include +#include +#include +#define GLM_ENABLE_EXPERIMENTAL +#include +#include #include +#include +#include + +#include #include #include #include #include -#include -#include -#include -#include -#include +#include +#include + +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 - -#include "utilities/imageLoader.hpp" -#include "utilities/glfont.h" - -enum KeyFrameAction { - BOTTOM, TOP -}; - #include -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 lightNodes; std::vector 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); } diff --git a/src/gamelogic.h b/src/gamelogic.h index ca0f534..d4a4e87 100644 --- a/src/gamelogic.h +++ b/src/gamelogic.h @@ -1,11 +1,10 @@ #pragma once #include - #include #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);