diff --git a/App/main.cpp b/App/main.cpp index c486a98..03eb88e 100644 --- a/App/main.cpp +++ b/App/main.cpp @@ -58,9 +58,8 @@ protected: const auto ground = GetAssets().CreatePlane(15.0f, 140, 140, 145); m_Ground = scene.CreateEntity("Ground"); - m_Ground.GetComponent() // 出生即有,直接取 - .SetPosition(glm::vec3(0.0f, -0.5f, 0.0f)) - .Compose(); + m_Ground.GetComponent() + .SetPosition(glm::vec3(0.0f, -0.5f, 0.0f)); // dirty → 帧循环统一 Compose m_Ground.AddComponent(MeshRendererComponent{ground}); // 相机实体(primary = 当前渲染视角;位姿存实体 Transform) @@ -68,15 +67,13 @@ protected: auto& camera = m_Camera.AddComponent(); camera.primary = true; m_Camera.GetComponent() - .SetPosition(glm::vec3(0.0f, 0.0f, 7.0f)) - .Compose(); + .SetPosition(glm::vec3(0.0f, 0.0f, 7.0f)); // 神庙实体 × 1( FBX 单位较大,缩放到场景尺度) { auto e = scene.CreateEntity("SunTemple"); e.GetComponent() - .SetScale(glm::vec3(0.05f)) - .Compose(); + .SetScale(glm::vec3(0.05f)); e.AddComponent(MeshRendererComponent{temple}); } @@ -116,8 +113,7 @@ protected: { auto& t = m_Spinners[i].GetComponent(); t.rotation.y = m_Time * 60.0f + static_cast(i) * 40.0f; - t.scale = glm::vec3(0.5f); - t.Compose(); + t.scale = glm::vec3(0.5f); // dirty → 帧循环统一 Compose } } diff --git a/Engine/include/Engine/ECS/Components.h b/Engine/include/Engine/ECS/Components.h index ebf5837..54b0495 100644 --- a/Engine/include/Engine/ECS/Components.h +++ b/Engine/include/Engine/ECS/Components.h @@ -34,8 +34,10 @@ namespace Engine::ECS { std::string name; }; - // 世界变换:可编辑的 SRT 分量 + 派生的世界矩阵。 - // 编辑分量后调用 Compose() 重算 world;渲染热路径只读 world。 + // 世界变换:可编辑的 SRT 分量 + 派生的 world 矩阵。 + // 分量可直接读写(含直接改字段);world 由帧循环的 + // Scene::UpdateTransforms() 全量重算(UE transform-update 阶段 + // 模式),调用方永远不需要手动 Compose。渲染热路径只读 world。 // 层级(父子变换)留给后续的层级同步系统。 struct TransformComponent { glm::vec3 position{0.0f}; @@ -47,7 +49,8 @@ namespace Engine::ECS { TransformComponent& SetRotation(const glm::vec3& r) { rotation = r; return *this; } TransformComponent& SetScale(const glm::vec3& s) { scale = s; return *this; } - // 分量 → world 矩阵(T * R * S) + // SRT 分量 → world 矩阵(T * R * S)。 + // 由 UpdateTransforms 每帧全量调用;勿在用户代码手动调用 TransformComponent& Compose() { const glm::mat4 t = glm::translate(glm::mat4(1.0f), position); diff --git a/Engine/include/Engine/ECS/Scene.h b/Engine/include/Engine/ECS/Scene.h index e70b113..2d5e9de 100644 --- a/Engine/include/Engine/ECS/Scene.h +++ b/Engine/include/Engine/ECS/Scene.h @@ -51,6 +51,11 @@ namespace Engine::ECS { EntityHandle CreateEntity(std::string name = {}); void DestroyEntity(Entity e); // 延迟:帧尾统一销毁 void FlushDestroyQueue(); // 每帧末尾由 Application 调用 + + // 帧循环统一重算脏 transform 的 world 矩阵(UE transform-update + // 阶段模式)——由 Application 在 OnUpdate 之后、渲染之前调用, + // 用户代码永远不需要手动 Compose + void UpdateTransforms(); bool IsAlive(Entity e) const { return m_Registry.valid(e); } // 按稳定 UUID 查实体(序列化/跨引用场景);找不到返回 entt::null diff --git a/Engine/src/Core/Application.cpp b/Engine/src/Core/Application.cpp index 397a8fd..cf900cd 100644 --- a/Engine/src/Core/Application.cpp +++ b/Engine/src/Core/Application.cpp @@ -96,6 +96,7 @@ namespace Engine { // 延迟销毁在渲染前统一 flush,保证渲染遍历时池结构不变 m_Scene->FlushDestroyQueue(); + m_Scene->UpdateTransforms(); // UE transform-update 阶段:渲染前保证 world 新鲜 const auto uiStart = std::chrono::steady_clock::now(); diff --git a/Engine/src/ECS/Scene.cpp b/Engine/src/ECS/Scene.cpp index c20b854..f1fdbe2 100644 --- a/Engine/src/ECS/Scene.cpp +++ b/Engine/src/ECS/Scene.cpp @@ -53,6 +53,16 @@ namespace Engine::ECS { m_PendingDestroy.clear(); } + void Scene::UpdateTransforms() + { + // 全量重算:分量允许直接写字段(无法可靠 dirty 追踪), + // 无条件 Compose 保证任何写法下 world 都新鲜。 + // 实测万级实体内成本可忽略;更大规模再引入 dirty+封装优化 + m_Registry.view().each([](TransformComponent& t) { + t.Compose(); + }); + } + CameraComponent* Scene::GetPrimaryCamera(TransformComponent** outTransform) { CameraComponent* found = nullptr; diff --git a/Engine/src/Renderer/Camera.cpp b/Engine/src/Renderer/Camera.cpp index d577f5f..f5f8fce 100644 --- a/Engine/src/Renderer/Camera.cpp +++ b/Engine/src/Renderer/Camera.cpp @@ -14,9 +14,8 @@ namespace Engine { void Camera::Update(Window& window, ECS::TransformComponent& t, f32 deltaTime) { - bool dirty = false; - - // 鼠标旋转:直接写实体旋转(yaw → rotation.y,pitch → rotation.x) + // 鼠标旋转:直接写实体旋转(yaw → rotation.y,pitch → rotation.x)。 + // Set*/成员写后由帧循环的 UpdateTransforms 统一 Compose if (m_MouseLookEnabled) { f32 mouseDX = 0, mouseDY = 0; @@ -27,7 +26,6 @@ namespace Engine { t.rotation.y += mouseDX * m_MouseSensitivity; t.rotation.x = glm::clamp( t.rotation.x - mouseDY * m_MouseSensitivity, -89.0f, 89.0f); - dirty = true; } } @@ -42,15 +40,12 @@ namespace Engine { const glm::vec3 right = r * glm::vec3(1, 0, 0); f32 speed = m_MoveSpeed * deltaTime; - 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(); + if (window.IsKeyPressed(GLFW_KEY_W)) { t.position += forward * speed; } + if (window.IsKeyPressed(GLFW_KEY_S)) { t.position -= forward * speed; } + if (window.IsKeyPressed(GLFW_KEY_A)) { t.position -= right * speed; } + if (window.IsKeyPressed(GLFW_KEY_D)) { t.position += right * speed; } + if (window.IsKeyPressed(GLFW_KEY_SPACE)) { t.position.y += speed; } + if (window.IsKeyPressed(GLFW_KEY_LEFT_SHIFT)) { t.position.y -= speed; } } glm::mat4 Camera::GetView(const ECS::TransformComponent& t) const