diff --git a/assets/shaders/basicFragment.frag b/assets/shaders/basicFragment.frag index 49c55db..ad71e9a 100644 --- a/assets/shaders/basicFragment.frag +++ b/assets/shaders/basicFragment.frag @@ -1,7 +1,6 @@ #version 330 core out vec4 FragColor; -in vec3 ourColor; in vec2 TexCoord; uniform sampler2D ourTexture; @@ -9,5 +8,5 @@ uniform sampler2D decal; void main() { - FragColor = mix(texture(ourTexture, TexCoord), texture(decal, TexCoord), 0.2); + FragColor = mix(texture(ourTexture, TexCoord), texture(decal, TexCoord), 0.2f); } \ No newline at end of file diff --git a/assets/shaders/basicVertex.vert b/assets/shaders/basicVertex.vert index f6783a5..ea883bd 100644 --- a/assets/shaders/basicVertex.vert +++ b/assets/shaders/basicVertex.vert @@ -3,7 +3,6 @@ layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aColor; layout (location = 2) in vec2 aTexCoord; -out vec3 ourColor; out vec2 TexCoord; uniform mat4 model; @@ -13,8 +12,6 @@ uniform mat4 projection; void main() { //gl_Position = vec4(aPos, 1.0); - gl_Position = projection * view * model * vec4(aPos, 1.0); - - ourColor = aColor; - TexCoord = aTexCoord; + gl_Position = projection * view * model * vec4(aPos, 1.0f); + TexCoord = vec2(aTexCoord.x, aTexCoord.y); } \ No newline at end of file diff --git a/engine/camera.h b/engine/camera.h index 0357a4b..ce01cd1 100644 --- a/engine/camera.h +++ b/engine/camera.h @@ -2,11 +2,120 @@ #ifndef ENGINE2026_CAMERA_H #define ENGINE2026_CAMERA_H +#include +#include +#include + +// Movement keys/buttons +enum Camera_Movement { + FORWARD, + BACKWARD, + LEFT, + RIGHT +}; + +// Default camera values +const float YAW = -90.0f; +const float PITCH = 0.0f; +const float SPEED = 2.5f; +const float SENSITIVITY = 0.1f; +const float ZOOM = 45.0f; class Camera { public: - Camera(); + glm::vec3 position, front, up, right, worldUp; + float yaw, pitch; + float movementSpeed, mouseSensitivity, zoom; + + // constructor with vectors + Camera(glm::vec3 newPosition = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), float newYaw = YAW, float newPitch = PITCH) : front(glm::vec3(0.0f, 0.0f, -1.0f)), movementSpeed(SPEED), mouseSensitivity(SENSITIVITY), zoom(ZOOM) + { + position = newPosition; + worldUp = up; + yaw = newYaw; + pitch = newPitch; + updateCameraVectors(); + } + // constructor with scalar values + Camera(float posX, float posY, float posZ, float upX, float upY, float upZ, float newYaw, float newPitch) : front(glm::vec3(0.0f, 0.0f, -1.0f)), movementSpeed(SPEED), mouseSensitivity(SENSITIVITY), zoom(ZOOM) + { + position = glm::vec3(posX, posY, posZ); + worldUp = glm::vec3(upX, upY, upZ); + yaw = newYaw; + pitch = newPitch; + updateCameraVectors(); + } + + // returns the view matrix calculated using Euler Angles and the LookAt Matrix + glm::mat4 GetViewMatrix() + { + return glm::lookAt(position, position + front, up); + } + + // processes input received from any keyboard-like input system. Accepts input parameter in the form of camera defined ENUM (to abstract it from windowing systems) + void processKeyboard(Camera_Movement direction, float deltaTime) + { + float velocity = movementSpeed * deltaTime; + if (direction == FORWARD) + position += front * velocity; + if (direction == BACKWARD) + position -= front * velocity; + if (direction == LEFT) + position -= right * velocity; + if (direction == RIGHT) + position += right * velocity; + } + + // processes input received from a mouse input system. Expects the offset value in both the x and y direction. + void processMouseMovement(float xoffset, float yoffset, GLboolean constrainPitch = true) + { + xoffset *= mouseSensitivity; + yoffset *= mouseSensitivity; + + yaw += xoffset; + pitch += yoffset; + + // make sure that when pitch is out of bounds, screen doesn't get flipped + if (constrainPitch) + { + if (pitch > 89.0f) + pitch = 89.0f; + + if (pitch < -89.0f) + pitch = -89.0f; + } + + // update Front, Right and Up Vectors using the updated Euler angles + updateCameraVectors(); + } + + // processes input received from a mouse scroll-wheel event. Only requires input on the vertical wheel-axis + void processMouseScroll(float yoffset) + { + zoom -= (float)yoffset; + if (zoom < 1.0f) + zoom = 1.0f; + if (zoom > 45.0f) + zoom = 45.0f; + } + + +private: + // calculates the front vector from the Camera's (updated) Euler Angles + void updateCameraVectors() + { + // calculate the new Front vector + glm::vec3 newFront; + newFront.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch)); + newFront.y = sin(glm::radians(pitch)); + newFront.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch)); + front = glm::normalize(newFront); + + // also re-calculate the Right and Up vector + right = glm::normalize(glm::cross(front, worldUp)); // normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement. + up = glm::normalize(glm::cross(right, front)); + } }; #endif \ No newline at end of file diff --git a/engine/mesh.h b/engine/mesh.h index cd04227..348aa1c 100644 --- a/engine/mesh.h +++ b/engine/mesh.h @@ -15,7 +15,7 @@ class Mesh public: unsigned int VBO, VAO, EBO; std::size_t indexCount = 0; - + Mesh(const std::vector& vertices, const std::vector& indices) : indexCount(indices.size()) { if (vertices.empty() || indices.empty()) { @@ -55,7 +55,7 @@ public: std::cout << "Failed to load mesh" << std::endl; } } - + void Draw() const { glDrawArrays(GL_TRIANGLES, 0, 36); diff --git a/engine/shader.h b/engine/shader.h index 5a41a3d..55b6342 100644 --- a/engine/shader.h +++ b/engine/shader.h @@ -13,7 +13,7 @@ class Shader public: // Compiled Shader Program ID unsigned int ID; - + Shader(const char* vertexPath, const char* fragmentPath) { std::string vertexCode, fragmentCode; @@ -72,7 +72,7 @@ public: glDeleteShader(vertexShader); glDeleteShader(fragmentShader); } - + void Use() { glUseProgram(ID); diff --git a/engine/texture.h b/engine/texture.h index 3cbe40e..21b20fc 100644 --- a/engine/texture.h +++ b/engine/texture.h @@ -10,7 +10,7 @@ class Texture public: unsigned int ID; int width, height, nrChannels; - + Texture(const char* texturePath, bool flip = false) { // Create the Texture and set our ID diff --git a/main.cpp b/main.cpp index a13b384..536ae17 100644 --- a/main.cpp +++ b/main.cpp @@ -12,6 +12,12 @@ #include #include #include +#include + +void window_resize_callback(GLFWwindow* window, int width, int height); +void mouse_callback(GLFWwindow* window, double xpos, double ypos); +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); +void processInput(GLFWwindow *window); const int SCREEN_WIDTH = 1920; const int SCREEN_HEIGHT = 1080; @@ -19,34 +25,14 @@ const int SCREEN_HEIGHT = 1080; static bool drawWireframe = false; static bool wireframeHeld = false; -void window_resize_callback(GLFWwindow* window, int width, int height) -{ - glViewport(0, 0, width, height); -} +Camera camera(glm::vec3(0.0f, 0.0f, 3.0f)); +float lastX = SCREEN_WIDTH / 2.0f; +float lastY = SCREEN_HEIGHT / 2.0f; +bool firstMouse = true; -void processInput(GLFWwindow* window) -{ - // Close on Escape - if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) - glfwSetWindowShouldClose(window, true); - // Wireframe toggle - if (glfwGetKey(window, GLFW_KEY_F1) == GLFW_PRESS && !wireframeHeld) - { - wireframeHeld = true; - drawWireframe = !drawWireframe; - if (drawWireframe) - { - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - } - else - { - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - } - } - if (glfwGetKey(window, GLFW_KEY_F1) == GLFW_RELEASE) - wireframeHeld = false; -} +float deltaTime = 0.0f; // time between current frame and last frame +float lastFrame = 0.0f; int main(int argc, char* argv[]) { @@ -76,6 +62,16 @@ int main(int argc, char* argv[]) // Initialize Viewport & setup resize callback glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); glfwSetFramebufferSizeCallback(window, window_resize_callback); + glfwSetCursorPosCallback(window, mouse_callback); + glfwSetScrollCallback(window, scroll_callback); + + // Hide & Lock Cursor + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE); + + /* + Testing + */ // Load Shader Shader shaderTest("assets/shaders/basicVertex.vert", "assets/shaders/basicFragment.frag"); @@ -87,7 +83,7 @@ int main(int argc, char* argv[]) { -0.5f, 0.5f, -0.5f, 1.0f, 0.2f, 0.2f, 0.0f, 1.0f }, { -0.5f, -0.5f, -0.5f, 1.0f, 0.2f, 0.2f, 0.0f, 0.0f }, - // Back face (+z) – green-ish + // Back face (+z) – green-ish { -0.5f, -0.5f, 0.5f, 0.2f, 1.0f, 0.2f, 0.0f, 0.0f }, { 0.5f, -0.5f, 0.5f, 0.2f, 1.0f, 0.2f, 1.0f, 0.0f }, { 0.5f, 0.5f, 0.5f, 0.2f, 1.0f, 0.2f, 1.0f, 1.0f }, @@ -95,7 +91,7 @@ int main(int argc, char* argv[]) { -0.5f, 0.5f, 0.5f, 0.2f, 1.0f, 0.2f, 0.0f, 1.0f }, { -0.5f, -0.5f, 0.5f, 0.2f, 1.0f, 0.2f, 0.0f, 0.0f }, - // Left face (-x) – blue-ish + // Left face (-x) – blue-ish { -0.5f, 0.5f, 0.5f, 0.2f, 0.2f, 1.0f, 1.0f, 0.0f }, { -0.5f, 0.5f, -0.5f, 0.2f, 0.2f, 1.0f, 1.0f, 1.0f }, { -0.5f, -0.5f, -0.5f, 0.2f, 0.2f, 1.0f, 0.0f, 1.0f }, @@ -103,7 +99,7 @@ int main(int argc, char* argv[]) { -0.5f, -0.5f, 0.5f, 0.2f, 0.2f, 1.0f, 0.0f, 0.0f }, { -0.5f, 0.5f, 0.5f, 0.2f, 0.2f, 1.0f, 1.0f, 0.0f }, - // Right face (+x) – yellow-ish + // Right face (+x) – yellow-ish { 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 0.2f, 1.0f, 0.0f }, { 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.2f, 1.0f, 1.0f }, { 0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 0.2f, 0.0f, 1.0f }, @@ -111,7 +107,7 @@ int main(int argc, char* argv[]) { 0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 0.2f, 0.0f, 0.0f }, { 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 0.2f, 1.0f, 0.0f }, - // Bottom face (-y) – magenta-ish + // Bottom face (-y) – magenta-ish { -0.5f, -0.5f, -0.5f, 1.0f, 0.2f, 1.0f, 0.0f, 1.0f }, { 0.5f, -0.5f, -0.5f, 1.0f, 0.2f, 1.0f, 1.0f, 1.0f }, { 0.5f, -0.5f, 0.5f, 1.0f, 0.2f, 1.0f, 1.0f, 0.0f }, @@ -119,7 +115,7 @@ int main(int argc, char* argv[]) { -0.5f, -0.5f, 0.5f, 1.0f, 0.2f, 1.0f, 0.0f, 0.0f }, { -0.5f, -0.5f, -0.5f, 1.0f, 0.2f, 1.0f, 0.0f, 1.0f }, - // Top face (+y) – cyan-ish + // Top face (+y) – cyan-ish { -0.5f, 0.5f, -0.5f, 0.2f, 1.0f, 1.0f, 0.0f, 1.0f }, { 0.5f, 0.5f, -0.5f, 0.2f, 1.0f, 1.0f, 1.0f, 1.0f }, { 0.5f, 0.5f, 0.5f, 0.2f, 1.0f, 1.0f, 1.0f, 0.0f }, @@ -154,20 +150,21 @@ int main(int argc, char* argv[]) glm::vec3(-1.3f, 1.0f, -1.5f) }; + // Render Loop - double lastTime = 0.0; - int nbFrames = 0; + float lastUpdate = 0; while (!glfwWindowShouldClose(window)) { - // Calculate FPS - double currentTime = glfwGetTime(); - nbFrames++; - if (currentTime - lastTime >= 1.0) { // Update every second - double fps = nbFrames / (currentTime - lastTime); - std::cout << fps << std::endl; + // per-frame time logic + // -------------------- + float currentFrame = static_cast(glfwGetTime()); + deltaTime = currentFrame - lastFrame; + lastFrame = currentFrame; - nbFrames = 0; - lastTime += 1.0; + // crappy fps printout + if (currentFrame - lastUpdate >= 1.0) { + std::cout << 1.0f / deltaTime << std::endl; + lastUpdate += 1.0f; } // Input processing @@ -185,21 +182,14 @@ int main(int argc, char* argv[]) shaderTest.Use(); - // Test - glm::mat4 model = glm::mat4(1.0f); - model = glm::rotate(model, (float)glfwGetTime() * glm::radians(50.0f), glm::vec3(0.5f, 1.0f, 0.0f)); - - glm::mat4 view = glm::mat4(1.0f); - // note that we're translating the scene in the reverse direction of where we want to move - view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f)); - - glm::mat4 projection; - projection = glm::perspective(glm::radians(45.0f), 800.0f / 600.0f, 0.1f, 100.0f); - - shaderTest.setMat4("model", model); - shaderTest.setMat4("view", view); + // pass projection matrix to shader (note that in this case it could change every frame) + glm::mat4 projection = glm::perspective(glm::radians(camera.zoom), (float)SCREEN_WIDTH / (float)SCREEN_HEIGHT, 0.1f, 100.0f); shaderTest.setMat4("projection", projection); + // camera/view transformation + glm::mat4 view = camera.GetViewMatrix(); + shaderTest.setMat4("view", view); + //testMesh.Draw(); @@ -224,3 +214,74 @@ int main(int argc, char* argv[]) glfwTerminate(); return 0; } + + +void window_resize_callback(GLFWwindow* window, int width, int height) +{ + glViewport(0, 0, width, height); +} + +void processInput(GLFWwindow* window) +{ + // Close on Escape + if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) + glfwSetWindowShouldClose(window, true); + + // Wireframe toggle + if (glfwGetKey(window, GLFW_KEY_F1) == GLFW_PRESS && !wireframeHeld) + { + wireframeHeld = true; + drawWireframe = !drawWireframe; + if (drawWireframe) + { + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + } + else + { + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } + } + if (glfwGetKey(window, GLFW_KEY_F1) == GLFW_RELEASE) + wireframeHeld = false; + + // Camera + if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) + camera.processKeyboard(FORWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) + camera.processKeyboard(BACKWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) + camera.processKeyboard(LEFT, deltaTime); + if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) + camera.processKeyboard(RIGHT, deltaTime); +} + + +// glfw: whenever the mouse moves, this callback is called +// ------------------------------------------------------- +void mouse_callback(GLFWwindow* window, double xposIn, double yposIn) +{ + float xpos = static_cast(xposIn); + float ypos = static_cast(yposIn); + + if (firstMouse) + { + lastX = xpos; + lastY = ypos; + firstMouse = false; + } + + float xoffset = xpos - lastX; + float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top + + lastX = xpos; + lastY = ypos; + + camera.processMouseMovement(xoffset, yoffset); +} + +// glfw: whenever the mouse scroll wheel scrolls, this callback is called +// ---------------------------------------------------------------------- +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) +{ + camera.processMouseScroll(static_cast(yoffset)); +} \ No newline at end of file