diff --git a/.gitignore b/.gitignore index 259148f..7170c9a 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,5 @@ *.exe *.out *.app + +build/ diff --git a/res/shaders/simple.frag b/res/shaders/simple.frag index 8eed273..eb212a0 100644 --- a/res/shaders/simple.frag +++ b/res/shaders/simple.frag @@ -1,12 +1,67 @@ #version 430 core in layout(location = 0) vec3 normal; +in layout(location = 1) vec3 vertex; + +// point lights +struct Light { + vec3 position; + mat4 MV; + bool is_spot; + vec3 spot_target; // MV space coordinates +}; + +#define N_LIGHTS 3 + +uniform Light light[N_LIGHTS]; out vec4 color; +// constants +float shininess = 15; +vec3 c_diffuse = vec3(0.75390625, 0.4296875, 0.4375); +vec3 c_emissive = vec3(0.01171875, 0.0, 0.15234375); +vec3 c_specular = vec3(0.9453125, 0.94921875, 0.84765625); + +float spot_cuttof_angle = cos(4 / 180.0 * 3.1415926535); + 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); + vec3 nnormal = normalize(normal); + + float diffuse_intensity = 0.0; + float specular_intensity = 0.0; + + for (int i = 0; i<3; i++) { + vec3 L = vec3(light[i].MV * vec4(light[i].position, 1.0f)) - vertex; + float attenuation = clamp(1000/(1 + 40*length(L) + 0.1*length(L)*length(L)), 0.0, 1.25); + L = normalize(L); + + if (light[i].is_spot) { + vec3 L2 = normalize(vec3(light[i].MV * vec4(light[i].position, 1.0f)) - light[i].spot_target); + if (dot(L2, L) < spot_cuttof_angle) { + continue; + } + attenuation *= 70; + } + + float diffuse_i = dot(nnormal, L); + + float specular_i = pow(dot(reflect(-L, nnormal), normalize(vec3(0,0,0) - vertex)), shininess); + + + if (diffuse_i > 0) diffuse_intensity += attenuation*diffuse_i; + if (specular_i > 0) specular_intensity += attenuation*specular_i; + } + + //diffuse_intensity *= 1.0 / N_LIGHTS; + //specular_intensity *= 1.0 / N_LIGHTS; + + + //float intensity = dot(normalize(normal), normalize(light_pos_2)); + //color = vec4(0.5 * normal + 0.5, 1.0); + + color = vec4(c_emissive + + c_diffuse*diffuse_intensity + + c_specular*specular_intensity, 1.0f); } diff --git a/res/shaders/simple.vert b/res/shaders/simple.vert index 1d2cbcb..d15a76a 100644 --- a/res/shaders/simple.vert +++ b/res/shaders/simple.vert @@ -4,11 +4,16 @@ in layout(location = 0) vec3 position; in layout(location = 1) vec3 normal_in; uniform layout(location = 3) mat4 MVP; +uniform layout(location = 4) mat4 MV; +uniform layout(location = 5) mat4 MVnormal; + out layout(location = 0) vec3 normal_out; +out layout(location = 1) vec3 vertex_out; void main() { - normal_out = normal_in; - gl_Position = MVP * vec4(position, 1.0f); + normal_out = normalize(vec3(MVnormal * vec4(normal_in, 1.0f))); + vertex_out = vec3(MV*vec4(position, 1.0f)); + gl_Position = MVP * vec4(position, 1.0f); } diff --git a/src/gamelogic.cpp b/src/gamelogic.cpp index bef19ac..c7670a0 100644 --- a/src/gamelogic.cpp +++ b/src/gamelogic.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include "gamelogic.h" #include "sceneGraph.hpp" @@ -33,6 +34,8 @@ SceneNode* boxNode; SceneNode* ballNode; SceneNode* padNode; +SceneNode* lightNode[3]; + double ballRadius = 3.0f; // These are heap allocated, because they should not be initialised at the start of the program @@ -55,7 +58,7 @@ bool hasLost = false; bool jumpedToNextFrame = false; // Modify if you want the music to start further on in the track. Measured in seconds. -const float debug_startTime = 0; +const float debug_startTime = 45; double totalElapsedTime = debug_startTime; @@ -108,7 +111,7 @@ void initGame(GLFWwindow* window, CommandLineOptions gameOptions) { unsigned int padVAO = generateBuffer(pad); rootNode = createSceneNode(); - boxNode = createSceneNode(); + boxNode = createSceneNode() ; padNode = createSceneNode(); ballNode = createSceneNode(); @@ -125,12 +128,28 @@ void initGame(GLFWwindow* window, CommandLineOptions gameOptions) { ballNode->vertexArrayObjectID = ballVAO; ballNode->VAOIndexCount = sphere.indices.size(); + // task 1a, add point lights + for (int i = 0; i<3; i++) { + lightNode[i] = createSceneNode(); + lightNode[i]->nodeType = SceneNodeType::POINT_LIGHT; + lightNode[i]->lightID = i; + } + rootNode->children.push_back(lightNode[0]); + rootNode->children.push_back(lightNode[1]); + ballNode->children.push_back(lightNode[2]); + lightNode[0]->position = {boxDimensions.x/2 - 10, boxDimensions.y/2 - 10, boxDimensions.z/2 - 10}; + lightNode[1]->position = {300,300,400}; + lightNode[2]->position = {0, 0, 0}; + + lightNode[1]->nodeType = SPOT_LIGHT; + padNode->targeted_by = lightNode[1]; + getTimeDeltaSeconds(); std::cout << "Ready. Click to start!" << std::endl; } -void updateNodeTransformations(SceneNode* node, glm::mat4 transformationThusFar) { +void updateNodeTransformations(SceneNode* node, glm::mat4 transformationThusFar, glm::mat4 V, glm::mat4 P) { glm::mat4 transformationMatrix(1.0); @@ -146,17 +165,30 @@ void updateNodeTransformations(SceneNode* node, glm::mat4 transformationThusFar) * glm::scale(glm::mat4(1.0), node->scale); break; case POINT_LIGHT: - - break; case SPOT_LIGHT: - + transformationMatrix = + glm::translate(glm::mat4(1.0), node->position); break; } + glm::mat4 M = transformationThusFar * transformationMatrix; + glm::mat4 MV = V*M; - node->currentTransformationMatrix = transformationThusFar * transformationMatrix; + node->currentTransformationMatrixMV = MV; + node->currentTransformationMatrix = P*MV; + node->currentTransformationMatrixMVnormal = glm::inverse(glm::transpose(MV)); for(SceneNode* child : node->children) { - updateNodeTransformations(child, node->currentTransformationMatrix); + updateNodeTransformations(child, M, V, P); + } + + if (node->targeted_by != nullptr) { + assert(node->targeted_by->nodeType == SPOT_LIGHT); + node->targeted_by->rotation = glm::vec3(MV*glm::vec4(node->position, 1.0)); + + //std::cout << node->targeted_by->rotation[0] + // << " " << node->targeted_by->rotation[1] + // << " " << node->targeted_by->rotation[2] + // << std::endl; } } @@ -295,9 +327,7 @@ void updateFrame(GLFWwindow* window) { * glm::rotate(glm::mat4(1.0), 0.2f, glm::vec3(1, 0, 0)) * glm::rotate(glm::mat4(1.0), float(M_PI), glm::vec3(0, 1, 0)); - glm::mat4 VP = projection * cameraTransform; - - updateNodeTransformations(rootNode, VP); + updateNodeTransformations(rootNode, glm::mat4(1.0), cameraTransform, projection); boxNode->position = {-boxDimensions.x / 2, -boxDimensions.y / 2 - 15, boxDimensions.z - 10}; padNode->position = {-boxDimensions.x / 2 + (1 - padPositionX) * (boxDimensions.x - padDimensions.x), @@ -313,6 +343,8 @@ void updateFrame(GLFWwindow* window) { void renderNode(SceneNode* node) { glUniformMatrix4fv(3, 1, GL_FALSE, glm::value_ptr(node->currentTransformationMatrix)); + glUniformMatrix4fv(4, 1, GL_FALSE, glm::value_ptr(node->currentTransformationMatrixMV)); + glUniformMatrix4fv(5, 1, GL_FALSE, glm::value_ptr(node->currentTransformationMatrixMVnormal)); switch(node->nodeType) { case GEOMETRY: @@ -320,13 +352,19 @@ void renderNode(SceneNode* node) { glBindVertexArray(node->vertexArrayObjectID); glDrawElements(GL_TRIANGLES, node->VAOIndexCount, GL_UNSIGNED_INT, nullptr); } - break; - case POINT_LIGHT: - break; case SPOT_LIGHT: + case POINT_LIGHT: { + std::string pre = "light[" + std::to_string(node->lightID) + "]"; + + glUniform3fv(shader->location(pre+".position"), 1, glm::value_ptr(node->position)); + glUniformMatrix4fv(shader->location(pre+".MV"), 1, GL_FALSE, glm::value_ptr(node->currentTransformationMatrixMV)); + + glUniform1i(shader->location(pre+".is_spot"), node->nodeType == SPOT_LIGHT); + glUniform3fv(shader->location(pre+".spot_target"), 1, glm::value_ptr(node->rotation)); break; + } } for(SceneNode* child : node->children) { diff --git a/src/program.cpp b/src/program.cpp index bb45890..606d88f 100644 --- a/src/program.cpp +++ b/src/program.cpp @@ -57,7 +57,8 @@ void runProgram(GLFWwindow* window, CommandLineOptions options) void handleKeyboardInput(GLFWwindow* window) { // Use escape key for terminating the GLFW window - if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) + if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS + || glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS) { glfwSetWindowShouldClose(window, GL_TRUE); } diff --git a/src/sceneGraph.hpp b/src/sceneGraph.hpp index 51719f3..d48df58 100644 --- a/src/sceneGraph.hpp +++ b/src/sceneGraph.hpp @@ -1,67 +1,74 @@ -#pragma once - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -enum SceneNodeType { - GEOMETRY, POINT_LIGHT, SPOT_LIGHT -}; - -// In case you haven't got much experience with C or C++, let me explain this "typedef" you see below. -// The point of a typedef is that you it, as its name implies, allows you to define arbitrary data types based upon existing ones. For instance, "typedef float typeWhichMightBeAFloat;" allows you to define a variable such as this one: "typeWhichMightBeAFloat variableName = 5.0;". The C/C++ compiler translates this type into a float. -// What is the point of using it here? A smrt person, while designing the C language, thought it would be a good idea for various reasons to force you to explicitly state that you are using a data structure datatype (struct). So, when defining a variable, you'd have to type "struct SceneNode node = ..." in the case of a SceneNode. Which can get in the way of readability. -// If we just use typedef to define a new type called "SceneNode", which really is the type "struct SceneNode", we can omit the "struct" part when creating an instance of SceneNode. -struct SceneNode { - SceneNode() { - position = glm::vec3(0, 0, 0); - rotation = glm::vec3(0, 0, 0); - scale = glm::vec3(1, 1, 1); - - referencePoint = glm::vec3(0, 0, 0); - vertexArrayObjectID = -1; - VAOIndexCount = 0; - - nodeType = GEOMETRY; - } - - // A list of all children that belong to this node. - // For instance, in case of the scene graph of a human body shown in the assignment text, the "Upper Torso" node would contain the "Left Arm", "Right Arm", "Head" and "Lower Torso" nodes in its list of children. - std::vector children; - - // The node's position and rotation relative to its parent - glm::vec3 position; - glm::vec3 rotation; - glm::vec3 scale; - - // A transformation matrix representing the transformation of the node's location relative to its parent. This matrix is updated every frame. - glm::mat4 currentTransformationMatrix; - - // The location of the node's reference point - glm::vec3 referencePoint; - - // The ID of the VAO containing the "appearance" of this SceneNode. - int vertexArrayObjectID; - unsigned int VAOIndexCount; - - // Node type is used to determine how to handle the contents of a node - SceneNodeType nodeType; -}; - -// Struct for keeping track of 2D coordinates - -SceneNode* createSceneNode(); -void addChild(SceneNode* parent, SceneNode* child); -void printNode(SceneNode* node); - - -// For more details, see SceneGraph.cpp. \ No newline at end of file +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +enum SceneNodeType { + GEOMETRY, POINT_LIGHT, SPOT_LIGHT +}; + +// In case you haven't got much experience with C or C++, let me explain this "typedef" you see below. +// The point of a typedef is that you it, as its name implies, allows you to define arbitrary data types based upon existing ones. For instance, "typedef float typeWhichMightBeAFloat;" allows you to define a variable such as this one: "typeWhichMightBeAFloat variableName = 5.0;". The C/C++ compiler translates this type into a float. +// What is the point of using it here? A smrt person, while designing the C language, thought it would be a good idea for various reasons to force you to explicitly state that you are using a data structure datatype (struct). So, when defining a variable, you'd have to type "struct SceneNode node = ..." in the case of a SceneNode. Which can get in the way of readability. +// If we just use typedef to define a new type called "SceneNode", which really is the type "struct SceneNode", we can omit the "struct" part when creating an instance of SceneNode. +struct SceneNode { + SceneNode() { + position = glm::vec3(0, 0, 0); + rotation = glm::vec3(0, 0, 0); + scale = glm::vec3(1, 1, 1); + + referencePoint = glm::vec3(0, 0, 0); + vertexArrayObjectID = -1; + VAOIndexCount = 0; + + nodeType = GEOMETRY; + targeted_by = nullptr; + } + + // A list of all children that belong to this node. + // For instance, in case of the scene graph of a human body shown in the assignment text, the "Upper Torso" node would contain the "Left Arm", "Right Arm", "Head" and "Lower Torso" nodes in its list of children. + std::vector children; + + // The node's position and rotation relative to its parent + glm::vec3 position; + glm::vec3 rotation; + glm::vec3 scale; + + // A transformation matrix representing the transformation of the node's location relative to its parent. This matrix is updated every frame. + glm::mat4 currentTransformationMatrix; // MVP + glm::mat4 currentTransformationMatrixMV; // MV + glm::mat4 currentTransformationMatrixMVnormal; // transpose(inverse(MV)) + + // The location of the node's reference point + glm::vec3 referencePoint; + + // The ID of the VAO containing the "appearance" of this SceneNode. + int vertexArrayObjectID; + unsigned int VAOIndexCount; + + // Node type is used to determine how to handle the contents of a node + SceneNodeType nodeType; + + // for lights: + unsigned int lightID; + SceneNode* targeted_by; // spot +}; + +// Struct for keeping track of 2D coordinates + +SceneNode* createSceneNode(); +void addChild(SceneNode* parent, SceneNode* child); +void printNode(SceneNode* node); + + +// For more details, see SceneGraph.cpp. diff --git a/src/utilities/shader.hpp b/src/utilities/shader.hpp index bd722be..c73e4c9 100644 --- a/src/utilities/shader.hpp +++ b/src/utilities/shader.hpp @@ -19,6 +19,11 @@ namespace Gloom public: Shader() { mProgram = glCreateProgram(); } + // hack?: + GLint location(std::string const& name) { + return glGetUniformLocation(mProgram, name.c_str()); + } + // Public member functions void activate() { glUseProgram(mProgram); } void deactivate() { glUseProgram(0); }