From 78ef93bcf513276e0eec2304db6ebbca284d76e7 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Fri, 14 Oct 2022 23:17:22 -0400 Subject: [PATCH] Start working on fixed timestep Frame interprelation still needs to be done to make a slow TPS look smooth --- .clang-tidy | 4 ++ examples/dev_testing/src/main.cpp | 6 +- include/simpleengine/camera.h | 11 ++++ .../ecs/component/transform_component.h | 9 ++- include/simpleengine/game.h | 13 +++-- include/simpleengine/gfx/light.h | 2 +- include/simpleengine/gfx/renderer.h | 13 +++-- include/simpleengine/renderable.h | 2 +- include/simpleengine/scene.h | 8 ++- resources/shaders/core/3d/fragment_core.glsl | 2 +- src/game.cpp | 36 ++++++++---- src/gfx/renderer.cpp | 55 ++++++++++++++----- src/scene.cpp | 21 ++++--- 13 files changed, 133 insertions(+), 49 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index b3fa1d3..adcf3e8 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -35,5 +35,9 @@ CheckOptions: value: "NULL" - key: readability-magic-numbers value: "0" + - key: cppcoreguidelines-avoid-magic-numbers + value: "0" + - key: cppcoreguidelines-non-private-member-variables-in-classes + value: "0" --- diff --git a/examples/dev_testing/src/main.cpp b/examples/dev_testing/src/main.cpp index ca77c59..e87abfe 100644 --- a/examples/dev_testing/src/main.cpp +++ b/examples/dev_testing/src/main.cpp @@ -70,11 +70,12 @@ int main(int argc, char *argv[]) { // Create a renderer auto renderer = std::make_shared(game.get_window(), core_shader, camera); renderer->initialize(); - game.add_renderable(renderer); + //game.add_renderable(renderer); // Create a Scene and give it the renderer auto scene = std::make_shared(renderer); - game.add_event(scene); + //game.add_event(scene); + game.add_renderable(scene); se::ecs::Entity other_e = scene->create_entity(); other_e.add_component("examples/dev_testing/resources/transparent_window.fbx", @@ -95,6 +96,7 @@ int main(int argc, char *argv[]) { se::ecs::Entity brick_e = scene->create_entity(); brick_e.add_component("examples/dev_testing/resources/bricks/bricks.fbx"); + brick_e.add_component(); auto &brick_transf = brick_e.add_component(); brick_transf.translate(6.f, -0.5f, 1.f); diff --git a/include/simpleengine/camera.h b/include/simpleengine/camera.h index 3a5f012..bf7c3a5 100644 --- a/include/simpleengine/camera.h +++ b/include/simpleengine/camera.h @@ -10,10 +10,21 @@ #include namespace simpleengine { + namespace gfx { + class Renderer; + } + class Camera : public simpleengine::Event { + friend gfx::Renderer; private: GLFWwindow* window; + + glm::vec3 last_position; + glm::vec3 last_rotation; + glm::mat4 last_view_matrix; + glm::vec3 last_camera_front; public: + glm::vec3 position; glm::vec3 rotation; gfx::Shader shader; diff --git a/include/simpleengine/ecs/component/transform_component.h b/include/simpleengine/ecs/component/transform_component.h index f0fa948..9a15519 100644 --- a/include/simpleengine/ecs/component/transform_component.h +++ b/include/simpleengine/ecs/component/transform_component.h @@ -1,5 +1,6 @@ #pragma once +//#include "simpleengine/scene.h" #include #include @@ -9,14 +10,18 @@ namespace simpleengine { * */ class TransformComponent { + friend class Scene; + private: + // This is the transform from the last render loop. The renderer uses this for frame interprelation + glm::mat4 last_transform_matrix; public: glm::mat4 transform_matrix; - TransformComponent() : transform_matrix(glm::mat4(1.f)) { + TransformComponent() : transform_matrix(1.f), last_transform_matrix(1.f) { } - TransformComponent(glm::mat4 transform_matrix) : transform_matrix(transform_matrix) { + TransformComponent(glm::mat4 transform_matrix) : transform_matrix(transform_matrix), last_transform_matrix(1.f) { } diff --git a/include/simpleengine/game.h b/include/simpleengine/game.h index 2f1ad17..ce6bd95 100644 --- a/include/simpleengine/game.h +++ b/include/simpleengine/game.h @@ -42,8 +42,8 @@ namespace simpleengine { void update(const float& delta_time); void handle_input(const float& delta_time); - void render_window(const float& delta_time); - void render_items(const float& delta_time); + void render_window(const float& interpolate_alpha, const float& delta_time); + void render_items(const float& interpolate_alpha, const float& delta_time); void exit(); int run(); @@ -63,8 +63,13 @@ namespace simpleengine { // FPS related stuff void update_enabled_vsync() const; void limit_framerate(const float& delta_time) const; // Triggered at the end of a draw to help limit the FPS to `fps_limit`. - int fps_limit; - bool enable_vsync; + int fps_limit = -1; + bool enable_vsync = true; + + int max_engine_tps = 60; // The maximum engine TPS + double tps_accumulator = 0.f; + //int engine_ticks_second = 0; // The amount of ticks in a second + //double last_sec_engine_tick; // The time of the last second float get_delta_time(); diff --git a/include/simpleengine/gfx/light.h b/include/simpleengine/gfx/light.h index f534792..a522f1c 100644 --- a/include/simpleengine/gfx/light.h +++ b/include/simpleengine/gfx/light.h @@ -23,7 +23,7 @@ namespace simpleengine::gfx { shader.unuse(); } - virtual void render() override { + virtual void render(const float& interpolate_alpha, const float& frame_time) override { } }; diff --git a/include/simpleengine/gfx/renderer.h b/include/simpleengine/gfx/renderer.h index bb0e06b..8536602 100644 --- a/include/simpleengine/gfx/renderer.h +++ b/include/simpleengine/gfx/renderer.h @@ -17,9 +17,10 @@ namespace simpleengine::gfx { public: RenderingType rendering_type; gfx::Mesh* rendering_mesh; + glm::mat4 last_transform_mat; glm::mat4 transform_mat; - RenderingJob(RenderingType rendering_type, gfx::Mesh& mesh, glm::mat4 position); + RenderingJob(RenderingType rendering_type, gfx::Mesh& mesh, glm::mat4 last_pos, glm::mat4 position); }; class Renderer : public simpleengine::Renderable { @@ -43,7 +44,7 @@ namespace simpleengine::gfx { void enable_debug(); virtual void sort_jobs(); - virtual void queue_job(RenderingType rendering_type, gfx::Mesh& mesh, glm::mat4 position); + virtual void queue_job(RenderingType rendering_type, gfx::Mesh& mesh, glm::mat4 last_position, glm::mat4 position); virtual void queue_job(RenderingJob job); virtual void create_job_buffers(RenderingJob& job); @@ -52,7 +53,7 @@ namespace simpleengine::gfx { virtual void update(const float& delta_time) override; - virtual void render() override; + virtual void render(const float& interpolate_alpha, const float& frame_time) override; /** * @brief Renders a single job. @@ -61,8 +62,8 @@ namespace simpleengine::gfx { * @return true if the job was rendered successfully. * @return false if there was an error when trying to render the job. */ - virtual bool render_job(const RenderingJob& job); - virtual void render_job_queue(std::queue& queue); - virtual void render_job_queue(std::map>& queue); + virtual bool render_job(const float& interpolate_alpha, const RenderingJob& job); + virtual void render_job_queue(const float& interpolate_alpha, std::queue& queue); + virtual void render_job_queue(const float& interpolate_alpha, std::map>& queue); }; } \ No newline at end of file diff --git a/include/simpleengine/renderable.h b/include/simpleengine/renderable.h index cf7446c..667c20b 100644 --- a/include/simpleengine/renderable.h +++ b/include/simpleengine/renderable.h @@ -14,6 +14,6 @@ namespace simpleengine { Renderable() = default; virtual ~Renderable() = default; - virtual void render() = 0; + virtual void render(const float& interpolate_alpha, const float& frame_time) = 0; }; } \ No newline at end of file diff --git a/include/simpleengine/scene.h b/include/simpleengine/scene.h index ec086fa..bda0945 100644 --- a/include/simpleengine/scene.h +++ b/include/simpleengine/scene.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -18,16 +19,21 @@ namespace simpleengine { class Entity; } - class Scene : public simpleengine::Event { + //class Scene : public simpleengine::Event { + class Scene : public simpleengine::Renderable { protected: entt::registry registry; std::shared_ptr renderer; + + // Last transform matrixes for all entities. + std::unordered_map last_transforms; public: Scene(std::shared_ptr renderer); ecs::Entity create_entity(); virtual void update(const float& delta_time) override; + virtual void render(const float& interpolate_alpha, const float& frame_time) override; virtual void destroy() override; }; diff --git a/resources/shaders/core/3d/fragment_core.glsl b/resources/shaders/core/3d/fragment_core.glsl index cd76c7b..f7ffb44 100644 --- a/resources/shaders/core/3d/fragment_core.glsl +++ b/resources/shaders/core/3d/fragment_core.glsl @@ -26,7 +26,7 @@ struct Material { bool has_specular_map; sampler2D specular_map; - + bool has_normal_map; sampler2D normal_map; diff --git a/src/game.cpp b/src/game.cpp index dc7d5ac..f158503 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -19,7 +19,7 @@ simpleengine::Game::Game(int w, int h, const std::string& window_name, const int& gl_profile, const int& major_version, const int& minor_version, const bool& resizeable, const int& forward_compat) : window_resizeable(resizeable), - enable_vsync(true), fps_limit(-1) { + tps_accumulator(0.f) { initialize(gl_profile, major_version, minor_version, window_resizeable, forward_compat); // Create a window @@ -45,6 +45,8 @@ simpleengine::Game::Game(int w, int h, const std::string& window_name, const int } enable_default_gl_options(); + + last_frame_time = std::chrono::high_resolution_clock::now(); } void simpleengine::Game::enable_default_gl_options() const { @@ -105,6 +107,10 @@ void simpleengine::Game::update_enabled_vsync() const { } } +void simpleengine::Game::handle_input(const float& delta_time) { + // TODO +} + void simpleengine::Game::update(const float& delta_time) { handle_input(delta_time); @@ -114,19 +120,15 @@ void simpleengine::Game::update(const float& delta_time) { } } -void simpleengine::Game::handle_input(const float& delta_time) { - // TODO -} - -void simpleengine::Game::render_window(const float& delta_time) { +void simpleengine::Game::render_window(const float& interpolate_alpha, const float& delta_time) { glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - render_items(delta_time); + render_items(interpolate_alpha, delta_time); } -void simpleengine::Game::render_items(const float& delta_time) { +void simpleengine::Game::render_items(const float& interpolate_alpha, const float& delta_time) { for (const std::shared_ptr& renderable : renderable_events) { - renderable->render(); + renderable->render(interpolate_alpha, delta_time); } } @@ -155,12 +157,24 @@ int simpleengine::Game::run() { while (!glfwWindowShouldClose(window)) { // Get delta time first thing float delta_time = get_delta_time(); + //std::cout << "Delta time: " << delta_time << std::endl; // Poll input events glfwPollEvents(); - update(delta_time); - render_window(delta_time); + const double max_delta_time = 0.5; + + tps_accumulator += delta_time; + + while (tps_accumulator >= max_delta_time) { + update(max_delta_time); + + tps_accumulator -= max_delta_time; + } + + const double interpolate_alpha = tps_accumulator / max_delta_time; + + render_window(interpolate_alpha, delta_time); // End draw glfwSwapBuffers(window); diff --git a/src/gfx/renderer.cpp b/src/gfx/renderer.cpp index 47d099d..514ec33 100644 --- a/src/gfx/renderer.cpp +++ b/src/gfx/renderer.cpp @@ -13,11 +13,13 @@ #include #include +#include + namespace simpleengine::gfx { void create_mesh_buffers(simpleengine::gfx::Mesh &mesh); - RenderingJob::RenderingJob(RenderingType rendering_type, gfx::Mesh &mesh, glm::mat4 position) - : rendering_type(rendering_type), rendering_mesh(&mesh), transform_mat(position) {} + RenderingJob::RenderingJob(RenderingType rendering_type, gfx::Mesh &mesh, glm::mat4 last_pos, glm::mat4 position) + : rendering_type(rendering_type), rendering_mesh(&mesh), last_transform_mat(last_pos), transform_mat(position) {} Renderer::Renderer(GLFWwindow *window, gfx::Shader shader, std::shared_ptr camera) : window(window), shader(shader), camera(camera)/* , transparent_render_queue(CameraDistanceComparator(camera)) */ {} @@ -50,8 +52,8 @@ namespace simpleengine::gfx { // std::sort() } - void Renderer::queue_job(RenderingType rendering_type, gfx::Mesh &mesh, glm::mat4 position) { - RenderingJob job(rendering_type, mesh, position); + void Renderer::queue_job(RenderingType rendering_type, gfx::Mesh &mesh, glm::mat4 last_position, glm::mat4 position) { + RenderingJob job(rendering_type, mesh, last_position, position); this->queue_job(job); } @@ -135,10 +137,37 @@ namespace simpleengine::gfx { } */ } - bool Renderer::render_job(const RenderingJob &job) { + glm::mat4 lerp(glm::mat4 a, glm::mat4 b, float alpha) { + //return a * (1.f - alpha) + b * alpha; + glm::quat rot0 = glm::quat_cast(a); + glm::quat rot1= glm::quat_cast(b); + + glm::quat finalRot = glm::slerp(rot0, rot1, alpha); + + glm::mat4 finalMat = glm::mat4_cast(finalRot); + + finalMat[3] = a[3] * (1 - alpha) + b[3] * alpha; + + return finalMat; + } + + bool Renderer::render_job(const float& interpolate_alpha, const RenderingJob &job) { Mesh *mesh = job.rendering_mesh; - shader.set_uniform_matrix_4f("u_transform_matrix", job.transform_mat); + /* glm::mat4 transform_mat = job.transform_mat * interpolate_alpha + + job.last_transform_mat * (1.f - interpolate_alpha); */ + + //glm::mat4 transform_mat = lerp(job.transform_mat, job.last_transform_mat, interpolate_alpha); + glm::mat4 transform_mat = lerp(job.last_transform_mat, job.transform_mat, interpolate_alpha); + + std::cout << "Last transform: " << glm::to_string(job.last_transform_mat) << std::endl << + "Current Transform: " << glm::to_string(job.transform_mat) << std::endl << + "Lerp: " << glm::to_string(transform_mat) << std::endl << std::endl; + + //std::cout << "Current: " << job.transform_mat[3] + + shader.set_uniform_matrix_4f("u_transform_matrix", transform_mat); + //shader.set_uniform_matrix_4f("u_transform_matrix", job.transform_mat); std::optional &material = mesh->material; @@ -196,12 +225,12 @@ namespace simpleengine::gfx { return true; } - void Renderer::render_job_queue(std::queue &rendering_queue) { + void Renderer::render_job_queue(const float& interpolate_alpha, std::queue &rendering_queue) { while (!rendering_queue.empty()) { // Get the job from the queue, we'll remove it after we render. RenderingJob &job = rendering_queue.front(); - bool res = this->render_job(job); + bool res = this->render_job(interpolate_alpha, job); if (res) { // Now we'll remove the job from the queue. @@ -210,19 +239,19 @@ namespace simpleengine::gfx { } } - void Renderer::render_job_queue(std::map>& rendering_queue) { + void Renderer::render_job_queue(const float& interpolate_alpha, std::map>& rendering_queue) { // Render each job then clear the queue for (const auto& it : rendering_queue) { - this->render_job(it.second); + this->render_job(interpolate_alpha, it.second); } rendering_queue.clear(); } - void Renderer::render() { + void Renderer::render(const float& interpolate_alpha, const float& frame_time) { check_if_initialized(); // Render other (opaque) objects first - render_job_queue(other_render_queue); + render_job_queue(interpolate_alpha, other_render_queue); // Render transparent objects std::map> transparent_jobs; @@ -235,6 +264,6 @@ namespace simpleengine::gfx { transparent_render_queue.pop(); } - render_job_queue(transparent_jobs); + render_job_queue(interpolate_alpha, transparent_jobs); } } // namespace simpleengine::gfx \ No newline at end of file diff --git a/src/scene.cpp b/src/scene.cpp index d5e5540..875acd8 100644 --- a/src/scene.cpp +++ b/src/scene.cpp @@ -16,30 +16,37 @@ namespace simpleengine { } void Scene::update(const float& delta_time) { + // Rotate the model + registry.view().each([this, &delta_time](TransformComponent& transform, RotatingComponent& rotating) { + transform.rotate(rotating.rate * delta_time, rotating.rotation_axis); + }); + } + + void Scene::render(const float& interpolate_alpha, const float& frame_time) { // Is there a way these can be grouped? - registry.view().each([this](const TransformComponent& transform, ModelComponent& model_component) { + registry.view().each([this](TransformComponent& transform, ModelComponent& model_component) { for (auto& mesh : model_component.model.meshes) { auto rendering_type = gfx::RenderingType::RendType_OPAQUE; if (mesh.material) { rendering_type = mesh.material->rendering_type; } - renderer->queue_job(gfx::RenderingJob(rendering_type, mesh, transform.transform_matrix)); + renderer->queue_job(gfx::RenderingJob(rendering_type, mesh, transform.last_transform_matrix, transform.transform_matrix)); + transform.last_transform_matrix = transform.transform_matrix; // Update last transform } }); - registry.view().each([this](const TransformComponent& transform, MeshComponent& mesh_component) { + registry.view().each([this](TransformComponent& transform, MeshComponent& mesh_component) { auto rendering_type = gfx::RenderingType::RendType_OPAQUE; if (mesh_component.mesh.material) { rendering_type = mesh_component.mesh.material->rendering_type; } - renderer->queue_job(gfx::RenderingJob(rendering_type, mesh_component.mesh, transform.transform_matrix)); + renderer->queue_job(gfx::RenderingJob(rendering_type, mesh_component.mesh, transform.last_transform_matrix, transform.transform_matrix)); + transform.last_transform_matrix = transform.transform_matrix; // Update last transform }); - registry.view().each([this, &delta_time](TransformComponent& transform, RotatingComponent& rotating) { - transform.rotate(rotating.rate * delta_time, rotating.rotation_axis); - }); + renderer->render(interpolate_alpha, frame_time); } void Scene::destroy() {