diff --git a/app/next/src/main_window.cpp b/app/next/src/main_window.cpp index 81e5b05..f6ae2f4 100644 --- a/app/next/src/main_window.cpp +++ b/app/next/src/main_window.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include #include "gltf_loader.hpp" #include "main_window.hpp" @@ -39,6 +39,9 @@ namespace zephyr { void MainWindow::MainLoop() { SDL_Event event{}; + f32 euler_x = 0.0f; + f32 euler_y = 0.0f; + while(true) { while(SDL_PollEvent(&event)) { if(event.type == SDL_QUIT) { @@ -63,6 +66,21 @@ namespace zephyr { } } + const u8* key_state = SDL_GetKeyboardState(nullptr); + + Vector3& camera_position = m_camera_node->GetTransform().GetPosition(); + const f32 delta_p = 0.075f; + const f32 delta_r = 0.01f; + if(key_state[SDL_SCANCODE_W]) camera_position -= m_camera_node->GetTransform().GetLocal().Z().XYZ() * delta_p; + if(key_state[SDL_SCANCODE_S]) camera_position += m_camera_node->GetTransform().GetLocal().Z().XYZ() * delta_p; + if(key_state[SDL_SCANCODE_A]) camera_position -= m_camera_node->GetTransform().GetLocal().X().XYZ() * delta_p; + if(key_state[SDL_SCANCODE_D]) camera_position += m_camera_node->GetTransform().GetLocal().X().XYZ() * delta_p; + if(key_state[SDL_SCANCODE_LEFT]) euler_y += delta_r; + if(key_state[SDL_SCANCODE_RIGHT]) euler_y -= delta_r; + if(key_state[SDL_SCANCODE_UP]) euler_x += delta_r; + if(key_state[SDL_SCANCODE_DOWN]) euler_x -= delta_r; + m_camera_node->GetTransform().GetRotation().SetFromEuler(euler_x, euler_y, 0.0f); + RenderFrame(); } } @@ -125,6 +143,10 @@ namespace zephyr { void MainWindow::CreateScene() { m_scene_root = SceneNode::New(); + m_camera_node = m_scene_root->CreateChild("RenderCamera"); + m_camera_node->CreateComponent(45.0f, 16.f / 9.f, 0.01f, 100.f); + m_camera_node->GetTransform().GetPosition() = {0.f, 0.f, 5.f}; + GLTFLoader gltf_loader{}; std::shared_ptr gltf_scene_1 = gltf_loader.Parse("models/DamagedHelmet/DamagedHelmet.gltf"); gltf_scene_1->GetTransform().GetPosition() = {1.0f, 0.0f, -5.0f}; diff --git a/app/next/src/main_window.hpp b/app/next/src/main_window.hpp index 85af89b..781bdee 100644 --- a/app/next/src/main_window.hpp +++ b/app/next/src/main_window.hpp @@ -39,6 +39,7 @@ namespace zephyr { std::unique_ptr m_render_engine{}; std::shared_ptr m_scene_root{}; + std::shared_ptr m_camera_node{}; std::shared_ptr m_behemoth_scene{}; u64 m_frame{}; diff --git a/zephyr/renderer/CMakeLists.txt b/zephyr/renderer/CMakeLists.txt index b53c955..f7a595a 100644 --- a/zephyr/renderer/CMakeLists.txt +++ b/zephyr/renderer/CMakeLists.txt @@ -19,6 +19,7 @@ set(HEADERS_PUBLIC include/zephyr/renderer/backend/render_backend.hpp include/zephyr/renderer/backend/render_backend_ogl.hpp include/zephyr/renderer/backend/render_backend_vk.hpp + include/zephyr/renderer/component/camera.hpp include/zephyr/renderer/component/mesh.hpp include/zephyr/renderer/engine/geometry_cache.hpp include/zephyr/renderer/resource/geometry.hpp diff --git a/zephyr/renderer/include/zephyr/renderer/backend/render_backend.hpp b/zephyr/renderer/include/zephyr/renderer/backend/render_backend.hpp index ab2eca5..fc7ff0c 100644 --- a/zephyr/renderer/include/zephyr/renderer/backend/render_backend.hpp +++ b/zephyr/renderer/include/zephyr/renderer/backend/render_backend.hpp @@ -60,7 +60,7 @@ namespace zephyr { virtual void DestroyRenderGeometry(RenderGeometry* geometry) = 0; /// Just a quick thing for testing the rendering. - virtual void Render(const Matrix4& projection, std::span render_objects) = 0; + virtual void Render(const Matrix4& view_projection, std::span render_objects) = 0; /// Start rendering the next frame. virtual void SwapBuffers() = 0; diff --git a/zephyr/renderer/include/zephyr/renderer/component/camera.hpp b/zephyr/renderer/include/zephyr/renderer/component/camera.hpp new file mode 100644 index 0000000..3be6a8a --- /dev/null +++ b/zephyr/renderer/include/zephyr/renderer/component/camera.hpp @@ -0,0 +1,102 @@ + +#pragma once + +#include +#include +#include +#include + +namespace zephyr { + + class PerspectiveCameraComponent : public Component { + public: + PerspectiveCameraComponent(f32 field_of_view, f32 aspect_ratio, f32 near, f32 far) { + Setup(field_of_view, aspect_ratio, near, far); + } + + void Setup(f32 field_of_view, f32 aspect_ratio, f32 near, f32 far) { + m_field_of_view = field_of_view; + m_aspect_ratio = aspect_ratio; + m_near = near; + m_far = far; + m_needs_update = true; + } + + [[nodiscard]] const Matrix4& GetProjectionMatrix() const { + if(m_needs_update) { + UpdateProjectionMatrixAndFrustum(); + } + return m_projection_matrix; + } + + [[nodiscard]] const Frustum& GetFrustum() const { + if(m_needs_update) { + UpdateProjectionMatrixAndFrustum(); + } + return m_frustum; + } + + [[nodiscard]] f32 GetFieldOfView() const { + return m_field_of_view; + } + + void SetFieldOfView(f32 field_of_view) { + m_field_of_view = field_of_view; + m_needs_update = true; + } + + [[nodiscard]] f32 GetAspectRatio() const { + return m_aspect_ratio; + } + + void SetAspectRatio(f32 aspect_ratio) { + m_aspect_ratio = aspect_ratio; + m_needs_update = true; + } + + [[nodiscard]] f32 GetNear() const { + return m_near; + } + + void SetNear(f32 near) { + m_near = near; + m_needs_update = true; + } + + [[nodiscard]] f32 GetFar() const { + return m_far; + } + + void SetFar(f32 far) { + m_far = far; + m_needs_update = true; + } + + private: + void UpdateProjectionMatrixAndFrustum() const { + m_projection_matrix = Matrix4::PerspectiveVK(m_field_of_view, m_aspect_ratio, m_near, m_far); + + const f32 x = 1.f / m_projection_matrix.X().X(); + const f32 y = 1.f / m_projection_matrix.Y().Y(); + + m_frustum.SetPlane(Frustum::Side::NZ, Plane{Vector3{ 0, 0, -1}, -m_near}); + m_frustum.SetPlane(Frustum::Side::PZ, Plane{Vector3{ 0, 0, 1}, -m_far }); + m_frustum.SetPlane(Frustum::Side::NX, Plane{Vector3{ 1 , 0, -x}.Normalize(), 0}); + m_frustum.SetPlane(Frustum::Side::PX, Plane{Vector3{-1 , 0, -x}.Normalize(), 0}); + m_frustum.SetPlane(Frustum::Side::NY, Plane{Vector3{ 0, 1, -y}.Normalize(), 0}); + m_frustum.SetPlane(Frustum::Side::PY, Plane{Vector3{ 0, -1, -y}.Normalize(), 0}); + + m_needs_update = false; + } + + f32 m_field_of_view{}; + f32 m_aspect_ratio{}; + f32 m_near{}; + f32 m_far{}; + + mutable bool m_needs_update{}; + mutable Matrix4 m_projection_matrix{}; + mutable Frustum m_frustum{}; + }; + +} // namespace zephyr diff --git a/zephyr/renderer/include/zephyr/renderer/render_engine.hpp b/zephyr/renderer/include/zephyr/renderer/render_engine.hpp index e1ae141..2f4fc59 100644 --- a/zephyr/renderer/include/zephyr/renderer/render_engine.hpp +++ b/zephyr/renderer/include/zephyr/renderer/render_engine.hpp @@ -42,6 +42,11 @@ namespace zephyr { }; std::vector m_game_thread_render_objects; + struct RenderCamera { + Matrix4 view; + Matrix4 projection; + } m_render_camera{}; + std::vector m_render_objects; }; diff --git a/zephyr/renderer/src/backend/opengl/render_backend.cpp b/zephyr/renderer/src/backend/opengl/render_backend.cpp index 99a2ecb..505ce72 100644 --- a/zephyr/renderer/src/backend/opengl/render_backend.cpp +++ b/zephyr/renderer/src/backend/opengl/render_backend.cpp @@ -154,14 +154,14 @@ namespace zephyr { delete geometry; } - void Render(const Matrix4& projection, std::span render_objects) override { + void Render(const Matrix4& view_projection, std::span render_objects) override { glClearColor(0.1f, 0.1f, 0.1f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glUseProgram(m_gl_shader_program); glBindBufferBase(GL_UNIFORM_BUFFER, 0, m_gl_ubo); - glNamedBufferSubData(m_gl_ubo, 0, sizeof(Matrix4), &projection); + glNamedBufferSubData(m_gl_ubo, 0, sizeof(Matrix4), &view_projection); for(const RenderObject& render_object : render_objects) { glNamedBufferSubData(m_gl_ubo, sizeof(Matrix4), sizeof(Matrix4), &render_object.local_to_world); diff --git a/zephyr/renderer/src/backend/vulkan/render_backend.cpp b/zephyr/renderer/src/backend/vulkan/render_backend.cpp index 9d0cdce..406336f 100644 --- a/zephyr/renderer/src/backend/vulkan/render_backend.cpp +++ b/zephyr/renderer/src/backend/vulkan/render_backend.cpp @@ -73,7 +73,7 @@ namespace zephyr { ZEPHYR_PANIC("unimplemented"); } - void Render(const Matrix4& projection, std::span render_objects) override { + void Render(const Matrix4& view_projection, std::span render_objects) override { const VkClearValue clear_value{ .color = VkClearColorValue{ .float32 = {0.01f, 0.01f, 0.01f, 1.0f} @@ -95,7 +95,7 @@ namespace zephyr { vkCmdBeginRenderPass(m_vk_command_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); vkCmdBindPipeline(m_vk_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_vk_pipeline); - vkCmdPushConstants(m_vk_command_buffer, m_vk_pipeline_layout, VK_SHADER_STAGE_ALL, 0u, sizeof(Matrix4), &projection); + vkCmdPushConstants(m_vk_command_buffer, m_vk_pipeline_layout, VK_SHADER_STAGE_ALL, 0u, sizeof(Matrix4), &view_projection); for(const RenderObject& render_object : render_objects) { vkCmdPushConstants(m_vk_command_buffer, m_vk_pipeline_layout, VK_SHADER_STAGE_ALL, sizeof(Matrix4), sizeof(Matrix4), &render_object.local_to_world); diff --git a/zephyr/renderer/src/render_engine.cpp b/zephyr/renderer/src/render_engine.cpp index 70f387b..f205ac5 100644 --- a/zephyr/renderer/src/render_engine.cpp +++ b/zephyr/renderer/src/render_engine.cpp @@ -1,4 +1,5 @@ +#include #include #include #include @@ -22,8 +23,10 @@ namespace zephyr { // Instruct the geometry cache to evict geometries which had been deleted in the submitted frame. m_geometry_cache.CommitPendingDeleteTaskList(); - // Build a list of objects to render and instruct the geometry cache to update (if necessary) any geometries we might need to render. + // Traverse the scene and update any data structures (such as render lists and resource caches) needed to render the frame. m_game_thread_render_objects.clear(); + m_render_camera.view = Matrix4::Identity(); + m_render_camera.projection = Matrix4::Identity(); scene_root->Traverse([&](SceneNode* node) -> bool { if(!node->IsVisible()) return false; @@ -41,6 +44,13 @@ namespace zephyr { } } + // TODO(fleroviux): think of a better way to mark the camera we actually want to use. + if(node->HasComponent()) { + const PerspectiveCameraComponent& camera_component = node->GetComponent(); + m_render_camera.view = node->GetTransform().GetWorld().Inverse(); + m_render_camera.projection = camera_component.GetProjectionMatrix(); + } + return true; }); @@ -68,9 +78,8 @@ namespace zephyr { while(m_render_thread_running) { ReadyRenderThreadData(); - // TODO(fleroviux): do not hardcode the aspect ratio. - const Matrix4 projection = Matrix4::PerspectiveVK(45.0f, 16.0f/9.0, 0.01f, 100.0f); - m_render_backend->Render(projection, m_render_objects); + const Matrix4 view_projection = m_render_camera.projection * m_render_camera.view; + m_render_backend->Render(view_projection, m_render_objects); m_render_backend->SwapBuffers(); }