Files
HelloVulkan/App/editor/Editor.cpp
T
atdunbg 1449bea86d feat: HitProxy GPU 拾取 + UE 选中描边 + 编辑器拾取交互
- HitProxy 拾取:RG 管线渲染实体 id → host readback,点击帧
  PickEntity 精确命中;拾取按视觉几何(Model 联合包围球射线求交),
  与 Collider 解耦(UE/Unity 编辑器语义),命中打印实体名+碰撞体参数
- 选中描边(UE 语义):SelectionMask pass(R8Unorm 完整投影轮廓,
  无深度测试透墙可见)→ Outline pass(边缘检测 + alpha 叠加,
  必须注册在 Tonemap 后);edgeCut 贴视口边缘;widthPx=6 可调
- shader object 实证修复:VUID-08879 同程序 set layouts 超集一致、
  VUID-10065 push block 4×float 平铺、新 pass 析构销毁 VkShaderEXT
- RG:SideEffect() 保活"产出无 RG 内消费者"的 pass 链
- Editor:层次面板点击选中、Inspector 姿态编辑、Stats 面板拾取信息
- 相机 Update 改 const(姿态写入 Transform 引用,Camera 本身无状态变更)
2026-10-02 20:09:05 +08:00

208 lines
6.7 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// Editor.cpp:docking 骨架实现。
//
// 布局方案(首帧 DockBuilder 一次,之后 imgui.ini 接管):
// ┌──────────────────────────────┬───────────────┐
// │ Hierarchy │ │
// ├──────────────────────────────┤ Inspector │
// │ Stats │ (28%) │
// └──────────────────────────────┴───────────────┘
//
#include "Editor.h"
#include "HierarchyPanel.h"
#include "InspectorPanel.h"
#include "ViewportPanel.h"
#include "StatsPanel.h"
#include <imgui.h>
#include <imgui_internal.h> // DockBuilder*(内部 API)
#include <GLFW/glfw3.h>
using namespace Engine; // kDisplayViewport / kDisplayDirect(SceneTypes.h)
Editor::Editor() = default;
Editor::~Editor() = default;
void Editor::Init(const EditorContext& ctx)
{
m_Ctx = ctx;
m_Hierarchy = std::make_unique<HierarchyPanel>();
m_Inspector = std::make_unique<InspectorPanel>();
m_Viewport = std::make_unique<ViewportPanel>();
m_Stats = std::make_unique<StatsPanel>();
}
void Editor::Draw()
{
// host 窗口:铺满主视口,承载菜单栏 + DockSpace
ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->Pos);
ImGui::SetNextWindowSize(viewport->Size);
ImGui::SetNextWindowViewport(viewport->ID);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::Begin("##EditorDockHost", nullptr,
ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking |
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoBringToFrontOnFocus |
ImGuiWindowFlags_NoNavFocus);
ImGui::PopStyleVar(3);
DrawMenuBar();
DrawDockSpace();
ImGui::End();
// 常规面板窗口(窗口名 = docking 身份)
if (m_ShowHierarchy) m_Hierarchy->Draw(m_Ctx, m_Selected);
if (m_ShowInspector) m_Inspector->Draw(m_Ctx, m_Selected);
if (m_ShowViewport)
{
if (auto picked = m_Viewport->Draw(m_Ctx, m_VpInput)) {
SelectEntity(*picked); // Viewport 点击拾取 → 选中联动 Inspector/Hierarchy
}
}
if (m_ShowStats) m_Stats->Draw(m_Ctx);
DrawQuitModal();
}
// 输入归属判定:ImGui 侧经自身后端链式回调永远正常收输入,这里
// 只决定"剩余输入给不给游戏侧"。UI 被隐藏(反引号)时不会走到
// Draw → m_VpInput 停在旧值,谓词调用方(SandboxApp)须先查
// GetImGuiEnabled() 再问这里。
bool Editor::BlockingKeyboard() const
{
const ImGuiIO& io = ImGui::GetIO();
if (io.WantTextInput) return true; // 打字中(改名等)不让位快捷键
// 游戏形态(Fullscreen):WASD 全窗口生效,键盘只让位文本输入
if (m_Ctx.renderer &&
m_Ctx.renderer->GetDisplayMode() == kDisplayDirect)
{
return false;
}
// 编辑器形态:Viewport 面板持焦才拥有键盘(UE Scene view 语义)
return !m_VpInput.focused;
}
bool Editor::BlockingMouse() const
{
// 游戏形态:沿用 ImGui 全局判定(UI 隐藏时 WantCaptureMouse=false)
if (m_Ctx.renderer &&
m_Ctx.renderer->GetDisplayMode() == kDisplayDirect)
{
return ImGui::GetIO().WantCaptureMouse;
}
// 编辑器形态:悬停 Viewport 才放行(模态框遮挡时 hovered=false)
return !m_VpInput.hovered;
}
void Editor::DrawMenuBar()
{
if (!ImGui::BeginMenuBar())
{
return;
}
if (ImGui::BeginMenu("File"))
{
if (ImGui::MenuItem("Quit", "Alt+F4"))
{
RequestQuit();
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("View"))
{
ImGui::MenuItem("Hierarchy", nullptr, &m_ShowHierarchy);
ImGui::MenuItem("Inspector", nullptr, &m_ShowInspector);
ImGui::MenuItem("Viewport", nullptr, &m_ShowViewport);
ImGui::MenuItem("Stats", nullptr, &m_ShowStats);
ImGui::EndMenu();
}
ImGui::EndMenuBar();
}
void Editor::DrawDockSpace()
{
const ImGuiID dockspaceId = ImGui::GetID("EditorDockSpace");
// 默认布局:仅当 dock node 不存在时构建一次
//(imgui.ini 已保存过布局 → DockBuilderGetNode 非空 → 跳过)
if (!m_LayoutBuilt && ImGui::DockBuilderGetNode(dockspaceId) == nullptr)
{
m_LayoutBuilt = true;
ImGui::DockBuilderRemoveNode(dockspaceId);
ImGui::DockBuilderAddNode(dockspaceId, ImGuiDockNodeFlags_DockSpace);
ImGui::DockBuilderSetNodeSize(dockspaceId, ImGui::GetMainViewport()->Size);
ImGuiID dockLeft = 0, dockRight = 0, dockCenter = 0, dockStats = 0;
ImGui::DockBuilderSplitNode(dockspaceId, ImGuiDir_Left, 0.20f,
&dockLeft, &dockCenter);
ImGui::DockBuilderSplitNode(dockCenter, ImGuiDir_Right, 0.28f,
&dockRight, &dockCenter);
ImGui::DockBuilderSplitNode(dockLeft, ImGuiDir_Down, 0.38f,
&dockStats, &dockLeft);
ImGui::DockBuilderDockWindow("Hierarchy", dockLeft);
ImGui::DockBuilderDockWindow("Stats", dockStats);
ImGui::DockBuilderDockWindow("Inspector", dockRight);
ImGui::DockBuilderDockWindow("Viewport", dockCenter);
ImGui::DockBuilderFinish(dockspaceId);
}
ImGui::DockSpace(dockspaceId, ImVec2(0.0f, 0.0f),
ImGuiDockNodeFlags_None);
}
void Editor::DrawQuitModal()
{
if (m_ShowQuitConfirm)
{
ImGui::OpenPopup("Should quit?");
m_ShowQuitConfirm = false;
}
ImGui::SetNextWindowPos(
ImGui::GetMainViewport()->GetCenter(),
ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
if (ImGui::BeginPopupModal("Should quit?", nullptr,
ImGuiWindowFlags_AlwaysAutoResize))
{
ImGui::TextUnformatted("Quit the application?");
ImGui::Separator();
if (ImGui::Button("Yes", ImVec2(120, 0)))
{
m_QuitConfirmed = true;
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("No", ImVec2(120, 0)))
{
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
if (m_QuitConfirmed)
{
m_QuitConfirmed = false;
m_Ctx.app->Close();
}
}