diff --git a/App/main.cpp b/App/main.cpp index 3d5b497..c486a98 100644 --- a/App/main.cpp +++ b/App/main.cpp @@ -63,14 +63,16 @@ protected: .Compose(); m_Ground.AddComponent(MeshRendererComponent{ground}); - // 相机实体(primary = 当前渲染视角) + // 相机实体(primary = 当前渲染视角;位姿存实体 Transform) m_Camera = scene.CreateEntity("MainCamera"); auto& camera = m_Camera.AddComponent(); camera.primary = true; - camera.camera.SetPosition(glm::vec3(0.0f, 0.0f, 7.0f)); + m_Camera.GetComponent() + .SetPosition(glm::vec3(0.0f, 0.0f, 7.0f)) + .Compose(); // 神庙实体 × 1( FBX 单位较大,缩放到场景尺度) - if (0){ + { auto e = scene.CreateEntity("SunTemple"); e.GetComponent() .SetScale(glm::vec3(0.05f)) @@ -103,9 +105,11 @@ protected: m_Time += deltaTime; } - // 相机(WASD/Space/Shift 移动) + // 相机(WASD/Space/Shift 移动;位姿读写实体 Transform) m_Camera.GetComponent() - .camera.Update(GetWindow(), deltaTime); + .camera.Update(GetWindow(), + m_Camera.GetComponent(), + deltaTime); // 旋转动画:编辑 Transform 分量后 Compose 重算 world for (size_t i = 0; i < m_Spinners.size(); ++i) diff --git a/Engine/include/Engine/ECS/Scene.h b/Engine/include/Engine/ECS/Scene.h index fd66e6c..e70b113 100644 --- a/Engine/include/Engine/ECS/Scene.h +++ b/Engine/include/Engine/ECS/Scene.h @@ -86,8 +86,10 @@ namespace Engine::ECS { // ---- 常用查询 ---- - // primary 相机(渲染视角来源);无则返回 nullptr - CameraComponent* GetPrimaryCamera(); + // 主相机(primary=true,渲染视角来源);无则返回 nullptr。 + // outTransform 可选:带出该实体的 TransformComponent + //(相机位姿的单一事实源) + CameraComponent* GetPrimaryCamera(TransformComponent** outTransform = nullptr); private: Registry m_Registry; diff --git a/Engine/include/Engine/Renderer/Camera.h b/Engine/include/Engine/Renderer/Camera.h index db096c8..8b343f3 100644 --- a/Engine/include/Engine/Renderer/Camera.h +++ b/Engine/include/Engine/Renderer/Camera.h @@ -2,8 +2,11 @@ // Created by atdunbg on 2026/9/22. // // Camera:独立的相机模块。 -// 职责:输入驱动的运动更新 + 视图/投影矩阵计算。 -// 不再依赖具体渲染器——Renderer/RenderGraph 只消费它的矩阵输出。 +// +// 设计(Transform 单一事实源):相机的位置与朝向完全由实体上的 +// TransformComponent 承载——Update 把输入直接写回 transform(末尾 +// Compose),视图矩阵 = 实体世界矩阵的逆(本地 -Z 为视线方向)。 +// 相机类自身不保存任何位姿状态,杜绝"位置两套来源"的脱节。 // #ifndef HELLOVULKAN_CAMERA_H @@ -15,25 +18,20 @@ namespace Engine { class Window; + namespace ECS { struct TransformComponent; } class Camera { public: - // 每帧调用:读取窗口输入,更新位置与朝向 - void Update(Window& window, f32 deltaTime); + // 输入驱动:WASD/Space/Shift 移动 + 鼠标视角(需 SetMouseLookEnabled)。 + // 全部写回传入的 transform,有变更才 Compose。 + void Update(Window& window, ECS::TransformComponent& transform, f32 deltaTime); - glm::mat4 GetView() const; + // 相机视图 = 实体世界矩阵的逆(要求 transform 已 Compose) + glm::mat4 GetView(const ECS::TransformComponent& transform) const; // Vulkan 投影:内部完成 Y 翻转(clip space Y 向下)和 [0,1] 深度 glm::mat4 GetProjection(f32 aspect, f32 nearZ, f32 farZ) const; - glm::vec3 GetPosition() const { return m_Position; } - void SetPosition(const glm::vec3& pos) { m_Position = pos; } - - void SetYaw(f32 yaw) { m_Yaw = yaw; } - void SetPitch(f32 pitch) { m_Pitch = pitch; } - f32 GetYaw() const { return m_Yaw; } - f32 GetPitch() const { return m_Pitch; } - void SetMoveSpeed(f32 speed) { m_MoveSpeed = speed; } void SetMouseSensitivity(f32 sens) { m_MouseSensitivity = sens; } @@ -41,24 +39,10 @@ namespace Engine { void SetMouseLookEnabled(bool enabled) { m_MouseLookEnabled = enabled; } bool IsMouseLookEnabled() const { return m_MouseLookEnabled; } - glm::vec3 GetForward() const { return m_Forward; } - glm::vec3 GetRight() const { return m_Right; } - glm::vec3 GetUp() const { return m_Up; } - private: - void UpdateBasis(); - - glm::vec3 m_Position{0.0f, 0.0f, 7.0f}; - f32 m_Yaw = -90.0f; // 朝向 -Z - f32 m_Pitch = 0.0f; f32 m_MoveSpeed = 2.5f; f32 m_MouseSensitivity = 0.1f; bool m_MouseLookEnabled = false; - - // 缓存的方向基向量,Update 时刷新,避免每帧三角函数重复计算 - glm::vec3 m_Forward{0.0f, 0.0f, -1.0f}; - glm::vec3 m_Right{1.0f, 0.0f, 0.0f}; - glm::vec3 m_Up{0.0f, 1.0f, 0.0f}; }; } // namespace Engine diff --git a/Engine/src/ECS/Scene.cpp b/Engine/src/ECS/Scene.cpp index eab9e58..c20b854 100644 --- a/Engine/src/ECS/Scene.cpp +++ b/Engine/src/ECS/Scene.cpp @@ -53,12 +53,20 @@ namespace Engine::ECS { m_PendingDestroy.clear(); } - CameraComponent* Scene::GetPrimaryCamera() + CameraComponent* Scene::GetPrimaryCamera(TransformComponent** outTransform) { CameraComponent* found = nullptr; - m_Registry.view().each([&](Entity, CameraComponent& cc) { - if (!found && cc.primary) found = &cc; + Entity foundEntity = entt::null; + m_Registry.view().each([&](Entity e, CameraComponent& cc) { + if (!found && cc.primary) { + found = &cc; + foundEntity = e; + } }); + if (outTransform) { + *outTransform = (found && foundEntity != entt::null) + ? &m_Registry.get(foundEntity) : nullptr; + } return found; } diff --git a/Engine/src/Renderer/Camera.cpp b/Engine/src/Renderer/Camera.cpp index 0587262..d577f5f 100644 --- a/Engine/src/Renderer/Camera.cpp +++ b/Engine/src/Renderer/Camera.cpp @@ -4,6 +4,7 @@ #include #include +#include #include @@ -11,9 +12,11 @@ namespace Engine { - void Camera::Update(Window& window, f32 deltaTime) + void Camera::Update(Window& window, ECS::TransformComponent& t, f32 deltaTime) { - // 鼠标旋转(需显式启用,配合 cursor capture 使用) + bool dirty = false; + + // 鼠标旋转:直接写实体旋转(yaw → rotation.y,pitch → rotation.x) if (m_MouseLookEnabled) { f32 mouseDX = 0, mouseDY = 0; @@ -21,37 +24,39 @@ namespace Engine { if (mouseDX != 0 || mouseDY != 0) { - m_Yaw += mouseDX * m_MouseSensitivity; - m_Pitch -= mouseDY * m_MouseSensitivity; - m_Pitch = glm::clamp(m_Pitch, -89.0f, 89.0f); + t.rotation.y += mouseDX * m_MouseSensitivity; + t.rotation.x = glm::clamp( + t.rotation.x - mouseDY * m_MouseSensitivity, -89.0f, 89.0f); + dirty = true; } } - UpdateBasis(); + // 基向量由实体旋转决定(本地 -Z 为视线方向;应用顺序 Y→X, + // 与 TransformComponent::Compose 一致;roll 不参与 FPS 相机) + const glm::mat3 r = static_cast( + glm::rotate(glm::mat4(1.0f), glm::radians(t.rotation.y), + glm::vec3(0, 1, 0)) * + glm::rotate(glm::mat4(1.0f), glm::radians(t.rotation.x), + glm::vec3(1, 0, 0))); + const glm::vec3 forward = r * glm::vec3(0, 0, -1); + const glm::vec3 right = r * glm::vec3(1, 0, 0); f32 speed = m_MoveSpeed * deltaTime; - if (window.IsKeyPressed(GLFW_KEY_W)) m_Position += m_Forward * speed; - if (window.IsKeyPressed(GLFW_KEY_S)) m_Position -= m_Forward * speed; - if (window.IsKeyPressed(GLFW_KEY_A)) m_Position -= m_Right * speed; - if (window.IsKeyPressed(GLFW_KEY_D)) m_Position += m_Right * speed; - if (window.IsKeyPressed(GLFW_KEY_SPACE)) m_Position.y += speed; - if (window.IsKeyPressed(GLFW_KEY_LEFT_SHIFT)) m_Position.y -= speed; + if (window.IsKeyPressed(GLFW_KEY_W)) { t.position += forward * speed; dirty = true; } + if (window.IsKeyPressed(GLFW_KEY_S)) { t.position -= forward * speed; dirty = true; } + if (window.IsKeyPressed(GLFW_KEY_A)) { t.position -= right * speed; dirty = true; } + if (window.IsKeyPressed(GLFW_KEY_D)) { t.position += right * speed; dirty = true; } + if (window.IsKeyPressed(GLFW_KEY_SPACE)) { t.position.y += speed; dirty = true; } + if (window.IsKeyPressed(GLFW_KEY_LEFT_SHIFT)) { t.position.y -= speed; dirty = true; } + + // 世界矩阵供 GetView(inverse)与渲染消费 + if (dirty) t.Compose(); } - void Camera::UpdateBasis() + glm::mat4 Camera::GetView(const ECS::TransformComponent& t) const { - m_Forward.x = cosf(glm::radians(m_Yaw)) * cosf(glm::radians(m_Pitch)); - m_Forward.y = sinf(glm::radians(m_Pitch)); - m_Forward.z = sinf(glm::radians(m_Yaw)) * cosf(glm::radians(m_Pitch)); - m_Forward = glm::normalize(m_Forward); - - m_Right = glm::normalize(glm::cross(m_Forward, glm::vec3(0, 1, 0))); - m_Up = glm::normalize(glm::cross(m_Right, m_Forward)); - } - - glm::mat4 Camera::GetView() const - { - return glm::lookAt(m_Position, m_Position + m_Forward, m_Up); + // 相机作为实体:视图 = 世界矩阵的逆(无重复位姿状态) + return glm::inverse(t.world); } glm::mat4 Camera::GetProjection(f32 aspect, f32 nearZ, f32 farZ) const diff --git a/Engine/src/Renderer/SceneRenderer.cpp b/Engine/src/Renderer/SceneRenderer.cpp index 6d96ca1..5a4c439 100644 --- a/Engine/src/Renderer/SceneRenderer.cpp +++ b/Engine/src/Renderer/SceneRenderer.cpp @@ -136,9 +136,10 @@ namespace Engine ubo.prevView = m_LastView; ubo.prevProj = m_LastProjNoJitter; - // ---- 1. 相机 ---- - auto* cameraComp = scene.GetPrimaryCamera(); - if (!cameraComp) { + // ---- 1. 相机(位姿单一事实源 = 相机实体的 Transform)---- + TransformComponent* camTransform = nullptr; + auto* cameraComp = scene.GetPrimaryCamera(&camTransform); + if (!cameraComp || !camTransform) { // 无相机:本帧跳过(正常场景必有相机,安全网) return false; } @@ -146,7 +147,7 @@ namespace Engine const f32 aspect = static_cast(targetExtent.width) / static_cast(targetExtent.height); - ubo.view = camera.GetView(); + ubo.view = camera.GetView(*camTransform); ubo.proj = camera.GetProjection(aspect, 0.1f, 100.0f); // 未抖动投影:velocity 的当前帧 clip 位置用它(抖动不能进 velocity) @@ -184,7 +185,7 @@ namespace Engine ubo.lights[i].color = glm::vec4(0.0f); } ComputeCascades(shadowDir); - ubo.cameraPos = glm::vec4(camera.GetPosition(), 1.0f); + ubo.cameraPos = glm::vec4(camTransform->position, 1.0f); // ---- 3. GPUDriven:单趟收集 + 多视图剔除 ---- // 每个实例的包围球对 4 个视锥分别测试: