diff --git a/CMakeLists.txt b/CMakeLists.txt index 2212728b..82f2b45b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -207,6 +207,7 @@ set(SCENE_SOURCES src/Scene/GameObject.cpp src/Scene/MaterialComponent.cpp src/Scene/MeshComponent.cpp + src/Scene/RenderView.cpp src/Scene/SceneBase.cpp src/Scene/Scene.cpp src/Scene/TransformComponent.cpp diff --git a/include/public/Camera/Camera.hpp b/include/public/Camera/Camera.hpp index e146b7a8..35580910 100644 --- a/include/public/Camera/Camera.hpp +++ b/include/public/Camera/Camera.hpp @@ -1,19 +1,20 @@ #ifndef _CAMERA_HPP_ #define _CAMERA_HPP_ -#include -#include +#include #include +#include #include #include +#include namespace Sleak { /// Perspective or orthographic projection mode for a Camera. /// @ingroup camera enum class ProjectionType {Perspective, Orthographic}; - /// GameObject that owns the engine's single active view/projection - /// matrices and view frustum. Position/orientation come from CameraController subclasses. + /// GameObject that describes a view: position, look target, and + /// projection. Position/orientation come from CameraController subclasses. /// /// Create one, add it to the scene, and call /// SceneBase::SetActiveCamera() so the renderer knows which view to @@ -23,12 +24,11 @@ namespace Sleak { /// ColliderComponent and RigidbodyComponent for a player character /// that collides with the world. /// - /// The view and projection matrices are static, shared state. The - /// active camera rewrites them each Update() through - /// GetMainViewMatrix(), GetMainProjectionMatrix(), - /// GetMainCameraPosition(), and GetMainViewFrustum(), which is how the - /// renderer and CullingSystem read the current view. Only one camera - /// should be active at a time. + /// Each camera keeps its own view and projection matrices. Only the + /// scene's active camera drives rendering: after the update pass the + /// scene turns it into a RenderView through BuildRenderView() and + /// submits the frame with that. Other cameras in the scene can move + /// freely without affecting what is drawn or culled. /// /// Orientation is expressed as a look target rather than a rotation. /// SetLookTarget() aims at a world point and SetDirection() aims along @@ -63,7 +63,7 @@ namespace Sleak { float fov = 60, float near = 1.0f, float far = 1000.0f); void Initialize() override; - /// Recalculates the view/projection matrices and frustum while active. + /// Recalculates this camera's view/projection matrices while active. void Update(float DeltaTime) override; void SetFieldOfView(float fov) {fieldOfView = fov;} @@ -100,44 +100,61 @@ namespace Sleak { /// Updates the viewport dimensions and recomputes the projection matrix. void OnResize(uint32_t width, uint32_t height); + const Math::Matrix4& GetViewMatrix() const { return m_view; } + const Math::Matrix4& GetProjectionMatrix() const { + return m_projection; + } + + /// Builds a RenderView from the camera's current position, target + /// and projection. + RenderView BuildRenderView() const; + + /// @deprecated Use RenderView::GetCurrent().view. static const Math::Matrix4& GetMainViewMatrix() { - return Camera::View; + return RenderView::GetCurrent().view; } + /// @deprecated Use RenderView::GetCurrent().projection. static const Math::Matrix4& GetMainProjectionMatrix() { - return Camera::Projection; + return RenderView::GetCurrent().projection; } + /// @deprecated Use RenderView::GetCurrent().position. static const Math::Vector3D& GetMainCameraPosition() { - return Camera::MainPosition; + return RenderView::GetCurrent().position; } + /// @deprecated Use RenderView::GetCurrent().frustum. static const ViewFrustum& GetMainViewFrustum() { - return Camera::s_frustum; + return RenderView::GetCurrent().frustum; } protected: - /// Rebuilds the static view matrix and view frustum from position/target/up, and begins culling for the frame. - void RecalculateViewMatrix(); - /// Rebuilds the static projection matrix from FOV/aspect/near/far or the orthographic extents. - void RecalculateProjectionMatrix(); - - float fieldOfView; - float nearPlane; - float farPlane; - float width; - float height; - - ProjectionType type = ProjectionType::Perspective; - - Math::Vector3D Position; - Math::Vector3D LookTarget; - Math::Vector3D Up = Math::Vector3D::Up(); - - static Math::Matrix4 View; - static Math::Matrix4 Projection; - static Math::Vector3D MainPosition; - static ViewFrustum s_frustum; + /// Rebuilds this camera's view matrix from position/target/up. + void RecalculateViewMatrix(); + /// Rebuilds this camera's projection matrix from FOV/aspect/near/far + /// or the orthographic extents. + void RecalculateProjectionMatrix(); + + /// LookAt matrix for the current position, target and up vector. + Math::Matrix4 ComputeViewMatrix() const; + /// Perspective or orthographic matrix for the current settings. + Math::Matrix4 ComputeProjectionMatrix() const; + + float fieldOfView; + float nearPlane; + float farPlane; + float width; + float height; + + ProjectionType type = ProjectionType::Perspective; + + Math::Vector3D Position; + Math::Vector3D LookTarget; + Math::Vector3D Up = Math::Vector3D::Up(); + + Math::Matrix4 m_view = Math::Matrix4::Identity(); + Math::Matrix4 m_projection = Math::Matrix4::Identity(); }; } diff --git a/include/public/Camera/RenderView.hpp b/include/public/Camera/RenderView.hpp new file mode 100644 index 00000000..08edd6f0 --- /dev/null +++ b/include/public/Camera/RenderView.hpp @@ -0,0 +1,45 @@ +#ifndef _RENDER_VIEW_HPP_ +#define _RENDER_VIEW_HPP_ + +#include +#include +#include +#include + +namespace Sleak { + +/// Snapshot of one camera for one frame: view, projection, eye position +/// and the frustum culling tests against. +/// +/// SceneBase builds one from its active camera after the update pass +/// and hands it to the render submission step, so cameras that are not +/// the active one never touch what gets drawn. It is a plain value, so +/// rendering a second view means building a second RenderView. +/// +/// Renderer code that has no scene to ask reads the view the frame is +/// being submitted with through GetCurrent(). +/// +/// @see Camera, SceneBase, CullingSystem +/// @ingroup camera +struct ENGINE_API RenderView { + Math::Matrix4 view = Math::Matrix4::Identity(); + Math::Matrix4 projection = Math::Matrix4::Identity(); + Math::Matrix4 viewProjection = Math::Matrix4::Identity(); + Math::Vector3D position = Math::Vector3D(0, 0, 0); + ViewFrustum frustum{}; + + /// Builds a view from row-vector matrices, deriving viewProjection + /// and the frustum. + static RenderView Create(const Math::Matrix4& view, + const Math::Matrix4& projection, + const Math::Vector3D& position); + + /// The view the current frame is being submitted with. + static const RenderView& GetCurrent(); + /// Makes view the one GetCurrent() returns until the next call. + static void SetCurrent(const RenderView& view); +}; + +} // namespace Sleak + +#endif diff --git a/include/public/Core/GameObject.hpp b/include/public/Core/GameObject.hpp index 19085363..7c732129 100644 --- a/include/public/Core/GameObject.hpp +++ b/include/public/Core/GameObject.hpp @@ -122,6 +122,8 @@ namespace Sleak { virtual void Update(float deltaTime); virtual void FixedUpdate(float fixedDeltaTime); virtual void LateUpdate(float deltaTime); + /// Calls SubmitRender on every component, then on active children. + virtual void SubmitRender(const RenderView& view); /// Enables or disables the object, firing OnEnable/OnDisable on its components. void SetActive(bool active); diff --git a/include/public/Core/SceneBase.hpp b/include/public/Core/SceneBase.hpp index f8cc161f..3599b45f 100644 --- a/include/public/Core/SceneBase.hpp +++ b/include/public/Core/SceneBase.hpp @@ -1,11 +1,11 @@ #ifndef _SCENE_BASE_HPP_ #define _SCENE_BASE_HPP_ -#include -#include +#include #include +#include #include - +#include namespace Sleak { @@ -94,7 +94,8 @@ namespace Sleak { virtual void Begin() = 0; // Update loops - /// Advances all active, root-level objects by deltaTime, then steps lighting and physics. + /// Advances all active, root-level objects and physics, builds the + /// frame's RenderView from the active camera, then submits rendering. virtual void Update(float deltaTime) = 0; /// Advances all active, root-level objects on the fixed timestep. virtual void FixedUpdate(float fixedDeltaTime); @@ -141,7 +142,11 @@ namespace Sleak { size_t GetObjectCount() const { return Objects.GetSize(); } Camera* GetActiveCamera() const { return m_activeCamera; } + /// Picks the camera the scene renders and culls from. void SetActiveCamera(Camera* cam) { m_activeCamera = cam; } + /// The view this frame was submitted with, built from the active + /// camera. + const RenderView& GetRenderView() const { return m_renderView; } LightManager* GetLightManager() const { return m_lightManager; @@ -165,11 +170,15 @@ namespace Sleak { List m_pendingDestroy; Camera* m_activeCamera = nullptr; + RenderView m_renderView; LightManager* m_lightManager = nullptr; Physics::PhysicsWorld* m_physicsWorld = nullptr; Skybox* m_skybox = nullptr; + /// Queues draw work for every active object, lights, skybox and + /// debug lines, seen from view. + void SubmitRender(const RenderView& view); /// Actually deletes objects queued by DestroyObject(). void ProcessPendingDestroy(); /// Destroys every object still owned by the scene, e.g. during Unload(). diff --git a/include/public/Culling/CullingSystem.hpp b/include/public/Culling/CullingSystem.hpp index 1db40b73..10f3057d 100644 --- a/include/public/Culling/CullingSystem.hpp +++ b/include/public/Culling/CullingSystem.hpp @@ -15,15 +15,15 @@ namespace Sleak { /// game-submitted occluder volumes. Backend-agnostic (no GPU work). /// /// Frame protocol: -/// 1. BeginFrame(...) once per frame after camera update -/// (the engine calls this automatically from the main camera) +/// 1. BeginFrame(...) once per frame after the update pass +/// (SceneBase calls this with the active camera's RenderView) /// 2. SubmitOccluderBox/Triangles any number of world-space occluders /// 3. FinalizeOccluders() sort by distance, rasterize budget /// 4. IsVisible(aabb) frustum + occlusion query /// Steps 2-3 are optional; IsVisible degrades to frustum-only. /// -/// Everything here is static and lives for the process. The main camera -/// calls BeginFrame() for you during its update, so a game that only wants +/// Everything here is static and lives for the process. The scene calls +/// BeginFrame() for you from its active camera, so a game that only wants /// frustum culling can call IsVisible() and stop reading here. /// /// Occlusion culling is the part you opt into. Submit occluder volumes diff --git a/include/public/ECS/Component.hpp b/include/public/ECS/Component.hpp index a10a52ea..fb4abdad 100644 --- a/include/public/ECS/Component.hpp +++ b/include/public/ECS/Component.hpp @@ -7,6 +7,7 @@ namespace Sleak { class GameObject; + struct RenderView; /// Base for behavior attached to a GameObject. Owner deletes it via /// GameObject::RemoveComponent(), never directly. /// @@ -24,7 +25,9 @@ namespace Sleak { /// Lifecycle: the object calls Initialize() once (immediately if the /// object is already initialized when the component is attached, and /// otherwise during the object's own Initialize()), then OnEnable() if - /// the object is active, then Update() every frame. + /// the object is active, then Update() every frame. Components that + /// draw override SubmitRender(), which the scene calls once per frame + /// with the active camera's RenderView after all updates have run. /// /// @code{.cpp} /// class SpinComponent : public Sleak::Component { @@ -67,6 +70,9 @@ namespace Sleak { virtual void Update(float deltaTime) = 0; virtual void FixedUpdate(float fixedDeltaTime) {} virtual void LateUpdate(float deltaTime) {} + /// Queues this component's draw work for view; runs after every + /// Update of the frame. + virtual void SubmitRender(const RenderView& view) {} /// Called when the component is detached or the owner is destroyed. virtual void OnDestroy() {} diff --git a/include/public/ECS/Components/MaterialComponent.hpp b/include/public/ECS/Components/MaterialComponent.hpp index 73106988..1865917a 100644 --- a/include/public/ECS/Components/MaterialComponent.hpp +++ b/include/public/ECS/Components/MaterialComponent.hpp @@ -7,56 +7,57 @@ #include namespace Sleak { - /// Owns and binds a Material for rendering; submits it into the draw - /// command queue each Update while enabled. - /// @ingroup scene - class ENGINE_API MaterialComponent : public Component { - public: - // Construct with a shared material (RefPtr copy - safe for sharing) - MaterialComponent(GameObject* object, - const RefPtr& material); - - // Construct with a raw pointer (takes ownership) - MaterialComponent(GameObject* object, Material* material); - - // Construct with just a diffuse color (creates a new material) - MaterialComponent( - GameObject* object, - Math::Color diffuseColor = Math::Color::White); - - // Construct with a diffuse texture path (creates a new material) - MaterialComponent(GameObject* object, - const std::string& diffuseTexture); - - ~MaterialComponent() override = default; - - /// Initializes the owned material's GPU resources. - bool Initialize() override; - /// Submits the material to the render command queue for this frame. - void Update(float DeltaTime) override; - void OnEnable() override; - void OnDisable() override; - - // Material access - /// Replaces the material, re-initializing it if the component is already initialized. - void SetMaterial(const RefPtr& material); - /// Takes ownership of a raw material pointer, re-initializing it if already initialized. - void SetMaterial(Material* material); - const RefPtr& GetMaterial() const; - Material* GetMaterialRaw() const; - - // Convenience shortcuts that forward to the material - void SetDiffuseColor(Math::Color color); - void SetDiffuseTexture(const std::string& path); - void SetNormalTexture(const std::string& path); - void SetShininess(float shininess); - void SetMetallic(float metallic); - void SetRoughness(float roughness); - - private: - RefPtr m_material; - bool m_enabled = true; - }; +/// Owns and binds a Material for rendering; submits it into the draw +/// command queue each frame from SubmitRender while enabled. +/// @ingroup scene +class ENGINE_API MaterialComponent : public Component { + public: + // Construct with a shared material (RefPtr copy - safe for sharing) + MaterialComponent(GameObject* object, const RefPtr& material); + + // Construct with a raw pointer (takes ownership) + MaterialComponent(GameObject* object, Material* material); + + // Construct with just a diffuse color (creates a new material) + MaterialComponent(GameObject* object, + Math::Color diffuseColor = Math::Color::White); + + // Construct with a diffuse texture path (creates a new material) + MaterialComponent(GameObject* object, const std::string& diffuseTexture); + + ~MaterialComponent() override = default; + + /// Initializes the owned material's GPU resources. + bool Initialize() override; + /// Nothing per frame; binding happens in SubmitRender. + void Update(float DeltaTime) override; + /// Submits the material to the render command queue for this frame. + void SubmitRender(const RenderView& view) override; + void OnEnable() override; + void OnDisable() override; + + // Material access + /// Replaces the material, re-initializing it if the component is already + /// initialized. + void SetMaterial(const RefPtr& material); + /// Takes ownership of a raw material pointer, re-initializing it if already + /// initialized. + void SetMaterial(Material* material); + const RefPtr& GetMaterial() const; + Material* GetMaterialRaw() const; + + // Convenience shortcuts that forward to the material + void SetDiffuseColor(Math::Color color); + void SetDiffuseTexture(const std::string& path); + void SetNormalTexture(const std::string& path); + void SetShininess(float shininess); + void SetMetallic(float metallic); + void SetRoughness(float roughness); + + private: + RefPtr m_material; + bool m_enabled = true; +}; } #endif diff --git a/include/public/ECS/Components/MeshComponent.hpp b/include/public/ECS/Components/MeshComponent.hpp index 1da11242..4ff102e2 100644 --- a/include/public/ECS/Components/MeshComponent.hpp +++ b/include/public/ECS/Components/MeshComponent.hpp @@ -21,7 +21,8 @@ namespace Sleak { }; /// Holds the GPU vertex/index/constant buffers for a renderable mesh and - /// submits a draw call each Update, subject to frustum culling. + /// submits a draw call each frame from SubmitRender, subject to frustum + /// culling. /// @ingroup scene class ENGINE_API MeshComponent : public Component { public: @@ -32,8 +33,10 @@ namespace Sleak { /// Validates that vertex and index buffers were created successfully. virtual bool Initialize() override; - /// Culls against cull bounds if set, then submits an indexed draw call. + /// Nothing per frame; drawing happens in SubmitRender. virtual void Update(float deltaTime) override; + /// Culls against cull bounds if set, then submits an indexed draw call. + void SubmitRender(const RenderView& view) override; void SetVertexBuffer(RefPtr& buffer); diff --git a/include/public/ECS/Components/TransformComponent.hpp b/include/public/ECS/Components/TransformComponent.hpp index 37307a81..978e8a56 100644 --- a/include/public/ECS/Components/TransformComponent.hpp +++ b/include/public/ECS/Components/TransformComponent.hpp @@ -35,8 +35,11 @@ namespace Sleak { /// Allocates the transform's GPU constant buffer. bool Initialize() override; - /// Refreshes and rebinds the transform constant buffer for this frame. + /// Refreshes the cached transform matrix. void Update(float DeltaTime) override; + /// Uploads world/view/projection for view and binds the constant + /// buffer. + void SubmitRender(const RenderView& view) override; // Transformation matrix calculation Matrix4 GetTransformMatrix(); @@ -99,11 +102,13 @@ namespace Sleak { private: RefPtr ConstantBuffer; - /// Rebuilds the transform matrix and pushes it to the GPU constant buffer. - void UpdateConstantBuffer(); + /// Rebuilds the transform matrix and pushes it with view's matrices + /// to the GPU constant buffer. + void UpdateConstantBuffer(const RenderView& view); /// Recomputes the cached Translation/Rotation/Scaling matrices from position/rotation/scale. void UpdateTransform(); - /// Combines the transform with the main camera's view and projection matrices. + /// Combines the transform with the current render view's + /// view-projection. Math::Matrix4 GetMVP(); }; diff --git a/include/public/Lighting/LightManager.hpp b/include/public/Lighting/LightManager.hpp index 02887700..5d752e0a 100644 --- a/include/public/Lighting/LightManager.hpp +++ b/include/public/Lighting/LightManager.hpp @@ -9,6 +9,7 @@ namespace Sleak { class Light; + struct RenderView; namespace RenderEngine { class BufferBase; @@ -66,10 +67,16 @@ namespace Sleak { void UnregisterLight(Light* light); /// Packs every registered light and the fog/ambient parameters into the light buffer and binds it. + void UpdateAndBind(const RenderView& view); + /// Same as UpdateAndBind(view) using RenderView::GetCurrent(). void UpdateAndBind(); /// Picks the active shadow-casting light and refreshes its shadow-space matrices. + void UpdateShadowData(const RenderView& view); + /// Same as UpdateShadowData(view) using RenderView::GetCurrent(). void UpdateShadowData(); /// Refreshes the deferred-pass constant buffer (fog, ambient) independent of the per-light data. + void UpdateDeferredCB(const RenderView& view); + /// Same as UpdateDeferredCB(view) using RenderView::GetCurrent(). void UpdateDeferredCB(); void SetAmbientColor(float r, float g, float b); diff --git a/include/public/Runtime/Skybox.hpp b/include/public/Runtime/Skybox.hpp index 14002284..1f35118c 100644 --- a/include/public/Runtime/Skybox.hpp +++ b/include/public/Runtime/Skybox.hpp @@ -13,6 +13,7 @@ namespace Sleak { class Shader; } class Texture; + struct RenderView; /// Renders the background sky as a cubemap, panorama, or procedural gradient. /// @ingroup rendering @@ -38,7 +39,9 @@ namespace Sleak { /// Create GPU resources (shader, buffers, cubemap texture) void Initialize(); - /// Submit render commands for this frame + /// Submit render commands for this frame, seen from view + void Render(const RenderView& view); + /// Same as Render(view) using RenderView::GetCurrent() void Render(); bool IsInitialized() const { return m_initialized; } diff --git a/src/Assets/Skybox.cpp b/src/Assets/Skybox.cpp index 806f9780..f2829e34 100644 --- a/src/Assets/Skybox.cpp +++ b/src/Assets/Skybox.cpp @@ -1,13 +1,12 @@ -#include -#include -#include +#include +#include +#include #include #include -#include +#include #include -#include -#include - +#include +#include #include namespace Sleak { @@ -120,17 +119,19 @@ void Skybox::Initialize() { m_mode == SkyboxMode::Gradient ? "gradient" : "default"); } -void Skybox::Render() { +void Skybox::Render() { Render(RenderView::GetCurrent()); } + +void Skybox::Render(const RenderView& renderView) { if (!m_initialized) return; if (!m_shader.IsValid()) return; // Build view-projection matrix with translation zeroed out - Math::Matrix4 view = Camera::GetMainViewMatrix(); + Math::Matrix4 view = renderView.view; view(3, 0) = 0.0f; view(3, 1) = 0.0f; view(3, 2) = 0.0f; - Math::Matrix4 proj = Camera::GetMainProjectionMatrix(); + const Math::Matrix4& proj = renderView.projection; Math::Matrix4 viewProj = view * proj; // Update constant buffer data diff --git a/src/Debug/DebugLineRenderer.cpp b/src/Debug/DebugLineRenderer.cpp index 18c193d4..e7206603 100644 --- a/src/Debug/DebugLineRenderer.cpp +++ b/src/Debug/DebugLineRenderer.cpp @@ -1,10 +1,10 @@ +#include +#include #include -#include -#include #include #include -#include -#include +#include +#include #include namespace Sleak { @@ -205,7 +205,7 @@ void DebugLineRenderer::Flush(Camera* camera) { // Update constant buffer with ViewProjection DebugLineCBData cbData; - cbData.ViewProjection = Camera::GetMainViewMatrix() * Camera::GetMainProjectionMatrix(); + cbData.ViewProjection = RenderView::GetCurrent().viewProjection; s_constantBuffer->Update(&cbData, sizeof(DebugLineCBData)); // Update vertex buffer diff --git a/src/Graphics/Common/MeshBatch.cpp b/src/Graphics/Common/MeshBatch.cpp index 0e98095e..dc930d59 100644 --- a/src/Graphics/Common/MeshBatch.cpp +++ b/src/Graphics/Common/MeshBatch.cpp @@ -1,10 +1,11 @@ +#include +#include #include -#include "../../include/private/Graphics/Common/ResourceManager.hpp" -#include "../../include/private/Graphics/Common/RenderCommandQueue.hpp" -#include "../../include/private/Graphics/Common/ConstantBuffer.hpp" + #include "../../include/private/Graphics/Common/BufferBase.hpp" -#include -#include +#include "../../include/private/Graphics/Common/ConstantBuffer.hpp" +#include "../../include/private/Graphics/Common/RenderCommandQueue.hpp" +#include "../../include/private/Graphics/Common/ResourceManager.hpp" namespace Sleak { @@ -73,9 +74,8 @@ void MeshBatch::BeginBatch(Material* material) { // Build identity world matrix — batch vertices are already in world space auto world = Math::Matrix4::Identity(); - RenderEngine::TransformBuffer tb(world, - Camera::GetMainViewMatrix(), - Camera::GetMainProjectionMatrix()); + const RenderView& view = RenderView::GetCurrent(); + RenderEngine::TransformBuffer tb(world, view.view, view.projection); if (!s_batchTransformBuffer) { s_batchTransformBuffer = RefPtr( diff --git a/src/Graphics/OpenGL/OpenGLRenderer.cpp b/src/Graphics/OpenGL/OpenGLRenderer.cpp index 58da1d67..27339abe 100644 --- a/src/Graphics/OpenGL/OpenGLRenderer.cpp +++ b/src/Graphics/OpenGL/OpenGLRenderer.cpp @@ -1,25 +1,28 @@ #include "../../include/private/Graphics/OpenGL/OpenGLRenderer.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include "../../include/private/Graphics/OpenGL/OpenGLBuffer.hpp" -#include "../../include/private/Graphics/OpenGL/OpenGLShader.hpp" -#include "../../include/private/Graphics/OpenGL/OpenGLTexture.hpp" #include "../../include/private/Graphics/OpenGL/OpenGLCubemapTexture.hpp" #include "../../include/private/Graphics/OpenGL/OpenGLIBL.hpp" -#include -#include -#include "Graphics/Common/ResourceManager.hpp" +#include "../../include/private/Graphics/OpenGL/OpenGLShader.hpp" +#include "../../include/private/Graphics/OpenGL/OpenGLTexture.hpp" #include "Graphics/Common/ConstantBuffer.hpp" #include "Graphics/Common/RenderCommandQueue.hpp" +#include "Graphics/Common/ResourceManager.hpp" #include "Graphics/Common/SSAOKernel.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include namespace Sleak { namespace RenderEngine { @@ -1293,8 +1296,8 @@ void OpenGLRenderer::ExecuteSSAOPass() { glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(s), &s); // ---- Upload Camera UBO (Projection, View, InvViewProj) ---- - const Math::Matrix4& V = Camera::GetMainViewMatrix(); - const Math::Matrix4& P = Camera::GetMainProjectionMatrix(); + const Math::Matrix4& V = RenderView::GetCurrent().view; + const Math::Matrix4& P = RenderView::GetCurrent().projection; float camData[16 * 3]; std::memcpy(camData, &P(0,0), sizeof(float) * 16); std::memcpy(camData + 16, &V(0,0), sizeof(float) * 16); diff --git a/src/Graphics/Vulkan/VulkanDeferred.cpp b/src/Graphics/Vulkan/VulkanDeferred.cpp index 201676ce..d1877a40 100644 --- a/src/Graphics/Vulkan/VulkanDeferred.cpp +++ b/src/Graphics/Vulkan/VulkanDeferred.cpp @@ -1,14 +1,14 @@ -#include "../../include/private/Graphics/Vulkan/VulkanRenderer.hpp" -#include "../../include/private/Graphics/Vulkan/VulkanTexture.hpp" -#include "../../include/private/Graphics/Vulkan/VulkanInternal.hpp" - #include #include #include #include #include + +#include "../../include/private/Graphics/Vulkan/VulkanInternal.hpp" +#include "../../include/private/Graphics/Vulkan/VulkanRenderer.hpp" +#include "../../include/private/Graphics/Vulkan/VulkanTexture.hpp" +#include "Camera/RenderView.hpp" #include "Core/Logger.hpp" -#include "Camera/Camera.hpp" #include "Math/Matrix.hpp" namespace Sleak { @@ -1285,8 +1285,8 @@ void VulkanRenderer::UpdateDeferredCB(const void* data, uint32_t size) { memcpy(m_deferredCBMapped[currentFrame], data, copySize); // Snapshot current camera View / Projection for SSAO/SSR/TAA UBO population. - const Math::Matrix4& V = Camera::GetMainViewMatrix(); - const Math::Matrix4& P = Camera::GetMainProjectionMatrix(); + const Math::Matrix4& V = RenderView::GetCurrent().view; + const Math::Matrix4& P = RenderView::GetCurrent().projection; memcpy(m_cachedView, &V(0, 0), sizeof(m_cachedView)); memcpy(m_cachedProjection, &P(0, 0), sizeof(m_cachedProjection)); diff --git a/src/Graphics/Vulkan/VulkanSSR.cpp b/src/Graphics/Vulkan/VulkanSSR.cpp index 01b0adf0..aeb82744 100644 --- a/src/Graphics/Vulkan/VulkanSSR.cpp +++ b/src/Graphics/Vulkan/VulkanSSR.cpp @@ -1,9 +1,9 @@ -#include "../../include/private/Graphics/Vulkan/VulkanRenderer.hpp" - -#include +#include #include #include #include + +#include "../../include/private/Graphics/Vulkan/VulkanRenderer.hpp" #include "Core/Logger.hpp" namespace Sleak { @@ -454,7 +454,7 @@ void VulkanRenderer::UpdateSSRUBO() { memcpy(p.Projection, m_cachedProjection, sizeof(p.Projection)); memcpy(p.InvViewProj, m_cachedInvViewProj, sizeof(p.InvViewProj)); - const auto& camPos = Camera::GetMainCameraPosition(); + const auto& camPos = RenderView::GetCurrent().position; p.CameraPos[0] = camPos.GetX(); p.CameraPos[1] = camPos.GetY(); p.CameraPos[2] = camPos.GetZ(); diff --git a/src/Lighting/LightManager.cpp b/src/Lighting/LightManager.cpp index b4517184..36f6146d 100644 --- a/src/Lighting/LightManager.cpp +++ b/src/Lighting/LightManager.cpp @@ -1,20 +1,20 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include #include -#include -#include #include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include namespace { /// 4x4 row-major matrix inverse via cofactors (Cramer's rule). @@ -93,13 +93,15 @@ void LightManager::UnregisterLight(Light* light) { } } -void LightManager::UpdateAndBind() { +void LightManager::UpdateAndBind() { UpdateAndBind(RenderView::GetCurrent()); } + +void LightManager::UpdateAndBind(const RenderView& view) { if (!m_lightBuffer) return; RenderEngine::LightCBData cbData{}; // Camera position - const auto& camPos = Camera::GetMainCameraPosition(); + const auto& camPos = view.position; cbData.CameraPosX = camPos.GetX(); cbData.CameraPosY = camPos.GetY(); cbData.CameraPosZ = camPos.GetZ(); @@ -155,13 +157,17 @@ void LightManager::UpdateAndBind() { m_lightBuffer->Update(); // Update shadow data for Vulkan renderer - UpdateShadowData(); + UpdateShadowData(view); // Update deferred CB (InvViewProj + screen size) for the lighting pass - UpdateDeferredCB(); + UpdateDeferredCB(view); } void LightManager::UpdateShadowData() { + UpdateShadowData(RenderView::GetCurrent()); +} + +void LightManager::UpdateShadowData(const RenderView& view) { auto* app = Application::GetInstance(); if (!app) return; auto* renderer = app->GetRenderer(); @@ -223,7 +229,7 @@ void LightManager::UpdateShadowData() { // Light position: follow camera XZ but fix Y at world origin. // Anchoring Y prevents the shadow frustum from shifting vertically // when the player jumps/flies, which causes hard Z-plane cutoff flicker. - const auto& camPos = Camera::GetMainCameraPosition(); + const auto& camPos = view.position; Math::Vector3D lightPos = Math::Vector3D(camPos.GetX(), 0.0f, camPos.GetZ()) + dir * (-shadowDist); @@ -315,7 +321,7 @@ void LightManager::UpdateShadowData() { renderer->SetLightVP(&lightVP(0, 0)); // Build shadow light UBO - const auto& camPos = Camera::GetMainCameraPosition(); + const auto& camPos = view.position; RenderEngine::ShadowLightUBO ubo{}; ubo.LightDir[0] = dir.GetX(); @@ -355,9 +361,7 @@ void LightManager::UpdateShadowData() { // per-fragment path shimmers under camera rotation). Uses the same // LightVP the UBO carries (prev frame — matches the bound shadow map). { - const Math::Matrix4& camV = Camera::GetMainViewMatrix(); - const Math::Matrix4& camP = Camera::GetMainProjectionMatrix(); - Math::Matrix4 camVP = camV * camP; + const Math::Matrix4& camVP = view.viewProjection; float invVP[16]; if (Invert4x4(&camVP(0, 0), invVP)) { Math::Matrix4 invVPm, lightVPm; @@ -432,6 +436,10 @@ void LightManager::SetAmbientColor(float r, float g, float b) { } void LightManager::UpdateDeferredCB() { + UpdateDeferredCB(RenderView::GetCurrent()); +} + +void LightManager::UpdateDeferredCB(const RenderView& view) { auto* app = Application::GetInstance(); if (!app) return; auto* renderer = app->GetRenderer(); @@ -440,9 +448,7 @@ void LightManager::UpdateDeferredCB() { if (!ctx || !ctx->IsDeferredEnabled()) return; // ViewProj = View * Proj (row-major engine convention) - const Math::Matrix4& V = Camera::GetMainViewMatrix(); - const Math::Matrix4& P = Camera::GetMainProjectionMatrix(); - Math::Matrix4 VP = V * P; + const Math::Matrix4& VP = view.viewProjection; RenderEngine::DeferredCBData cb{}; if (!Invert4x4(&VP(0, 0), cb.InvViewProj)) { diff --git a/src/Scene/Camera.cpp b/src/Scene/Camera.cpp index 157041d5..1855b170 100644 --- a/src/Scene/Camera.cpp +++ b/src/Scene/Camera.cpp @@ -1,16 +1,10 @@ #include -#include +#include #include #include -#include namespace Sleak { - Math::Matrix4 Camera::View = Math::Matrix4::Identity(); - Math::Matrix4 Camera::Projection = Math::Matrix4::Identity(); - Math::Vector3D Camera::MainPosition = Math::Vector3D(0, 0, 0); - ViewFrustum Camera::s_frustum; - Camera::Camera(std::string Name, Math::Vector3D Position, float Fov, float Near, float Far) : GameObject(Name) { this->Position = Position; fieldOfView = Fov; @@ -49,29 +43,30 @@ namespace Sleak { RecalculateProjectionMatrix(); } - void Camera::RecalculateViewMatrix() { - MainPosition = Position; + RenderView Camera::BuildRenderView() const { + return RenderView::Create(ComputeViewMatrix(), + ComputeProjectionMatrix(), Position); + } - View = Matrix4::LookAt(Position.BaseVector(), // Position - LookTarget.BaseVector(), // Target - Up.BaseVector()); // Up + void Camera::RecalculateViewMatrix() { m_view = ComputeViewMatrix(); } - // Update view frustum from VP = View * Projection (row-vector convention) - Math::Matrix4 VP = View * Projection; - s_frustum.ExtractFromVP(VP); + void Camera::RecalculateProjectionMatrix() { + m_projection = ComputeProjectionMatrix(); + } - CullingSystem::BeginFrame(s_frustum, VP, Position); + Math::Matrix4 Camera::ComputeViewMatrix() const { + Math::Vector3D eye = Position, target = LookTarget, up = Up; + return Matrix4::LookAt(eye.BaseVector(), target.BaseVector(), + up.BaseVector()); } - void Camera::RecalculateProjectionMatrix() { - if(type == ProjectionType::Perspective) - Projection = Matrix4::Perspective(fieldOfView * D2R, // Fov - width/height, // Aspect Ratio - nearPlane, // Near Plane - farPlane); // Far Plane - else - Projection = Matrix4::Orthographic(-width / 2.0f, width / 2.0f, - -height / 2.0f, height / 2.0f, - nearPlane, farPlane); + Math::Matrix4 Camera::ComputeProjectionMatrix() const { + if (type == ProjectionType::Perspective) + return Matrix4::Perspective(fieldOfView * D2R, width / height, + nearPlane, farPlane); + + return Matrix4::Orthographic(-width / 2.0f, width / 2.0f, + -height / 2.0f, height / 2.0f, nearPlane, + farPlane); } -} \ No newline at end of file + } // namespace Sleak diff --git a/src/Scene/GameObject.cpp b/src/Scene/GameObject.cpp index 13515ae3..74f4297f 100644 --- a/src/Scene/GameObject.cpp +++ b/src/Scene/GameObject.cpp @@ -77,6 +77,19 @@ namespace Sleak { } } + void GameObject::SubmitRender(const RenderView& view) { + if (!m_isActive || m_pendingDestroy) return; + + for (size_t i = 0; i < Components.GetSize(); ++i) { + if (Components[i]) Components[i]->SubmitRender(view); + } + + for (size_t i = 0; i < m_children.GetSize(); ++i) { + if (m_children[i] && m_children[i]->IsActive()) + m_children[i]->SubmitRender(view); + } + } + void GameObject::SetActive(bool active) { if (m_isActive == active) return; m_isActive = active; diff --git a/src/Scene/MaterialComponent.cpp b/src/Scene/MaterialComponent.cpp index 037ca3a8..3256b481 100644 --- a/src/Scene/MaterialComponent.cpp +++ b/src/Scene/MaterialComponent.cpp @@ -40,7 +40,9 @@ namespace Sleak { return true; } - void MaterialComponent::Update(float DeltaTime) { + void MaterialComponent::Update(float DeltaTime) {} + + void MaterialComponent::SubmitRender(const RenderView& view) { if (!m_enabled || !m_material) return; diff --git a/src/Scene/MeshComponent.cpp b/src/Scene/MeshComponent.cpp index 186e2d37..8f58c56b 100644 --- a/src/Scene/MeshComponent.cpp +++ b/src/Scene/MeshComponent.cpp @@ -51,7 +51,9 @@ MeshComponent::MeshComponent(GameObject* object, MeshData data) : Component(obje return true; } - void MeshComponent::Update(float deltaTime) { + void MeshComponent::Update(float deltaTime) {} + + void MeshComponent::SubmitRender(const RenderView& view) { if (!bIsInitialized) return; diff --git a/src/Scene/RenderView.cpp b/src/Scene/RenderView.cpp new file mode 100644 index 00000000..bdf0f6e9 --- /dev/null +++ b/src/Scene/RenderView.cpp @@ -0,0 +1,25 @@ +#include + +namespace Sleak { + +namespace { +RenderView s_currentView; +} + +RenderView RenderView::Create(const Math::Matrix4& view, + const Math::Matrix4& projection, + const Math::Vector3D& position) { + RenderView rv; + rv.view = view; + rv.projection = projection; + rv.viewProjection = view * projection; + rv.position = position; + rv.frustum.ExtractFromVP(rv.viewProjection); + return rv; +} + +const RenderView& RenderView::GetCurrent() { return s_currentView; } + +void RenderView::SetCurrent(const RenderView& view) { s_currentView = view; } + +} // namespace Sleak diff --git a/src/Scene/SceneBase.cpp b/src/Scene/SceneBase.cpp index 8ab146a4..1578c897 100644 --- a/src/Scene/SceneBase.cpp +++ b/src/Scene/SceneBase.cpp @@ -1,14 +1,17 @@ #include "../../include/public/Core/SceneBase.hpp" + +#include #include #include -#include +#include +#include #include #include #include -#include -#include #include -#include +#include +#include + #include "../../include/private/Graphics/Common/RenderCommandQueue.hpp" namespace Sleak { @@ -151,27 +154,37 @@ void SceneBase::Begin() { void SceneBase::Update(float deltaTime) { if (!bActive) return; - // Update objects FIRST so Camera::View/Projection reflect this frame's - // rotation/position before any render-side UBO computes InvViewProj. - // Why: LightManager::UpdateDeferredCB reads the static Camera matrices - // to build InvViewProj for the deferred lighting pass. If it ran - // before the camera object updated, InvViewProj was one frame stale - // and the lighting pass reconstructed wrong world positions on any - // camera rotation, causing whole-terrain shadow flicker. for (size_t i = 0; i < Objects.GetSize(); ++i) { if (Objects[i] && Objects[i]->IsActive() && !Objects[i]->HasParent()) { Objects[i]->Update(deltaTime); } } - if (m_lightManager) - m_lightManager->UpdateAndBind(); + if (m_physicsWorld) m_physicsWorld->Step(deltaTime); - if (m_skybox) - m_skybox->Render(); + if (m_activeCamera) { + m_renderView = m_activeCamera->BuildRenderView(); + RenderView::SetCurrent(m_renderView); + CullingSystem::BeginFrame(m_renderView.frustum, + m_renderView.viewProjection, + m_renderView.position); + } - if (m_physicsWorld) - m_physicsWorld->Step(deltaTime); + SubmitRender(RenderView::GetCurrent()); + + ProcessPendingDestroy(); +} + +void SceneBase::SubmitRender(const RenderView& view) { + for (size_t i = 0; i < Objects.GetSize(); ++i) { + if (Objects[i] && Objects[i]->IsActive() && !Objects[i]->HasParent()) { + Objects[i]->SubmitRender(view); + } + } + + if (m_lightManager) m_lightManager->UpdateAndBind(view); + + if (m_skybox) m_skybox->Render(view); if (DebugLineRenderer::IsEnabled()) { auto drawColliderShape = [](ColliderComponent* collider, const Math::Vector3D& worldPos, const Math::Vector3D& worldScale) { @@ -215,8 +228,6 @@ void SceneBase::Update(float deltaTime) { } DebugLineRenderer::Flush(m_activeCamera); - - ProcessPendingDestroy(); } void SceneBase::FixedUpdate(float fixedDeltaTime) { diff --git a/src/Scene/TransformComponent.cpp b/src/Scene/TransformComponent.cpp index e3400c68..ce3fafa5 100644 --- a/src/Scene/TransformComponent.cpp +++ b/src/Scene/TransformComponent.cpp @@ -1,10 +1,11 @@ +#include +#include +#include #include #include #include -#include -#include #include -#include +#include namespace Sleak { @@ -36,9 +37,8 @@ namespace Sleak { Transform = new TransformMatrix(); UpdateTransform(); auto world = GetTransformMatrix(); - RenderEngine::TransformBuffer tb(world, - Camera::GetMainViewMatrix(), - Camera::GetMainProjectionMatrix()); + const RenderView& view = RenderView::GetCurrent(); + RenderEngine::TransformBuffer tb(world, view.view, view.projection); ConstantBuffer = RefPtr( RenderEngine::ResourceManager::CreateBuffer( RenderEngine::BufferType::Constant, @@ -48,7 +48,11 @@ namespace Sleak { } void TransformComponent::Update(float DeltaTime) { - UpdateConstantBuffer(); + if (ConstantBuffer) UpdateTransform(); + } + + void TransformComponent::SubmitRender(const RenderView& view) { + UpdateConstantBuffer(view); RenderEngine::RenderCommandQueue::GetInstance()->SubmitBindConstantBuffer(ConstantBuffer, 0); } @@ -146,8 +150,7 @@ namespace Sleak { Transform->CalculateTransformMatrix(); } - - void TransformComponent::UpdateConstantBuffer() { + void TransformComponent::UpdateConstantBuffer(const RenderView& view) { if (!ConstantBuffer) { SLEAK_ERROR("Constant buffer is not found to update for object {}", owner->GetName()); return; @@ -156,18 +159,14 @@ namespace Sleak { UpdateTransform(); auto world = GetTransformMatrix(); - RenderEngine::TransformBuffer tb(world, - Camera::GetMainViewMatrix(), - Camera::GetMainProjectionMatrix()); + RenderEngine::TransformBuffer tb(world, view.view, view.projection); RenderEngine::RenderCommandQueue::GetInstance()->SubmitUpdateConstantBuffer(ConstantBuffer, tb.GetData(), tb.GetSize()); } - Math::Matrix4 TransformComponent::GetMVP() { - auto mat = GetTransformMatrix() * - Camera::GetMainViewMatrix() * - Camera::GetMainProjectionMatrix(); + auto mat = + GetTransformMatrix() * RenderView::GetCurrent().viewProjection; return mat; }