Start working on fixed timestep

Frame interprelation still needs to be done to make a slow TPS look smooth
This commit is contained in:
SeanOMik 2022-10-14 23:17:22 -04:00
parent ce8886ade1
commit 78ef93bcf5
Signed by: SeanOMik
GPG Key ID: 568F326C7EB33ACB
13 changed files with 133 additions and 49 deletions

View File

@ -35,5 +35,9 @@ CheckOptions:
value: "NULL" value: "NULL"
- key: readability-magic-numbers - key: readability-magic-numbers
value: "0" value: "0"
- key: cppcoreguidelines-avoid-magic-numbers
value: "0"
- key: cppcoreguidelines-non-private-member-variables-in-classes
value: "0"
--- ---

View File

@ -70,11 +70,12 @@ int main(int argc, char *argv[]) {
// Create a renderer // Create a renderer
auto renderer = std::make_shared<se::gfx::Renderer>(game.get_window(), core_shader, camera); auto renderer = std::make_shared<se::gfx::Renderer>(game.get_window(), core_shader, camera);
renderer->initialize(); renderer->initialize();
game.add_renderable(renderer); //game.add_renderable(renderer);
// Create a Scene and give it the renderer // Create a Scene and give it the renderer
auto scene = std::make_shared<se::Scene>(renderer); auto scene = std::make_shared<se::Scene>(renderer);
game.add_event(scene); //game.add_event(scene);
game.add_renderable(scene);
se::ecs::Entity other_e = scene->create_entity(); se::ecs::Entity other_e = scene->create_entity();
other_e.add_component<se::ModelComponent>("examples/dev_testing/resources/transparent_window.fbx", other_e.add_component<se::ModelComponent>("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(); se::ecs::Entity brick_e = scene->create_entity();
brick_e.add_component<se::ModelComponent>("examples/dev_testing/resources/bricks/bricks.fbx"); brick_e.add_component<se::ModelComponent>("examples/dev_testing/resources/bricks/bricks.fbx");
brick_e.add_component<se::RotatingComponent>();
auto &brick_transf = brick_e.add_component<se::TransformComponent>(); auto &brick_transf = brick_e.add_component<se::TransformComponent>();
brick_transf.translate(6.f, -0.5f, 1.f); brick_transf.translate(6.f, -0.5f, 1.f);

View File

@ -10,10 +10,21 @@
#include <glm/glm.hpp> #include <glm/glm.hpp>
namespace simpleengine { namespace simpleengine {
namespace gfx {
class Renderer;
}
class Camera : public simpleengine::Event { class Camera : public simpleengine::Event {
friend gfx::Renderer;
private: private:
GLFWwindow* window; GLFWwindow* window;
glm::vec3 last_position;
glm::vec3 last_rotation;
glm::mat4 last_view_matrix;
glm::vec3 last_camera_front;
public: public:
glm::vec3 position; glm::vec3 position;
glm::vec3 rotation; glm::vec3 rotation;
gfx::Shader shader; gfx::Shader shader;

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
//#include "simpleengine/scene.h"
#include <glm/ext/matrix_transform.hpp> #include <glm/ext/matrix_transform.hpp>
#include <glm/glm.hpp> #include <glm/glm.hpp>
@ -9,14 +10,18 @@ namespace simpleengine {
* *
*/ */
class TransformComponent { 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: public:
glm::mat4 transform_matrix; 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) {
} }

View File

@ -42,8 +42,8 @@ namespace simpleengine {
void update(const float& delta_time); void update(const float& delta_time);
void handle_input(const float& delta_time); void handle_input(const float& delta_time);
void render_window(const float& delta_time); void render_window(const float& interpolate_alpha, const float& delta_time);
void render_items(const float& delta_time); void render_items(const float& interpolate_alpha, const float& delta_time);
void exit(); void exit();
int run(); int run();
@ -63,8 +63,13 @@ namespace simpleengine {
// FPS related stuff // FPS related stuff
void update_enabled_vsync() const; 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`. 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; int fps_limit = -1;
bool enable_vsync; 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(); float get_delta_time();

View File

@ -23,7 +23,7 @@ namespace simpleengine::gfx {
shader.unuse(); shader.unuse();
} }
virtual void render() override { virtual void render(const float& interpolate_alpha, const float& frame_time) override {
} }
}; };

View File

@ -17,9 +17,10 @@ namespace simpleengine::gfx {
public: public:
RenderingType rendering_type; RenderingType rendering_type;
gfx::Mesh* rendering_mesh; gfx::Mesh* rendering_mesh;
glm::mat4 last_transform_mat;
glm::mat4 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 { class Renderer : public simpleengine::Renderable {
@ -43,7 +44,7 @@ namespace simpleengine::gfx {
void enable_debug(); void enable_debug();
virtual void sort_jobs(); 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 queue_job(RenderingJob job);
virtual void create_job_buffers(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 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. * @brief Renders a single job.
@ -61,8 +62,8 @@ namespace simpleengine::gfx {
* @return true if the job was rendered successfully. * @return true if the job was rendered successfully.
* @return false if there was an error when trying to render the job. * @return false if there was an error when trying to render the job.
*/ */
virtual bool render_job(const RenderingJob& job); virtual bool render_job(const float& interpolate_alpha, const RenderingJob& job);
virtual void render_job_queue(std::queue<RenderingJob>& queue); virtual void render_job_queue(const float& interpolate_alpha, std::queue<RenderingJob>& queue);
virtual void render_job_queue(std::map<float, RenderingJob, std::greater<>>& queue); virtual void render_job_queue(const float& interpolate_alpha, std::map<float, RenderingJob, std::greater<>>& queue);
}; };
} }

View File

@ -14,6 +14,6 @@ namespace simpleengine {
Renderable() = default; Renderable() = default;
virtual ~Renderable() = default; virtual ~Renderable() = default;
virtual void render() = 0; virtual void render(const float& interpolate_alpha, const float& frame_time) = 0;
}; };
} }

View File

@ -9,6 +9,7 @@
#include <memory> #include <memory>
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include <unordered_map>
#include <vector> #include <vector>
#include <entt/entt.hpp> #include <entt/entt.hpp>
@ -18,16 +19,21 @@ namespace simpleengine {
class Entity; class Entity;
} }
class Scene : public simpleengine::Event { //class Scene : public simpleengine::Event {
class Scene : public simpleengine::Renderable {
protected: protected:
entt::registry registry; entt::registry registry;
std::shared_ptr<gfx::Renderer> renderer; std::shared_ptr<gfx::Renderer> renderer;
// Last transform matrixes for all entities.
std::unordered_map<uint32_t, glm::mat4> last_transforms;
public: public:
Scene(std::shared_ptr<gfx::Renderer> renderer); Scene(std::shared_ptr<gfx::Renderer> renderer);
ecs::Entity create_entity(); ecs::Entity create_entity();
virtual void update(const float& delta_time) override; virtual void update(const float& delta_time) override;
virtual void render(const float& interpolate_alpha, const float& frame_time) override;
virtual void destroy() override; virtual void destroy() override;
}; };

View File

@ -26,7 +26,7 @@ struct Material {
bool has_specular_map; bool has_specular_map;
sampler2D specular_map; sampler2D specular_map;
bool has_normal_map; bool has_normal_map;
sampler2D normal_map; sampler2D normal_map;

View File

@ -19,7 +19,7 @@
simpleengine::Game::Game(int w, int h, const std::string& window_name, const int& gl_profile, const int& major_version, 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), 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); initialize(gl_profile, major_version, minor_version, window_resizeable, forward_compat);
// Create a window // 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(); enable_default_gl_options();
last_frame_time = std::chrono::high_resolution_clock::now();
} }
void simpleengine::Game::enable_default_gl_options() const { 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) { void simpleengine::Game::update(const float& delta_time) {
handle_input(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) { void simpleengine::Game::render_window(const float& interpolate_alpha, const float& delta_time) {
// TODO
}
void simpleengine::Game::render_window(const float& delta_time) {
glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); 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 : renderable_events) { for (const std::shared_ptr<Renderable>& renderable : renderable_events) {
renderable->render(); renderable->render(interpolate_alpha, delta_time);
} }
} }
@ -155,12 +157,24 @@ int simpleengine::Game::run() {
while (!glfwWindowShouldClose(window)) { while (!glfwWindowShouldClose(window)) {
// Get delta time first thing // Get delta time first thing
float delta_time = get_delta_time(); float delta_time = get_delta_time();
//std::cout << "Delta time: " << delta_time << std::endl;
// Poll input events // Poll input events
glfwPollEvents(); glfwPollEvents();
update(delta_time); const double max_delta_time = 0.5;
render_window(delta_time);
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 // End draw
glfwSwapBuffers(window); glfwSwapBuffers(window);

View File

@ -13,11 +13,13 @@
#include <glm/geometric.hpp> #include <glm/geometric.hpp>
#include <stdexcept> #include <stdexcept>
#include <glm/gtx/string_cast.hpp>
namespace simpleengine::gfx { namespace simpleengine::gfx {
void create_mesh_buffers(simpleengine::gfx::Mesh &mesh); void create_mesh_buffers(simpleengine::gfx::Mesh &mesh);
RenderingJob::RenderingJob(RenderingType rendering_type, gfx::Mesh &mesh, glm::mat4 position) RenderingJob::RenderingJob(RenderingType rendering_type, gfx::Mesh &mesh, glm::mat4 last_pos, glm::mat4 position)
: rendering_type(rendering_type), rendering_mesh(&mesh), transform_mat(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> camera) Renderer::Renderer(GLFWwindow *window, gfx::Shader shader, std::shared_ptr<Camera> camera)
: window(window), shader(shader), camera(camera)/* , transparent_render_queue(CameraDistanceComparator(camera)) */ {} : window(window), shader(shader), camera(camera)/* , transparent_render_queue(CameraDistanceComparator(camera)) */ {}
@ -50,8 +52,8 @@ namespace simpleengine::gfx {
// std::sort() // std::sort()
} }
void Renderer::queue_job(RenderingType rendering_type, gfx::Mesh &mesh, glm::mat4 position) { void Renderer::queue_job(RenderingType rendering_type, gfx::Mesh &mesh, glm::mat4 last_position, glm::mat4 position) {
RenderingJob job(rendering_type, mesh, position); RenderingJob job(rendering_type, mesh, last_position, position);
this->queue_job(job); 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; 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> &material = mesh->material; std::optional<Material> &material = mesh->material;
@ -196,12 +225,12 @@ namespace simpleengine::gfx {
return true; return true;
} }
void Renderer::render_job_queue(std::queue<RenderingJob> &rendering_queue) { void Renderer::render_job_queue(const float& interpolate_alpha, std::queue<RenderingJob> &rendering_queue) {
while (!rendering_queue.empty()) { while (!rendering_queue.empty()) {
// Get the job from the queue, we'll remove it after we render. // Get the job from the queue, we'll remove it after we render.
RenderingJob &job = rendering_queue.front(); RenderingJob &job = rendering_queue.front();
bool res = this->render_job(job); bool res = this->render_job(interpolate_alpha, job);
if (res) { if (res) {
// Now we'll remove the job from the queue. // Now we'll remove the job from the queue.
@ -210,19 +239,19 @@ namespace simpleengine::gfx {
} }
} }
void Renderer::render_job_queue(std::map<float, RenderingJob, std::greater<>>& rendering_queue) { void Renderer::render_job_queue(const float& interpolate_alpha, std::map<float, RenderingJob, std::greater<>>& rendering_queue) {
// Render each job then clear the queue // Render each job then clear the queue
for (const auto& it : rendering_queue) { for (const auto& it : rendering_queue) {
this->render_job(it.second); this->render_job(interpolate_alpha, it.second);
} }
rendering_queue.clear(); rendering_queue.clear();
} }
void Renderer::render() { void Renderer::render(const float& interpolate_alpha, const float& frame_time) {
check_if_initialized(); check_if_initialized();
// Render other (opaque) objects first // Render other (opaque) objects first
render_job_queue(other_render_queue); render_job_queue(interpolate_alpha, other_render_queue);
// Render transparent objects // Render transparent objects
std::map<float, RenderingJob, std::greater<>> transparent_jobs; std::map<float, RenderingJob, std::greater<>> transparent_jobs;
@ -235,6 +264,6 @@ namespace simpleengine::gfx {
transparent_render_queue.pop(); transparent_render_queue.pop();
} }
render_job_queue(transparent_jobs); render_job_queue(interpolate_alpha, transparent_jobs);
} }
} // namespace simpleengine::gfx } // namespace simpleengine::gfx

View File

@ -16,30 +16,37 @@ namespace simpleengine {
} }
void Scene::update(const float& delta_time) { void Scene::update(const float& delta_time) {
// Rotate the model
registry.view<TransformComponent, RotatingComponent>().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? // Is there a way these can be grouped?
registry.view<const TransformComponent, ModelComponent>().each([this](const TransformComponent& transform, ModelComponent& model_component) { registry.view<TransformComponent, ModelComponent>().each([this](TransformComponent& transform, ModelComponent& model_component) {
for (auto& mesh : model_component.model.meshes) { for (auto& mesh : model_component.model.meshes) {
auto rendering_type = gfx::RenderingType::RendType_OPAQUE; auto rendering_type = gfx::RenderingType::RendType_OPAQUE;
if (mesh.material) { if (mesh.material) {
rendering_type = mesh.material->rendering_type; 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<const TransformComponent, MeshComponent>().each([this](const TransformComponent& transform, MeshComponent& mesh_component) { registry.view<TransformComponent, MeshComponent>().each([this](TransformComponent& transform, MeshComponent& mesh_component) {
auto rendering_type = gfx::RenderingType::RendType_OPAQUE; auto rendering_type = gfx::RenderingType::RendType_OPAQUE;
if (mesh_component.mesh.material) { if (mesh_component.mesh.material) {
rendering_type = mesh_component.mesh.material->rendering_type; 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<TransformComponent, RotatingComponent>().each([this, &delta_time](TransformComponent& transform, RotatingComponent& rotating) { renderer->render(interpolate_alpha, frame_time);
transform.rotate(rotating.rate * delta_time, rotating.rotation_axis);
});
} }
void Scene::destroy() { void Scene::destroy() {