editor camera: dedicated Scene-view camera decoupled from camera entities
EditorCamera (UE FEditorViewportClient semantics) owned by SceneRenderer:
own TransformComponent + reused Camera controller. BuildFrame dispatches
render source by display mode — editor form always renders from the
editor camera (usable in empty scenes), game form renders from the
scene's primary camera entity. AlignTo copies an entity pose into the
editor camera ("Align Viewport to Camera" button in Inspector). F focus
and viewport gizmo now follow the same dispatch. main.cpp stripped of
dangling sandbox camera/spinner state; onSceneLoaded hook removed.
This commit is contained in:
@@ -367,10 +367,4 @@ void Editor::DoLoadScene()
|
||||
"keeping current", envPath);
|
||||
}
|
||||
}
|
||||
|
||||
// 应用层重解析悬空句柄(相机等沙盒状态)
|
||||
if (m_Ctx.onSceneLoaded)
|
||||
{
|
||||
m_Ctx.onSceneLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <Engine/ECS/Scene.h>
|
||||
#include <Engine/Assets/AssetManager.h>
|
||||
#include <Engine/Renderer/Renderer.h>
|
||||
#include <Engine/Renderer/EditorCamera.h>
|
||||
#include <Engine/Renderer/SceneTypes.h> // kDisplayViewport / kDisplayDirect
|
||||
|
||||
#include <functional>
|
||||
@@ -37,12 +38,12 @@ struct EditorContext {
|
||||
Engine::ECS::Scene *scene = nullptr;
|
||||
Engine::Assets::AssetManager *assets = nullptr;
|
||||
Engine::Application *app = nullptr;
|
||||
// 编辑器相机(Scene view 专用,归 Renderer 持有——与场景相机实体
|
||||
// 无关;面板经此做 F 焦点/Gizmo 视图/对齐跳转)
|
||||
Engine::EditorCamera *editorCamera = nullptr;
|
||||
|
||||
std::function<int()> getEnvIndex; // 当前环境表下标
|
||||
std::function<void(int)> setEnvIndex;
|
||||
// 场景加载成功后由 SandboxApp 重解析悬空句柄(registry 清场重建后
|
||||
// 应用层持有的 EntityHandle 全部悬空,见 Scene::Clear 注释)
|
||||
std::function<void()> onSceneLoaded;
|
||||
};
|
||||
|
||||
#endif //HELLOVULKAN_EDITORCONTEXT_H
|
||||
|
||||
@@ -198,7 +198,7 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
|
||||
if (ImGui::Checkbox("primary", &cam->primary) && cam->primary)
|
||||
{
|
||||
// primary 互斥:启用本相机时清掉其余实体的 primary
|
||||
//(引擎取第一个 primary 作为渲染视角)
|
||||
//(引擎取第一个 primary 作为游戏形态渲染视角)
|
||||
reg.view<Engine::ECS::CameraComponent>().each(
|
||||
[&](entt::entity e, Engine::ECS::CameraComponent& c)
|
||||
{
|
||||
@@ -208,6 +208,18 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 对齐跳转:把本实体位姿复制给编辑器相机,Scene view 立即
|
||||
// 从该位置观看(只取位姿,不接管渲染——渲染源分派在
|
||||
// SceneRenderer,编辑器形态恒用编辑器相机)
|
||||
if (ImGui::Button("Align Viewport to Camera") && ctx.editorCamera)
|
||||
{
|
||||
if (auto* tr = reg.try_get<Engine::ECS::TransformComponent>(
|
||||
selected))
|
||||
{
|
||||
ctx.editorCamera->AlignTo(*tr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,19 +19,28 @@ void StatsPanel::Draw(const EditorContext& ctx)
|
||||
auto& renderer = *ctx.renderer;
|
||||
auto& app = *ctx.app;
|
||||
|
||||
// ---- Camera:主相机位姿(只读遥测;位姿编辑走 Inspector)----
|
||||
// ---- Camera:视图相机位姿(只读遥测;跟随渲染源分派)----
|
||||
{
|
||||
if (ctx.renderer->GetDisplayMode() == kDisplayViewport && ctx.editorCamera)
|
||||
{
|
||||
const glm::vec3& pos = ctx.editorCamera->GetTransform().GetPosition();
|
||||
ImGui::Text("Editor camera pos");
|
||||
ImGui::Text("(%.3f, %.3f, %.3f)", pos.x, pos.y, pos.z);
|
||||
}
|
||||
else
|
||||
{
|
||||
Engine::ECS::TransformComponent* camT = nullptr;
|
||||
if (ctx.scene->GetPrimaryCamera(&camT) != nullptr && camT != nullptr)
|
||||
{
|
||||
const glm::vec3& pos = camT->GetPosition();
|
||||
ImGui::Text("Camera pos");
|
||||
ImGui::Text("Game camera pos");
|
||||
ImGui::Text("(%.3f, %.3f, %.3f)", pos.x, pos.y, pos.z);
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::TextDisabled("(no primary camera)");
|
||||
}
|
||||
}
|
||||
ImGui::Separator();
|
||||
}
|
||||
|
||||
|
||||
@@ -66,6 +66,22 @@ namespace
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
// 视图相机分派(必须与 SceneRenderer::BuildFrame 渲染源同一规则,
|
||||
// 否则 Gizmo 杆与画面错位):编辑器形态 = EditorCamera(Scene view
|
||||
// 专用,与相机实体无关);游戏形态 = primary 相机实体。
|
||||
// 返回 nullptr = 游戏形态且无 primary 相机(本帧无 Gizmo)
|
||||
Engine::Camera* ViewCamera(const EditorContext& ctx,
|
||||
ECS::TransformComponent*& outT)
|
||||
{
|
||||
if (ctx.renderer->GetDisplayMode() == kDisplayViewport)
|
||||
{
|
||||
outT = &ctx.editorCamera->GetTransform();
|
||||
return &ctx.editorCamera->GetController();
|
||||
}
|
||||
auto* cc = ctx.scene->GetPrimaryCamera(&outT);
|
||||
return cc ? &cc->camera : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<entt::entity> ViewportPanel::Draw(const EditorContext &ctx,
|
||||
@@ -143,14 +159,10 @@ std::optional<entt::entity> ViewportPanel::Draw(const EditorContext &ctx,
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_R)) gizmo.op = ImGuizmo::SCALE;
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_T)) gizmo.localSpace = !gizmo.localSpace;
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_F) && tr != nullptr) {
|
||||
// F = 焦点选中实体(frame-selection):环绕 pivot 设
|
||||
// 为选中实体位置,相机沿视线平移对准(环绕距离沿用
|
||||
// 当前值,滚轮可调)
|
||||
ECS::TransformComponent *camT = nullptr;
|
||||
if (auto *camComp = ctx.scene->GetPrimaryCamera(&camT);
|
||||
camComp != nullptr && camT != nullptr) {
|
||||
camComp->camera.FocusOn(*camT, tr->GetPosition());
|
||||
}
|
||||
// F = 焦点选中实体(frame-selection):编辑器相机
|
||||
// 环绕 pivot 设为选中实体位置,沿视线平移对准(环
|
||||
// 绕距离沿用当前值,滚轮可调)
|
||||
ctx.editorCamera->FocusOn(tr->GetPosition());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,14 +170,16 @@ std::optional<entt::entity> ViewportPanel::Draw(const EditorContext &ctx,
|
||||
// 选中实体 + 有 Transform 才出杆;矩阵分解直接写回
|
||||
// TransformComponent(与 Inspector 同源,下一帧数值自动跟随)
|
||||
if (tr != nullptr) {
|
||||
// 相机矩阵:primary 相机 view + Vulkan proj(转 GL 形式),
|
||||
// 参数与渲染侧一致(aspect = 渲染分辨率、near/far 0.1/100)
|
||||
// 相机矩阵:跟随渲染源(编辑器形态 = EditorCamera,游戏
|
||||
// 形态 = primary 相机实体;与 SceneRenderer::BuildFrame
|
||||
// 同一分派),参数与渲染侧一致(aspect = 渲染分辨率、
|
||||
// near/far 0.1/100)
|
||||
ECS::TransformComponent *camT = nullptr;
|
||||
if (const auto *camComp = ctx.scene->GetPrimaryCamera(&camT);
|
||||
camComp != nullptr && camT != nullptr) {
|
||||
const glm::mat4 view = camComp->camera.GetView(*camT);
|
||||
if (Engine::Camera *cam = ViewCamera(ctx, camT);
|
||||
cam != nullptr && camT != nullptr) {
|
||||
const glm::mat4 view = cam->GetView(*camT);
|
||||
const glm::mat4 projGL = ProjVulkanToGL(
|
||||
camComp->camera.GetProjection(aspect, 0.1f, 100.0f));
|
||||
cam->GetProjection(aspect, 0.1f, 100.0f));
|
||||
|
||||
ImGuizmo::SetDrawlist(ImGui::GetWindowDrawList());
|
||||
// 杆画在 Image 区域内(等比适配后的 item rect)
|
||||
|
||||
+14
-190
@@ -1,15 +1,18 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/20.
|
||||
//
|
||||
// SandboxApp:第一个基于 Application 框架的程序。
|
||||
// SandboxApp:基于 Application 框架的编辑器宿主程序。
|
||||
//
|
||||
// main 的职责边界:
|
||||
// - 定义 Application 派生类并运行它
|
||||
// - "程序逻辑":场景搭建(实体/组件)、逐帧更新、事件响应、
|
||||
// 异步环境加载
|
||||
// - 编辑器形态驱动 EditorCamera(Scene view 专用相机,非场景对象)
|
||||
// - 异步环境加载、全局热键、输入归属门控
|
||||
// - UI 全部委托 App/editor/ 的 docking Editor(本文件不再出现
|
||||
// 任何 ImGui 控件)
|
||||
//
|
||||
// 场景内容(实体/组件)由编辑器 File 菜单 Save/Load 场景文件管理,
|
||||
// 相机实体属于场景数据、只在游戏形态参与渲染(UE 语义)。
|
||||
//
|
||||
// 窗口、渲染器、事件泵、帧计时、场景容器全部由框架管理。
|
||||
//
|
||||
|
||||
@@ -18,16 +21,8 @@
|
||||
#include <Engine/Core/Window.h> // SetInputCapture(Application.h 只前置声明)
|
||||
#include <Engine/Core/Log.h>
|
||||
#include <Engine/Core/Stats.h>
|
||||
#include <Engine/Core/AsyncTask.h>
|
||||
#include <Engine/ECS/Scene.h>
|
||||
#include <Engine/ECS/EntityHandle.h>
|
||||
#include <Engine/ECS/Components.h>
|
||||
#include <Engine/Assets/AssetManager.h>
|
||||
#include <Engine/Renderer/Renderer.h>
|
||||
#include <Engine/Renderer/Model.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <Engine/Renderer/EditorCamera.h>
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
@@ -35,12 +30,9 @@
|
||||
#include "EditorContext.h" // kEnvPaths / kDefaultEnv(与 StatsPanel 共用)
|
||||
|
||||
using namespace Engine;
|
||||
using namespace Engine::ECS;
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr const char* kTemplePath = "assets/models/SunTemple/SunTemple.fbx";
|
||||
|
||||
class SandboxApp final : public Application
|
||||
{
|
||||
public:
|
||||
@@ -58,7 +50,6 @@ namespace
|
||||
|
||||
void OnInit() override
|
||||
{
|
||||
Scene& scene = GetScene();
|
||||
Renderer& renderer = GetRenderer();
|
||||
renderer.SetDisplayMode(kDisplayViewport);
|
||||
|
||||
@@ -68,162 +59,17 @@ namespace
|
||||
|
||||
// Editor 注入:引擎门面 + 沙盒能力回调(环境异步切换等)
|
||||
InitEditor();
|
||||
|
||||
// 默认环境(IBL 四件套):经资产层加载(compute 预积分,毫秒级),
|
||||
// 其余环境在 Stats 面板里按需 lazy 加载后切换
|
||||
const auto env = GetAssets().LoadEnvironment(kEnvPaths[0]);
|
||||
if (env.Valid()) {
|
||||
GetRenderer().SetEnvironment(GetAssets().GetEnvironment(env));
|
||||
}
|
||||
|
||||
/*
|
||||
const auto suntemple = GetAssets().LoadModel(kTemplePath);
|
||||
auto entityhandle = scene.CreateEntity("SunTemple");
|
||||
entityhandle.AddComponent<MeshRendererComponent>(MeshRendererComponent{suntemple});
|
||||
*/
|
||||
|
||||
scene.CreateEntity("test entity");
|
||||
scene.CreateEntity("test entity");
|
||||
scene.CreateEntity("test entity");
|
||||
|
||||
const auto gun = GetAssets().LoadModel("assets/models/m1911/M1911.fbx");
|
||||
if (gun.Valid()) {
|
||||
m_Gun = scene.CreateEntity("M1911");
|
||||
m_Gun.AddComponent<MeshRendererComponent>(MeshRendererComponent{gun});
|
||||
m_Gun.AddComponent<AnimationComponent>();
|
||||
auto& gt = m_Gun.GetComponent<TransformComponent>();
|
||||
gt.SetPosition(glm::vec3(0.0f, 1.2f, -6.0f));
|
||||
// 物理验证:Dynamic 刚体(默认 Box halfExtents 0.5),
|
||||
// 出生位置悬空 → 应下落至地面静止(Transform 由物理写回)
|
||||
m_Gun.AddComponent<RigidBodyComponent>();
|
||||
} else {
|
||||
ENGINE_WARN("m1911 model not loaded, animation test skipped");
|
||||
}
|
||||
|
||||
// 地面:程序化纯色平面(半边长 15,中心 y=-0.5)+ 静态盒刚体
|
||||
const auto ground = GetAssets().CreatePlane(15.0f, 140, 140, 145);
|
||||
m_Ground = scene.CreateEntity("Ground");
|
||||
m_Ground.GetComponent<TransformComponent>()
|
||||
.SetPosition(glm::vec3(0.0f, -0.5f, 0.0f)); // dirty → 帧循环统一 Compose
|
||||
m_Ground.AddComponent<MeshRendererComponent>(MeshRendererComponent{ground});
|
||||
auto& groundRb = m_Ground.AddComponent<RigidBodyComponent>();
|
||||
groundRb.type = RigidBodyComponent::BodyType::Static;
|
||||
groundRb.boxHalfExtents = glm::vec3(15.0f, 0.1f, 15.0f); // 平面顶点烘焙 ±15,不能用默认 0.5
|
||||
|
||||
/*
|
||||
const auto gun = GetAssets().LoadModel("assets/models/m1911/M1911.fbx");
|
||||
if (gun.Valid()) {
|
||||
m_Gun = scene.CreateEntity("M1911");
|
||||
m_Gun.AddComponent<MeshRendererComponent>(MeshRendererComponent{gun});
|
||||
m_Gun.AddComponent<AnimationComponent>();
|
||||
auto& gt = m_Gun.GetComponent<TransformComponent>();
|
||||
gt.SetPosition(glm::vec3(0.0f, 1.2f, -6.0f));
|
||||
} else {
|
||||
ENGINE_WARN("m1911 model not loaded, animation test skipped");
|
||||
}
|
||||
|
||||
int countSize = 0;
|
||||
if (countSize) {
|
||||
// 模型资产(PBR 验证场景:backpack 为旧式贴图资产,
|
||||
// 退化为电介质因子路径——albedo+normal,metallic=0/roughness=0.5)
|
||||
const auto model = GetAssets().LoadModel("assets/models/backpack/backpack.obj");
|
||||
// const auto model = GetAssets().LoadModel("assets/models/pocket_watch_4k.gltf/pocket_watch_4k.gltf");
|
||||
|
||||
for (int i = 0; i < countSize; i ++)
|
||||
{
|
||||
for (int j = 0; j < countSize; j ++)
|
||||
{
|
||||
auto m = scene.CreateEntity();
|
||||
m.AddComponent<MeshRendererComponent>(MeshRendererComponent{model});
|
||||
m.GetComponent<TransformComponent>().SetPosition(glm::vec3{(i - countSize * 0.5) * 2, 0, (j - countSize * 0.5) * 2});
|
||||
m_Spinners.push_back(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 地面:程序化纯色平面(半边长 15,中心 y=-2)
|
||||
const auto ground = GetAssets().CreatePlane(15.0f, 140, 140, 145);
|
||||
m_Ground = scene.CreateEntity("Ground");
|
||||
m_Ground.GetComponent<TransformComponent>()
|
||||
.SetPosition(glm::vec3(0.0f, -0.5f, 0.0f)); // dirty → 帧循环统一 Compose
|
||||
m_Ground.AddComponent<MeshRendererComponent>(MeshRendererComponent{ground});
|
||||
*/
|
||||
|
||||
// 相机实体(primary = 当前渲染视角;位姿存实体 Transform)
|
||||
m_Camera = scene.CreateEntity("MainCamera");
|
||||
auto& camera = m_Camera.AddComponent<CameraComponent>();
|
||||
camera.primary = true;
|
||||
m_Camera.GetComponent<TransformComponent>()
|
||||
.SetPosition(glm::vec3(0.0f, 12.0f, 22.0f));
|
||||
m_Camera.GetComponent<TransformComponent>()
|
||||
.SetRotation(glm::vec3(-25.0f, 0.0f, 0.0f));
|
||||
|
||||
// 灯光实体(第一个方向光 = 阴影光源)
|
||||
m_KeyLight = scene.CreateEntity("KeyLight");
|
||||
m_KeyLight.AddComponent<LightComponent>(
|
||||
LightComponent{
|
||||
.type = LightComponent::Type::Directional,
|
||||
.direction = glm::vec3(-0.5f, -0.6f, -0.35f),
|
||||
.color = glm::vec4(1.0f, 0.95f, 0.9f, 5.0f),
|
||||
.castShadow = true,
|
||||
});
|
||||
|
||||
/*
|
||||
scene.CreateEntity("FillLight")
|
||||
.AddComponent<LightComponent>(
|
||||
LightComponent{
|
||||
.type = LightComponent::Type::Directional,
|
||||
.direction = glm::vec3(0.5f, -0.5f, 0.3f),
|
||||
.color = glm::vec4(0.3f, 0.5f, 1.0f, 1.5f)
|
||||
});
|
||||
*/
|
||||
|
||||
/*
|
||||
// 点光源(位置 = 实体 Transform,出生即有;无阴影,距离衰减)
|
||||
auto fire = scene.CreateEntity("FirePitLight");
|
||||
fire.GetComponent<TransformComponent>().SetPosition(glm::vec3(2.0f, 1.5f, 1.0f));
|
||||
fire.AddComponent<LightComponent>(
|
||||
LightComponent{
|
||||
.type = LightComponent::Type::Point,
|
||||
.color = glm::vec4(1.0f, 0.6f, 0.3f, 1.0f), // 暖橙
|
||||
.radius = 8.0f,
|
||||
});
|
||||
|
||||
auto cool = scene.CreateEntity("CoolFillLight");
|
||||
cool.GetComponent<TransformComponent>().SetPosition(glm::vec3(-2.5f, 2.0f, -2.0f));
|
||||
cool.AddComponent<LightComponent>(
|
||||
LightComponent{
|
||||
.type = LightComponent::Type::Point,
|
||||
.color = glm::vec4(0.3f, 0.5f, 1.0f, 1.2f), // 冷蓝
|
||||
.radius = 10.0f,
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
void OnUpdate(const FrameTime& frameTime) override
|
||||
{
|
||||
// 加载过无相机实体的场景(或场景被清空):相机句柄悬空,
|
||||
// 直接跳过本帧游戏逻辑(渲染仍以编辑器视角进行)
|
||||
if (!m_Camera.IsAlive()) return;
|
||||
|
||||
// 相机(RMB 飞行 / MMB 焦点环绕 / 滚轮推拉;位姿读写实体
|
||||
// Transform)。编辑器相机走真实轨:暂停(timeScale=0)时仍
|
||||
// 可飞行观察。编辑器模式移动键需 RMB 按住(UE 语义:飞行时
|
||||
// 键盘归相机,W/E/R 归 Gizmo)
|
||||
m_Camera.GetComponent<CameraComponent>()
|
||||
.camera.Update(GetWindow(),
|
||||
m_Camera.GetComponent<TransformComponent>(),
|
||||
frameTime.UnscaledDt(),
|
||||
GetRenderer().GetDisplayMode() == kDisplayViewport);
|
||||
|
||||
// 旋转动画:编辑 Transform 分量后 Compose 重算 world。
|
||||
// 动画时钟 = Time::Elapsed()(scaled 累计,暂停自动冻结——
|
||||
// 帧历史验证:暂停后 Temporal Diff 视图 2 帧内整屏变黑)
|
||||
for (size_t i = 0; i < m_Spinners.size(); ++i)
|
||||
// 编辑器形态:驱动编辑器相机(RMB 飞行 / MMB 焦点环绕 /
|
||||
// 滚轮推拉)。暂停(timeScale=0)时仍可飞行——unscaled dt。
|
||||
// 游戏形态:相机实体由游戏逻辑驱动(本沙盒无逻辑 = 静止)
|
||||
if (GetRenderer().GetDisplayMode() == kDisplayViewport)
|
||||
{
|
||||
auto& t = m_Spinners[i].GetComponent<TransformComponent>();
|
||||
t.SetRotation(glm::vec3(
|
||||
0.0f, static_cast<f32>(Time::Elapsed()) * 60.0f +
|
||||
static_cast<f32>(i) * 40.0f, 0.0f));
|
||||
GetRenderer().GetEditorCamera().Update(GetWindow(),
|
||||
frameTime.UnscaledDt());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,25 +116,9 @@ namespace
|
||||
ctx.scene = &GetScene();
|
||||
ctx.assets = &GetAssets();
|
||||
ctx.app = this;
|
||||
ctx.editorCamera = &GetRenderer().GetEditorCamera();
|
||||
ctx.getEnvIndex = [this] { return m_EnvIndex; };
|
||||
ctx.setEnvIndex = [this](int index) { m_EnvIndex = index; };
|
||||
// 场景加载成功 → 沙盒侧悬空句柄重解析(registry 清场重建后
|
||||
// m_Camera 等成员句柄全部失效,不重解析则下一帧必崩)
|
||||
ctx.onSceneLoaded = [this] {
|
||||
Scene& scene = GetScene();
|
||||
m_Spinners.clear();
|
||||
m_Gun = {};
|
||||
m_Ground = {};
|
||||
m_KeyLight = {};
|
||||
entt::entity camEnt = entt::null;
|
||||
scene.Each<CameraComponent>([&](entt::entity e,
|
||||
CameraComponent& cc) {
|
||||
if (cc.primary) camEnt = e;
|
||||
});
|
||||
m_Camera = scene.GetEntityHandle(camEnt); // 无效 → 空句柄
|
||||
ENGINE_INFO("SandboxApp: scene state re-resolved (camera {})",
|
||||
m_Camera.IsAlive() ? "found" : "missing");
|
||||
};
|
||||
// 继承原沙盒默认:初始暂停(StatsPanel 的 Pause Simulation 开关恢复)
|
||||
Time::SetTimeScale(0.0f);
|
||||
m_Editor->Init(ctx);
|
||||
@@ -305,12 +135,6 @@ namespace
|
||||
|
||||
private:
|
||||
std::unique_ptr<Editor> m_Editor = std::make_unique<Editor>();
|
||||
|
||||
EntityHandle m_Camera{}; // Scene::CreateEntity 之前为空
|
||||
EntityHandle m_KeyLight{};
|
||||
EntityHandle m_Ground{};
|
||||
EntityHandle m_Gun{}; // 骨骼动画验证实体(M1911)
|
||||
std::vector<EntityHandle> m_Spinners;
|
||||
int m_EnvIndex = kDefaultEnv;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -54,6 +54,10 @@ namespace Engine {
|
||||
void SetMouseLookEnabled(const bool enabled) { m_MouseLookEnabled = enabled; }
|
||||
bool IsMouseLookEnabled() const { return m_MouseLookEnabled; }
|
||||
|
||||
// 清除环绕 pivot(位姿被外部整体改写后 pivot 已失效——下一次
|
||||
// MMB 环绕按新视线自动重建)
|
||||
void ResetOrbit() { m_HasPivot = false; }
|
||||
|
||||
private:
|
||||
f32 m_MoveSpeed = 2.5f;
|
||||
f32 m_MouseSensitivity = 0.1f;
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/10/3.
|
||||
//
|
||||
// EditorCamera:Scene view 专用相机(UE FEditorViewportClient 语义)。
|
||||
//
|
||||
// 与场景相机实体是两个东西——它是视口 UI 状态,不属于场景:
|
||||
// - 位姿自有(内嵌 TransformComponent,无父层级),空场景/无相机
|
||||
// 实体也可用,永在不可删
|
||||
// - 交互复用 Camera 控制器(RMB 飞行 / MMB 环绕 / 滚轮推拉),输入
|
||||
// 直接写回自有 transform——位姿单一事实源不变(transform 即位姿)
|
||||
// - 渲染源分派在 SceneRenderer::BuildFrame:编辑器形态恒用本类,
|
||||
// 游戏形态用场景 primary 相机实体(游戏逻辑所有,本类不参与,
|
||||
// 不 Update 即静止)
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_EDITORCAMERA_H
|
||||
#define HELLOVULKAN_EDITORCAMERA_H
|
||||
|
||||
#include <Engine/Renderer/Camera.h>
|
||||
#include <Engine/ECS/Components.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
class Window;
|
||||
|
||||
class EditorCamera {
|
||||
public:
|
||||
EditorCamera()
|
||||
{
|
||||
// 出场位姿:俯视场中央(与旧沙盒 MainCamera 出场一致,
|
||||
// 空场景也有可看的初始视角)
|
||||
m_Transform.SetPosition({0.0f, 12.0f, 22.0f});
|
||||
m_Transform.SetRotation({-25.0f, 0.0f, 0.0f});
|
||||
m_Transform.Compose();
|
||||
}
|
||||
|
||||
// 每帧输入驱动(编辑器形态由 Application 调用;游戏形态不调
|
||||
// 即静止)。flyRequiresRMB 恒 true:编辑器移动键只在 RMB 按住
|
||||
// 时生效(W/E/R 归 Gizmo,UE 语义)。暂停(timeScale=0)时
|
||||
// 仍可飞行——调用方传 unscaled dt
|
||||
void Update(Window& window, f32 deltaTime)
|
||||
{
|
||||
m_Controller.Update(window, m_Transform, deltaTime,
|
||||
/*flyRequiresRMB=*/true);
|
||||
m_Transform.Compose(); // World() 是渲染/Gizmo 读源,随写随算
|
||||
}
|
||||
|
||||
// 跳转(对齐视口):把相机实体(或任意实体)位姿复制过来,
|
||||
// Scene view 立即从该位置观看(UE "Pilot This Camera" 的
|
||||
// 一次性对齐版)。环绕 pivot 已失效,清除待 MMB 重建
|
||||
void AlignTo(const ECS::TransformComponent& target)
|
||||
{
|
||||
m_Transform.SetPosition(target.GetPosition());
|
||||
m_Transform.SetRotationQuat(target.GetRotationQuat());
|
||||
m_Transform.Compose();
|
||||
m_Controller.ResetOrbit();
|
||||
}
|
||||
|
||||
// 焦点选中(F 键):pivot 设为 point,沿当前视线平移对准
|
||||
void FocusOn(const glm::vec3& point)
|
||||
{
|
||||
m_Controller.FocusOn(m_Transform, point);
|
||||
}
|
||||
|
||||
// 视图 = 自有 transform 世界矩阵的逆(要求已 Compose——
|
||||
// Update/AlignTo/ctor 三个写入口都收尾 Compose)
|
||||
glm::mat4 GetView() const { return m_Controller.GetView(m_Transform); }
|
||||
|
||||
ECS::TransformComponent& GetTransform() { return m_Transform; }
|
||||
Camera& GetController() { return m_Controller; }
|
||||
|
||||
private:
|
||||
ECS::TransformComponent m_Transform; // 自有位姿(非场景实体)
|
||||
Camera m_Controller; // 交互控制器(环绕状态在此)
|
||||
};
|
||||
|
||||
} // namespace Engine
|
||||
|
||||
#endif //HELLOVULKAN_EDITORCAMERA_H
|
||||
@@ -41,6 +41,7 @@ namespace Engine {
|
||||
class CommandManager;
|
||||
class UniformBuffer;
|
||||
class SceneRenderer;
|
||||
class EditorCamera;
|
||||
class VulkanAssetUploader;
|
||||
class Cubemap;
|
||||
class IblGenerator;
|
||||
@@ -141,6 +142,9 @@ namespace Engine {
|
||||
// ---- 显示模式(Editor 视口 ↔ 全屏直出;帧首同步生效)----
|
||||
int GetDisplayMode() const { return m_DesiredDisplayMode; }
|
||||
void SetDisplayMode(int mode); // cpp 内定义(SceneRenderer 前置声明)
|
||||
// 编辑器相机(Scene view 恒用,与场景相机实体无关)。转发
|
||||
// SceneRenderer 持有的实例:Application 驱动 Update,面板读写位姿
|
||||
EditorCamera& GetEditorCamera(); // cpp 内定义(SceneRenderer 完整类型)
|
||||
// 当帧场景数据(只读直通)。CPU 消费如视口拾取:反投影矩阵必须
|
||||
// 与渲染同一台相机同一投影(含 [0,1] 深度语义),禁止自行重算
|
||||
const FrameSceneData& GetFrameSceneData() const; // cpp 内定义
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#include <array>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <Engine/Renderer/EditorCamera.h>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
namespace ECS { class Scene; }
|
||||
@@ -35,7 +37,8 @@ namespace Engine {
|
||||
|
||||
class SceneRenderer {
|
||||
public:
|
||||
// 从场景构建当帧渲染数据。无 primary 相机返回 false(渲染跳帧)。
|
||||
// 从场景构建当帧渲染数据。游戏形态无 primary 相机返回 false
|
||||
//(渲染跳帧;编辑器形态恒用编辑器相机,不跳)。
|
||||
// targetExtent 用于 TAA 子像素抖动的 NDC 偏移折算。
|
||||
// dt 驱动骨骼动画采样推进(crossfade 计时)。
|
||||
bool BuildFrame(ECS::Scene& scene, Assets::AssetManager& assets,
|
||||
@@ -62,6 +65,9 @@ namespace Engine {
|
||||
void SetLodEnabled(bool b) { m_LodEnabled = b; }
|
||||
void SetDebugView(int v) { m_DebugView = v; }
|
||||
void SetDisplayMode(int m) { m_DisplayMode = m; } // kDisplayDirect / kDisplayViewport
|
||||
// 编辑器相机(视口 UI 状态,SceneRenderer 持有 = 渲染源在此分派);
|
||||
// Application 驱动 Update,ViewportPanel/Inspector 经 Renderer 门面访问
|
||||
EditorCamera& GetEditorCamera() { return m_EditorCamera; }
|
||||
// Editor 视口拾取请求(UI 段写入,下一次 BuildFrame 落帧后清除)
|
||||
void SetPickRequest(const glm::vec2& ndc)
|
||||
{
|
||||
@@ -83,6 +89,7 @@ namespace Engine {
|
||||
static void PackIndirect(CullResult& view, bool byMaterial);
|
||||
|
||||
FrameSceneData m_Frame;
|
||||
EditorCamera m_EditorCamera; // 编辑器相机(视口 UI 状态,见 GetEditorCamera)
|
||||
PickRequest m_PendingPick{}; // 拾取请求缓冲(BuildFrame 落帧后清除)
|
||||
u32 m_PendingSelected = 0; // 选中实体缓冲(描边)
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <Engine/Renderer/Renderer.h>
|
||||
#include <Engine/Renderer/SceneRenderer.h>
|
||||
#include <Engine/Renderer/EditorCamera.h>
|
||||
#include <Engine/Renderer/VulkanAssetUploader.h>
|
||||
#include <Engine/Renderer/Passes/SceneGpu.h>
|
||||
#include <Engine/Renderer/Passes/FullscreenGpu.h>
|
||||
@@ -233,6 +234,11 @@ namespace Engine
|
||||
m_SceneRenderer->SetDisplayMode(mode);
|
||||
}
|
||||
|
||||
EditorCamera& Renderer::GetEditorCamera()
|
||||
{
|
||||
return m_SceneRenderer->GetEditorCamera();
|
||||
}
|
||||
|
||||
const FrameSceneData &Renderer::GetFrameSceneData() const
|
||||
{
|
||||
return m_SceneRenderer->Frame();
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <Engine/ECS/Scene.h>
|
||||
#include <Engine/ECS/Components.h>
|
||||
#include <Engine/Renderer/Camera.h>
|
||||
#include <Engine/Renderer/EditorCamera.h>
|
||||
#include <Engine/Renderer/Frustum.h>
|
||||
#include <Engine/Renderer/Model.h>
|
||||
#include <Engine/Renderer/Mesh.h>
|
||||
@@ -258,14 +259,21 @@ namespace Engine
|
||||
ubo.prevView = m_LastView;
|
||||
ubo.prevProj = m_LastProjNoJitter;
|
||||
|
||||
// ---- 1. 相机(位姿单一事实源 = 相机实体的 Transform)----
|
||||
TransformComponent* camTransform = nullptr;
|
||||
auto* cameraComp = scene.GetPrimaryCamera(&camTransform);
|
||||
if (!cameraComp || !camTransform) {
|
||||
// 无相机:本帧跳过(正常场景必有相机,安全网)
|
||||
// ---- 1. 相机:渲染源按显示模式分派(UE 语义)。编辑器形态
|
||||
// Scene view 恒用编辑器相机(视口 UI 状态,空场景可用);
|
||||
// 游戏形态用场景 primary 相机实体(游戏逻辑所有)----
|
||||
ECS::TransformComponent* camTransform = nullptr;
|
||||
const Camera* camera = nullptr;
|
||||
if (m_DisplayMode == kDisplayViewport) {
|
||||
camTransform = &m_EditorCamera.GetTransform();
|
||||
camera = &m_EditorCamera.GetController();
|
||||
} else if (auto* cameraComp = scene.GetPrimaryCamera(&camTransform)) {
|
||||
camera = &cameraComp->camera;
|
||||
}
|
||||
if (!camera || !camTransform) {
|
||||
// 游戏形态无 primary 相机:本帧跳过(安全网)
|
||||
return false; // 拾取请求未消费,留待下一帧重试
|
||||
}
|
||||
const Camera& camera = cameraComp->camera;
|
||||
|
||||
// 拾取请求落帧(一次性消费:本帧 HitProxy 渲染 + 像素拷贝)。
|
||||
// 放在相机校验之后:跳帧时请求保留,不丢点击
|
||||
@@ -276,8 +284,8 @@ namespace Engine
|
||||
|
||||
const f32 aspect = static_cast<f32>(targetExtent.width) /
|
||||
static_cast<f32>(targetExtent.height);
|
||||
ubo.view = camera.GetView(*camTransform);
|
||||
ubo.proj = camera.GetProjection(aspect, 0.1f, 100.0f);
|
||||
ubo.view = camera->GetView(*camTransform);
|
||||
ubo.proj = camera->GetProjection(aspect, 0.1f, 100.0f);
|
||||
|
||||
// 未抖动投影:velocity 的当前帧 clip 位置用它(抖动不能进 velocity)
|
||||
ubo.projNoJitter = ubo.proj;
|
||||
|
||||
Reference in New Issue
Block a user