- 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 本身无状态变更)
208 lines
6.7 KiB
C++
208 lines
6.7 KiB
C++
//
|
||
// 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();
|
||
}
|
||
}
|