From 2486d7d24377c9093bf23792ba9b41f6a5e43620 Mon Sep 17 00:00:00 2001 From: Gabe Ambrosio Date: Mon, 9 May 2022 20:01:23 -0500 Subject: [PATCH 1/2] Episode 5: Player Controller --- MinecraftYoutube/include/challenges/Cubes.h | 5 +- MinecraftYoutube/include/core.h | 2 + MinecraftYoutube/include/core/Application.h | 20 ++ MinecraftYoutube/include/core/Input.h | 3 + MinecraftYoutube/include/core/Window.h | 2 + .../include/gameplay/CharacterController.h | 29 +++ .../include/gameplay/CharacterSystem.h | 16 ++ .../include/gameplay/PlayerController.h | 14 ++ MinecraftYoutube/include/renderer/Camera.h | 34 ++++ MinecraftYoutube/src/challenges/Cubes.cpp | 28 +-- MinecraftYoutube/src/core/Application.cpp | 175 ++++++++++++++++++ MinecraftYoutube/src/core/Input.cpp | 27 +++ MinecraftYoutube/src/core/Window.cpp | 1 + .../src/gameplay/CharacterSystem.cpp | 101 ++++++++++ .../src/gameplay/PlayerController.cpp | 32 ++++ MinecraftYoutube/src/main.cpp | 88 +-------- MinecraftYoutube/src/renderer/Camera.cpp | 31 ++++ 17 files changed, 502 insertions(+), 106 deletions(-) create mode 100644 MinecraftYoutube/include/core/Application.h create mode 100644 MinecraftYoutube/include/gameplay/CharacterController.h create mode 100644 MinecraftYoutube/include/gameplay/CharacterSystem.h create mode 100644 MinecraftYoutube/include/gameplay/PlayerController.h create mode 100644 MinecraftYoutube/include/renderer/Camera.h create mode 100644 MinecraftYoutube/src/core/Application.cpp create mode 100644 MinecraftYoutube/src/gameplay/CharacterSystem.cpp create mode 100644 MinecraftYoutube/src/gameplay/PlayerController.cpp create mode 100644 MinecraftYoutube/src/renderer/Camera.cpp diff --git a/MinecraftYoutube/include/challenges/Cubes.h b/MinecraftYoutube/include/challenges/Cubes.h index 5cf9570..01a305d 100644 --- a/MinecraftYoutube/include/challenges/Cubes.h +++ b/MinecraftYoutube/include/challenges/Cubes.h @@ -4,13 +4,14 @@ namespace MinecraftClone { struct Window; + struct Camera; namespace Cubes { - void init(const Window& window); + void init(); void destroy(); - void update(float dt); + void update(const Camera& camera); } } diff --git a/MinecraftYoutube/include/core.h b/MinecraftYoutube/include/core.h index feed159..6d6010a 100644 --- a/MinecraftYoutube/include/core.h +++ b/MinecraftYoutube/include/core.h @@ -31,6 +31,8 @@ #include #include #include +#include +#include // Use this for hash map and hash sets instead of the crappy std lib #include diff --git a/MinecraftYoutube/include/core/Application.h b/MinecraftYoutube/include/core/Application.h new file mode 100644 index 0000000..8ff7ff6 --- /dev/null +++ b/MinecraftYoutube/include/core/Application.h @@ -0,0 +1,20 @@ +#ifndef MINECRAFT_CLONE_APPLICATION_H +#define MINECRAFT_CLONE_APPLICATION_H + +namespace MinecraftClone +{ + struct Window; + + namespace Application + { + bool init(); + void run(); + void free(); + + const Window& getWindow(); + + extern float deltaTime; + } +} + +#endif \ No newline at end of file diff --git a/MinecraftYoutube/include/core/Input.h b/MinecraftYoutube/include/core/Input.h index 1749159..12eca96 100644 --- a/MinecraftYoutube/include/core/Input.h +++ b/MinecraftYoutube/include/core/Input.h @@ -12,11 +12,14 @@ namespace MinecraftClone extern float mouseY; extern float mouseScrollX; extern float mouseScrollY; + extern float deltaMouseX; + extern float deltaMouseY; void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); void mouseCallback(GLFWwindow* window, double xpos, double ypos); void mouseButtonCallback(GLFWwindow* window, int button, int action, int mods); void mouseScrollCallback(GLFWwindow* window, double xoffset, double yoffset); + void endFrame(); bool isKeyDown(int key); bool isMouseButtonDown(int mouseButton); diff --git a/MinecraftYoutube/include/core/Window.h b/MinecraftYoutube/include/core/Window.h index d8916ca..403c89a 100644 --- a/MinecraftYoutube/include/core/Window.h +++ b/MinecraftYoutube/include/core/Window.h @@ -13,6 +13,8 @@ namespace MinecraftClone void installMainCallbacks(); void close(); + + inline float getAspectRatio() const { return (float)windowWidth / (float)windowHeight; } static Window* createWindow(int width, int height, const char* title, bool fullScreenMode=false); static void freeWindow(Window* window); diff --git a/MinecraftYoutube/include/gameplay/CharacterController.h b/MinecraftYoutube/include/gameplay/CharacterController.h new file mode 100644 index 0000000..bc8dc6b --- /dev/null +++ b/MinecraftYoutube/include/gameplay/CharacterController.h @@ -0,0 +1,29 @@ +#ifndef MINECRAFT_CLONE_CHARACTER_CONTROLLER_H +#define MINECRAFT_CLONE_CHARACTER_CONTROLLER_H +#include "core.h" + +namespace MinecraftClone +{ + struct CharacterController + { + float controllerBaseSpeed; + float controllerRunSpeed; + float movementSensitivity; + + glm::vec3 movementAxis; + glm::vec3 viewAxis; + glm::vec3 _targetViewAxis; + bool isRunning; + bool lockedToCamera; + }; + + // TODO: Move this into physics library once it gets created in the physics episode + struct Rigidbody + { + glm::vec3 velocity; + glm::vec3 acceleration; + float friction; + }; +} + +#endif \ No newline at end of file diff --git a/MinecraftYoutube/include/gameplay/CharacterSystem.h b/MinecraftYoutube/include/gameplay/CharacterSystem.h new file mode 100644 index 0000000..ed32f71 --- /dev/null +++ b/MinecraftYoutube/include/gameplay/CharacterSystem.h @@ -0,0 +1,16 @@ +#ifndef MINECRAFT_CLONE_CHARACTER_SYSTEM_H +#define MINECRAFT_CLONE_CHARACTER_SYSTEM_H + +namespace MinecraftClone +{ + struct Camera; + struct CharacterController; + struct Rigidbody; + + namespace CharacterSystem + { + void update(CharacterController& controller, Camera& camera, Rigidbody& rb); + } +} + +#endif \ No newline at end of file diff --git a/MinecraftYoutube/include/gameplay/PlayerController.h b/MinecraftYoutube/include/gameplay/PlayerController.h new file mode 100644 index 0000000..ef26a0f --- /dev/null +++ b/MinecraftYoutube/include/gameplay/PlayerController.h @@ -0,0 +1,14 @@ +#ifndef MINECRAFT_CLONE_PLAYER_CONTROLLER_H +#define MINECRAFT_CLONE_PLAYER_CONTROLLER_H + +namespace MinecraftClone +{ + struct CharacterController; + + namespace PlayerController + { + void update(CharacterController& controller); + } +} + +#endif \ No newline at end of file diff --git a/MinecraftYoutube/include/renderer/Camera.h b/MinecraftYoutube/include/renderer/Camera.h new file mode 100644 index 0000000..8a94d98 --- /dev/null +++ b/MinecraftYoutube/include/renderer/Camera.h @@ -0,0 +1,34 @@ +#ifndef MINECRAFT_CLONE_CAMERA_H +#define MINECRAFT_CLONE_CAMERA_H +#include "core.h" + +namespace MinecraftClone +{ + struct Camera + { + // TODO: Replace this with an entity representing the + // camera that holds a Transform containing a position + glm::vec3 position; + glm::quat orientation; + // NOTE: Techinally the forward, right and up vectors + // can be obtained from orientation. However, we can + // cache them to avoid recomputing an entire matrix + // every time we need a directional vector. + glm::vec3 forward; + glm::vec3 right; + glm::vec3 up; + glm::mat4 projection; + glm::mat4 view; + float fov; + + // This just calculates the view and then + // returns the member variable view + const glm::mat4& calculateViewMatrix(); + + // This just calculates projection and then + // returns member variable projection + const glm::mat4& calculateProjectionMatrix(); + }; +} + +#endif \ No newline at end of file diff --git a/MinecraftYoutube/src/challenges/Cubes.cpp b/MinecraftYoutube/src/challenges/Cubes.cpp index 043ad4f..21826d1 100644 --- a/MinecraftYoutube/src/challenges/Cubes.cpp +++ b/MinecraftYoutube/src/challenges/Cubes.cpp @@ -2,7 +2,9 @@ #include "core.h" #include "core/Input.h" #include "core/Window.h" +#include "core/Application.h" #include "renderer/ShaderProgram.h" +#include "renderer/Camera.h" namespace MinecraftClone { @@ -98,8 +100,6 @@ namespace MinecraftClone static std::vector cubePositions; static std::vector cubeTextures; - static glm::mat4 projection; - void createDefaultCube() { // Define the 8 unique positions for a cube according to the diagram above @@ -243,7 +243,7 @@ namespace MinecraftClone glDeleteTextures(1, &texture.textureId); } - void init(const Window& window) + void init() { if (!texturedCubeShader.compileAndLink("assets/shaders/vertex/cube.glsl", "assets/shaders/fragment/cube.glsl")) { @@ -253,12 +253,6 @@ namespace MinecraftClone createDefaultCube(); - float windowAspect = ((float)window.windowWidth / (float)window.windowHeight); - float fov = 70.0f; - float zNear = 0.1f; - float zFar = 10'000.0f; - projection = glm::perspective(fov, windowAspect, zNear, zFar); - for (auto& filepath : std::filesystem::directory_iterator("assets/images")) { if (filepath.path().stem().string() != "normal") @@ -299,22 +293,12 @@ namespace MinecraftClone } } - void update(float dt) + void update(const Camera& camera) { texturedCubeShader.bind(); - // Rotate the eye a little bit every frame - static glm::vec3 eye = glm::vec3(); - static float eyeRotation = 45.0f; - if (!Input::isKeyDown(GLFW_KEY_SPACE)) - eyeRotation += 30.0f * dt; - eye = glm::vec3(glm::sin(glm::radians(eyeRotation)) * 7.0f, 5.0f, glm::cos(glm::radians(eyeRotation)) * 7.0f); - - glm::vec3 center = glm::vec3(0.0f, 0.0f, 0.0f); - glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f); - glm::mat4 view = glm::lookAt(eye, center, up); - texturedCubeShader.uploadMat4("uView", view); - texturedCubeShader.uploadMat4("uProjection", projection); + texturedCubeShader.uploadMat4("uView", camera.view); + texturedCubeShader.uploadMat4("uProjection", camera.projection); // NOTE: This texture is not required for the challenges glActiveTexture(GL_TEXTURE0 + 1); diff --git a/MinecraftYoutube/src/core/Application.cpp b/MinecraftYoutube/src/core/Application.cpp new file mode 100644 index 0000000..2a08190 --- /dev/null +++ b/MinecraftYoutube/src/core/Application.cpp @@ -0,0 +1,175 @@ +#include "core/Application.h" +#include "core.h" +#include "core/Window.h" +#include "core/Input.h" + +// TODO: Temporary includes +#include "challenges/Cubes.h" +#include "gameplay/CharacterController.h" +#include "gameplay/CharacterSystem.h" +#include "gameplay/PlayerController.h" +#include "renderer/Camera.h" + +namespace MinecraftClone +{ + namespace Application + { + // Global variables + // Initialize this to 60FPS so we don't start the first frame with any + // division by 0 errors or something + float deltaTime = 1.0f / 60.0f; + + // Internal variables + static Window* window = nullptr; + static constexpr int windowWidth = 1920; + static constexpr int windowHeight = 1080; + static const char* windowTitle = "Minecraft Clone"; + + // Internal Functions + void GLAPIENTRY errorMessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam); + void glfwErrorCallback(int error, const char* description); + + bool init() + { + glfwSetErrorCallback(glfwErrorCallback); + if (!glfwInit()) + { + g_logger_error("GLFW failed to initialize"); + return false; + } + + window = Window::createWindow(windowWidth, windowHeight, windowTitle); + if (window == nullptr) + { + g_logger_error("Failed to create GLFW window"); + glfwTerminate(); + return false; + } + window->installMainCallbacks(); + + if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) + { + g_logger_error("Failed to initialize GLAD."); + glfwTerminate(); + return false; + } + + // During init, enable debug output + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(errorMessageCallback, 0); + + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + + glViewport(0, 0, windowWidth, windowHeight); + + // Set the window cursor to be locked + glfwSetInputMode((GLFWwindow*)window->nativeWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + + // Initialize our game stuff now + Cubes::init(); + + return true; + } + + void run() + { + float frameStart = 0.0f; + + Camera camera = {}; + // We need to initialize the orientation of the camera + glm::vec3 forward = glm::normalize(glm::vec3(1.0f, 0.0f, 0.0f)); + glm::vec3 right = glm::cross(glm::vec3(0.0f, 1.0f, 0.0f), forward); + glm::vec3 up = glm::cross(forward, right); + glm::mat4 rotMatrix = glm::transpose(glm::mat4( + glm::vec4(right, 0.0f), + glm::vec4(up, 0.0f), + glm::vec4(forward, 0.0f), + glm::vec4(0.0f, 0.0f, 0.0f, 1.0f) + )); + camera.orientation = glm::toQuat(rotMatrix); + // The camera projection matrix should only need to be calculated once + // unless the FOV or aspect ratio changes. I'll ignore those for now. + camera.fov = 70.0f; + camera.calculateProjectionMatrix(); + + CharacterController controller = {}; + controller.controllerBaseSpeed = 66.0f; + controller.controllerRunSpeed = controller.controllerBaseSpeed * 2.0f; + controller.movementSensitivity = 0.1f; + + Rigidbody rb = {}; + rb.friction = 0.1f; + + while (!glfwWindowShouldClose(window->nativeWindow)) + { + deltaTime = (float)(glfwGetTime() - (double)frameStart); + frameStart = (float)glfwGetTime(); + + glClearColor(39.0f / 255.0f, 40.0f / 255.0f, 34.0f / 255.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Update the player first, so that we get the input settled + PlayerController::update(controller); + // Then update the player data using the inputs we got + CharacterSystem::update(controller, camera, rb); + // Finally calculate our new view matrix and draw our scene + camera.calculateViewMatrix(); + Cubes::update(camera); + + if (Input::isKeyDown(GLFW_KEY_ESCAPE)) + { + glfwSetInputMode((GLFWwindow*)window->nativeWindow, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + } + else if (Input::isKeyDown(GLFW_KEY_ENTER)) + { + glfwSetInputMode((GLFWwindow*)window->nativeWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + } + + glfwSwapBuffers(window->nativeWindow); + + Input::endFrame(); + glfwPollEvents(); + } + } + + void free() + { + // Try to destroy things in the reverse order of initialization + // to minimize any weird dependency issues during de-initialization + Cubes::destroy(); + + Window::freeWindow(window); + window = nullptr; + + glfwTerminate(); + } + + const Window& getWindow() + { + return *window; + } + + // ------------ Internal Functions ------------ + void GLAPIENTRY errorMessageCallback( + GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam) + { + if (type == GL_DEBUG_TYPE_ERROR) + { + g_logger_error("GL CALLBACK: **GL ERROR** type = 0x%x, severity = 0x%x, message = %s\n", + type, severity, message); + } + } + + void glfwErrorCallback(int error, const char* description) + { + g_logger_error("GLFW CALLBACK: **GLFW ERROR** Error: %d description: %s\n", error, description); + } + } +} diff --git a/MinecraftYoutube/src/core/Input.cpp b/MinecraftYoutube/src/core/Input.cpp index 7d3f081..c18daa4 100644 --- a/MinecraftYoutube/src/core/Input.cpp +++ b/MinecraftYoutube/src/core/Input.cpp @@ -4,12 +4,20 @@ namespace MinecraftClone { namespace Input { + // Global Variables bool keyPressedData[GLFW_KEY_LAST] = {}; bool mouseButtonPressedData[GLFW_MOUSE_BUTTON_LAST] = {}; float mouseX = 0.0f; float mouseY = 0.0f; float mouseScrollX = 0.0f; float mouseScrollY = 0.0f; + float deltaMouseX = 0.0f; + float deltaMouseY = 0.0f; + + // Internal Variables + static float lastMouseX = 0.0f; + static float lastMouseY = 0.0f; + static bool firstFrame = true; void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) { @@ -23,6 +31,19 @@ namespace MinecraftClone { mouseX = (float)xpos; mouseY = (float)ypos; + if (firstFrame) + { + // Set these to the mouse position the first frame so that + // we don't get large erratic values the first frame + lastMouseX = (float)xpos; + lastMouseY = (float)ypos; + firstFrame = false; + } + + deltaMouseX = (float)xpos - lastMouseX; + deltaMouseY = lastMouseY - (float)ypos; + lastMouseX = (float)xpos; + lastMouseY = (float)ypos; } void mouseButtonCallback(GLFWwindow* window, int button, int action, int mods) @@ -39,6 +60,12 @@ namespace MinecraftClone mouseScrollY = (float)yoffset; } + void endFrame() + { + deltaMouseX = 0.0f; + deltaMouseY = 0.0f; + } + bool isKeyDown(int key) { if (key >= 0 && key < GLFW_KEY_LAST) diff --git a/MinecraftYoutube/src/core/Window.cpp b/MinecraftYoutube/src/core/Window.cpp index 5bcb2e8..ea57824 100644 --- a/MinecraftYoutube/src/core/Window.cpp +++ b/MinecraftYoutube/src/core/Window.cpp @@ -48,6 +48,7 @@ namespace MinecraftClone } glfwMakeContextCurrent(res->nativeWindow); glfwSetWindowUserPointer(res->nativeWindow, (void*)res); + glfwSwapInterval(1); res->windowWidth = width; res->windowHeight = height; diff --git a/MinecraftYoutube/src/gameplay/CharacterSystem.cpp b/MinecraftYoutube/src/gameplay/CharacterSystem.cpp new file mode 100644 index 0000000..dd3b0c6 --- /dev/null +++ b/MinecraftYoutube/src/gameplay/CharacterSystem.cpp @@ -0,0 +1,101 @@ +#include "gameplay/CharacterSystem.h" +#include "gameplay/CharacterController.h" +//#include "gameplay/PlayerController.h" +#include "renderer/Camera.h" +#include "core/Application.h" +#include "core/Input.h" + +namespace MinecraftClone +{ + namespace CharacterSystem + { + // Internal Variables + static float terminalVelocity = 8.0f; + + void update(CharacterController& controller, Camera& camera, Rigidbody& rb) + { + float speed = controller.controllerBaseSpeed; + if (controller.isRunning) + { + speed = controller.controllerRunSpeed; + } + + rb.acceleration.x = camera.forward.x * controller.movementAxis.x; + rb.acceleration.y = camera.forward.y * controller.movementAxis.x; + rb.acceleration.z = camera.forward.z * controller.movementAxis.x; + rb.acceleration.x += camera.right.x * controller.movementAxis.z; + rb.acceleration.y += camera.right.y * controller.movementAxis.z; + rb.acceleration.z += camera.right.z * controller.movementAxis.z; + // The y-axis just changes the player's global y position + // instead of using their local up axis + rb.acceleration.y += controller.movementAxis.y; + + if (glm::abs(rb.acceleration.x) > 0 || glm::abs(rb.acceleration.z) > 0 || (glm::abs(rb.acceleration.y) > 0)) + { + float denominator = glm::inversesqrt(rb.acceleration.x * rb.acceleration.x + rb.acceleration.z * rb.acceleration.z); + if (glm::abs(rb.acceleration.y) > 0) + { + denominator = glm::inversesqrt(rb.acceleration.x * rb.acceleration.x + rb.acceleration.z * rb.acceleration.z + rb.acceleration.y * rb.acceleration.y); + rb.acceleration.y *= denominator * speed; + } + rb.acceleration.x *= denominator * speed; + rb.acceleration.z *= denominator * speed; + } + + rb.velocity.x += rb.acceleration.x * Application::deltaTime; + rb.velocity.y += rb.acceleration.y * Application::deltaTime; + rb.velocity.z += rb.acceleration.z * Application::deltaTime; + + camera.position.x += rb.velocity.x * Application::deltaTime; + camera.position.y += rb.velocity.y * Application::deltaTime; + camera.position.z += rb.velocity.z * Application::deltaTime; + + // If acceleration is 0, apply friction to decelerate the object + if (glm::all(glm::epsilonEqual(rb.acceleration, glm::vec3(0.0f), 0.001f))) + { + rb.velocity.x *= rb.friction * Application::deltaTime; + rb.velocity.z *= rb.friction * Application::deltaTime; + rb.velocity.y *= rb.friction * Application::deltaTime; + } + + if (glm::length2(rb.velocity) >= terminalVelocity) + { + rb.velocity = glm::normalize(rb.velocity) * terminalVelocity; + } + + // Snap the velocity to 0 when it gets close enough + if (glm::all(glm::epsilonEqual(rb.velocity, glm::vec3(0.0f), 0.001f))) + { + rb.velocity = glm::vec3(0.0f); + } + + // Smooth the mouse movements over time + // Exponential decay function (https://en.wikipedia.org/wiki/Exponential_decay) + //constexpr float springiness = 50.0f; + //float delta = 1.0f - glm::exp(glm::log(0.5f) * Application::deltaTime * springiness); + //float newX = (controller.viewAxis.x - controller._targetViewAxis.x) * delta * Application::deltaTime * controller.movementSensitivity; + //float newY = (controller.viewAxis.y - controller._targetViewAxis.y) * delta * Application::deltaTime * controller.movementSensitivity; + //controller.viewAxis.x = newX; + //controller.viewAxis.y = newY; + //controller._targetViewAxis = controller.viewAxis; + controller.viewAxis *= controller.movementSensitivity; + + // Check out this stack overflow for a deeper explanation of + // why this is necessary + // https://gamedev.stackexchange.com/questions/136174/im-rotating-an-object-on-two-axes-so-why-does-it-keep-twisting-around-the-thir + if (controller.viewAxis.x != 0.0f) + { + // Yaw globally + glm::quat yaw = glm::quat(glm::vec3(0.0f, glm::radians(controller.viewAxis.x), 0.0f)); + camera.orientation = camera.orientation * yaw; + } + + if (controller.viewAxis.y != 0.0f) + { + // Pitch locally (this effects order of operation with quaternion multiplication) + glm::quat pitch = glm::quat(glm::vec3(glm::radians(-controller.viewAxis.y), 0.0f, 0.0f)); + camera.orientation = pitch * camera.orientation; + } + } + } +} \ No newline at end of file diff --git a/MinecraftYoutube/src/gameplay/PlayerController.cpp b/MinecraftYoutube/src/gameplay/PlayerController.cpp new file mode 100644 index 0000000..d0651da --- /dev/null +++ b/MinecraftYoutube/src/gameplay/PlayerController.cpp @@ -0,0 +1,32 @@ +#include "gameplay/PlayerController.h" +#include "gameplay/CharacterController.h" +#include "core.h" +#include "core/Input.h" + +namespace MinecraftClone +{ + namespace PlayerController + { + void update(CharacterController& controller) + { + controller.movementAxis.x = Input::isKeyDown(GLFW_KEY_W) + ? -1.0f + : Input::isKeyDown(GLFW_KEY_S) + ? 1.0f + : 0.0f; + controller.movementAxis.z = Input::isKeyDown(GLFW_KEY_D) + ? 1.0f + : Input::isKeyDown(GLFW_KEY_A) + ? -1.0f + : 0.0f; + controller.movementAxis.y = Input::isKeyDown(GLFW_KEY_SPACE) + ? 1.0f + : Input::isKeyDown(GLFW_KEY_LEFT_SHIFT) + ? -1.0f + : 0.0f; + + controller.viewAxis.x = Input::deltaMouseX; + controller.viewAxis.y = Input::deltaMouseY; + } + } +} \ No newline at end of file diff --git a/MinecraftYoutube/src/main.cpp b/MinecraftYoutube/src/main.cpp index 4b926c2..57ecd66 100644 --- a/MinecraftYoutube/src/main.cpp +++ b/MinecraftYoutube/src/main.cpp @@ -1,95 +1,19 @@ #include "core.h" -#include "core/Window.h" -#include "core/Input.h" -#include "challenges/Cubes.h" -#include "renderer/ShaderProgram.h" +#include "core/Application.h" using namespace MinecraftClone; -const int windowWidth = 1920; -const int windowHeight = 1080; -const char* windowTitle = "OpenGL Template"; - -void GLAPIENTRY errorMessageCallback( - GLenum source, - GLenum type, - GLuint id, - GLenum severity, - GLsizei length, - const GLchar* message, - const void* userParam) -{ - if (type == GL_DEBUG_TYPE_ERROR) - { - g_logger_error("GL CALLBACK: **GL ERROR** type = 0x%x, severity = 0x%x, message = %s\n", - type, severity, message); - } -} - -void glfwErrorCallback(int error, const char* description) -{ - g_logger_error("GLFW CALLBACK: **GLFW ERROR** Error: %d description: %s\n", error, description); -} - int main() { - glfwSetErrorCallback(glfwErrorCallback); - if(!glfwInit()){ - g_logger_error("GLFW failed to initialize"); - return -1; - } - - Window* window = Window::createWindow(windowWidth, windowHeight, windowTitle); - if (window == nullptr) + // Try to initialize the app, if it fails exit early + if (!Application::init()) { - g_logger_error("Failed to create GLFW window"); - glfwTerminate(); + g_logger_error("Failed to initialize the game. See the logs above for more details."); return -1; } - window->installMainCallbacks(); - - if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) - { - g_logger_error("Failed to initialize GLAD."); - glfwTerminate(); - return -1; - } - - // During init, enable debug output - glEnable(GL_DEBUG_OUTPUT); - glDebugMessageCallback(errorMessageCallback, 0); - - glEnable(GL_DEPTH_TEST); - glEnable(GL_CULL_FACE); - - glViewport(0, 0, windowWidth, windowHeight); - - float dt = 0.016f; - float frameStart = 0.0f; - - Cubes::init(*window); - - bool showPlayer = false; - bool showTerrain = false; - float keyDebounce = 0.1f; - while (!glfwWindowShouldClose(window->nativeWindow)) - { - dt = glfwGetTime() - frameStart; - frameStart = glfwGetTime(); - - glClearColor(39.0f/255.0f, 40.0f/255.0f, 34.0f/255.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - keyDebounce -= dt; - - Cubes::update(dt); - - glfwSwapBuffers(window->nativeWindow); - glfwPollEvents(); - } - Cubes::destroy(); + Application::run(); + Application::free(); - Window::freeWindow(window); - glfwTerminate(); return 0; } \ No newline at end of file diff --git a/MinecraftYoutube/src/renderer/Camera.cpp b/MinecraftYoutube/src/renderer/Camera.cpp new file mode 100644 index 0000000..97c8ff6 --- /dev/null +++ b/MinecraftYoutube/src/renderer/Camera.cpp @@ -0,0 +1,31 @@ +#include "renderer/Camera.h" +#include "core/Application.h" +#include "core/Window.h" + +namespace MinecraftClone +{ + const glm::mat4& Camera::calculateViewMatrix() + { + glm::mat4 rotationMatrix = glm::toMat4(orientation); + right = glm::row(rotationMatrix, 0); + up = glm::row(rotationMatrix, 1); + glm::vec3 oldForward = forward; + forward = glm::row(rotationMatrix, 2); + + glm::mat4 translationMatrix = glm::transpose(glm::mat4( + glm::vec4(1.0f, 0.0f, 0.0f, -position.x), + glm::vec4(0.0f, 1.0f, 0.0f, -position.y), + glm::vec4(0.0f, 0.0f, 1.0f, -position.z), + glm::vec4(0.0f, 0.0f, 0.0f, 1.0f) + )); + + view = rotationMatrix * translationMatrix; + return view; + } + + const glm::mat4& Camera::calculateProjectionMatrix() + { + projection = glm::perspective(fov, Application::getWindow().getAspectRatio(), 0.1f, 100.0f); + return projection; + } +} \ No newline at end of file From d8edc23ae8cbac6ee92427291c30d485b87f6b33 Mon Sep 17 00:00:00 2001 From: Gabe Ambrosio Date: Sat, 14 May 2022 16:26:38 -0500 Subject: [PATCH 2/2] Finalize the code --- .../include/gameplay/CharacterController.h | 1 - MinecraftYoutube/src/core/Application.cpp | 2 +- MinecraftYoutube/src/core/Input.cpp | 22 ++++++++- .../src/gameplay/CharacterSystem.cpp | 48 +++++-------------- .../src/gameplay/PlayerController.cpp | 2 + MinecraftYoutube/src/renderer/Camera.cpp | 1 - 6 files changed, 36 insertions(+), 40 deletions(-) diff --git a/MinecraftYoutube/include/gameplay/CharacterController.h b/MinecraftYoutube/include/gameplay/CharacterController.h index bc8dc6b..e05fe47 100644 --- a/MinecraftYoutube/include/gameplay/CharacterController.h +++ b/MinecraftYoutube/include/gameplay/CharacterController.h @@ -12,7 +12,6 @@ namespace MinecraftClone glm::vec3 movementAxis; glm::vec3 viewAxis; - glm::vec3 _targetViewAxis; bool isRunning; bool lockedToCamera; }; diff --git a/MinecraftYoutube/src/core/Application.cpp b/MinecraftYoutube/src/core/Application.cpp index 2a08190..7cb24f0 100644 --- a/MinecraftYoutube/src/core/Application.cpp +++ b/MinecraftYoutube/src/core/Application.cpp @@ -105,6 +105,7 @@ namespace MinecraftClone { deltaTime = (float)(glfwGetTime() - (double)frameStart); frameStart = (float)glfwGetTime(); + glfwPollEvents(); glClearColor(39.0f / 255.0f, 40.0f / 255.0f, 34.0f / 255.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -129,7 +130,6 @@ namespace MinecraftClone glfwSwapBuffers(window->nativeWindow); Input::endFrame(); - glfwPollEvents(); } } diff --git a/MinecraftYoutube/src/core/Input.cpp b/MinecraftYoutube/src/core/Input.cpp index c18daa4..5c92443 100644 --- a/MinecraftYoutube/src/core/Input.cpp +++ b/MinecraftYoutube/src/core/Input.cpp @@ -40,8 +40,26 @@ namespace MinecraftClone firstFrame = false; } - deltaMouseX = (float)xpos - lastMouseX; - deltaMouseY = lastMouseY - (float)ypos; + // NOTE: This stuff is just to prevent the camera from jumping if the mouse cursor + // moves off the window and then back onto the window at a different location + int windowWidth; + int windowHeight; + glfwGetWindowSize(window, &windowWidth, &windowHeight); + bool cursorLocked = glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED; + if (!cursorLocked && + (mouseX >= (float)windowWidth || mouseX <= 0.0f || mouseY >= (float)windowHeight || mouseY <= 0.0f)) + { + // NOTE: If the mouse just came from off the screen and the cursor is not locked + // then don't count that as a real delta movement + deltaMouseX = 0.0f; + deltaMouseY = 0.0f; + } + else + { + deltaMouseX = ((float)xpos - lastMouseX); + deltaMouseY = (lastMouseY - (float)ypos); + } + lastMouseX = (float)xpos; lastMouseY = (float)ypos; } diff --git a/MinecraftYoutube/src/gameplay/CharacterSystem.cpp b/MinecraftYoutube/src/gameplay/CharacterSystem.cpp index dd3b0c6..7fd68a9 100644 --- a/MinecraftYoutube/src/gameplay/CharacterSystem.cpp +++ b/MinecraftYoutube/src/gameplay/CharacterSystem.cpp @@ -1,6 +1,5 @@ #include "gameplay/CharacterSystem.h" #include "gameplay/CharacterController.h" -//#include "gameplay/PlayerController.h" #include "renderer/Camera.h" #include "core/Application.h" #include "core/Input.h" @@ -20,42 +19,28 @@ namespace MinecraftClone speed = controller.controllerRunSpeed; } - rb.acceleration.x = camera.forward.x * controller.movementAxis.x; - rb.acceleration.y = camera.forward.y * controller.movementAxis.x; - rb.acceleration.z = camera.forward.z * controller.movementAxis.x; - rb.acceleration.x += camera.right.x * controller.movementAxis.z; - rb.acceleration.y += camera.right.y * controller.movementAxis.z; - rb.acceleration.z += camera.right.z * controller.movementAxis.z; + rb.acceleration = camera.forward * controller.movementAxis.x; + rb.acceleration += camera.right * controller.movementAxis.z; // The y-axis just changes the player's global y position // instead of using their local up axis rb.acceleration.y += controller.movementAxis.y; - if (glm::abs(rb.acceleration.x) > 0 || glm::abs(rb.acceleration.z) > 0 || (glm::abs(rb.acceleration.y) > 0)) + // Only normalize the acceleration if it is greater than 0. This + // way we avoid division by 0 errors + float accelerationMagnitudeSquared = glm::length2(rb.acceleration); + if (accelerationMagnitudeSquared > 0) { - float denominator = glm::inversesqrt(rb.acceleration.x * rb.acceleration.x + rb.acceleration.z * rb.acceleration.z); - if (glm::abs(rb.acceleration.y) > 0) - { - denominator = glm::inversesqrt(rb.acceleration.x * rb.acceleration.x + rb.acceleration.z * rb.acceleration.z + rb.acceleration.y * rb.acceleration.y); - rb.acceleration.y *= denominator * speed; - } - rb.acceleration.x *= denominator * speed; - rb.acceleration.z *= denominator * speed; + float denominator = glm::inversesqrt(accelerationMagnitudeSquared); + rb.acceleration *= denominator * speed; } - rb.velocity.x += rb.acceleration.x * Application::deltaTime; - rb.velocity.y += rb.acceleration.y * Application::deltaTime; - rb.velocity.z += rb.acceleration.z * Application::deltaTime; - - camera.position.x += rb.velocity.x * Application::deltaTime; - camera.position.y += rb.velocity.y * Application::deltaTime; - camera.position.z += rb.velocity.z * Application::deltaTime; + rb.velocity += rb.acceleration * Application::deltaTime; + camera.position += rb.velocity * Application::deltaTime; // If acceleration is 0, apply friction to decelerate the object if (glm::all(glm::epsilonEqual(rb.acceleration, glm::vec3(0.0f), 0.001f))) { - rb.velocity.x *= rb.friction * Application::deltaTime; - rb.velocity.z *= rb.friction * Application::deltaTime; - rb.velocity.y *= rb.friction * Application::deltaTime; + rb.velocity *= rb.friction * Application::deltaTime; } if (glm::length2(rb.velocity) >= terminalVelocity) @@ -69,15 +54,8 @@ namespace MinecraftClone rb.velocity = glm::vec3(0.0f); } - // Smooth the mouse movements over time - // Exponential decay function (https://en.wikipedia.org/wiki/Exponential_decay) - //constexpr float springiness = 50.0f; - //float delta = 1.0f - glm::exp(glm::log(0.5f) * Application::deltaTime * springiness); - //float newX = (controller.viewAxis.x - controller._targetViewAxis.x) * delta * Application::deltaTime * controller.movementSensitivity; - //float newY = (controller.viewAxis.y - controller._targetViewAxis.y) * delta * Application::deltaTime * controller.movementSensitivity; - //controller.viewAxis.x = newX; - //controller.viewAxis.y = newY; - //controller._targetViewAxis = controller.viewAxis; + // Mouse movements don't need to be smoothed over time. + // They alread represent a change in position controller.viewAxis *= controller.movementSensitivity; // Check out this stack overflow for a deeper explanation of diff --git a/MinecraftYoutube/src/gameplay/PlayerController.cpp b/MinecraftYoutube/src/gameplay/PlayerController.cpp index d0651da..4158ec6 100644 --- a/MinecraftYoutube/src/gameplay/PlayerController.cpp +++ b/MinecraftYoutube/src/gameplay/PlayerController.cpp @@ -2,6 +2,8 @@ #include "gameplay/CharacterController.h" #include "core.h" #include "core/Input.h" +#include "core/Application.h" +#include "core/Window.h" namespace MinecraftClone { diff --git a/MinecraftYoutube/src/renderer/Camera.cpp b/MinecraftYoutube/src/renderer/Camera.cpp index 97c8ff6..9364bca 100644 --- a/MinecraftYoutube/src/renderer/Camera.cpp +++ b/MinecraftYoutube/src/renderer/Camera.cpp @@ -9,7 +9,6 @@ namespace MinecraftClone glm::mat4 rotationMatrix = glm::toMat4(orientation); right = glm::row(rotationMatrix, 0); up = glm::row(rotationMatrix, 1); - glm::vec3 oldForward = forward; forward = glm::row(rotationMatrix, 2); glm::mat4 translationMatrix = glm::transpose(glm::mat4(