2026-03-26 22:31:22 +08:00
|
|
|
#include "Actions/ActionRouting.h"
|
2026-03-28 17:40:14 +08:00
|
|
|
#include "Core/IEditorContext.h"
|
2026-03-29 16:18:13 +08:00
|
|
|
#include "Core/ISceneManager.h"
|
2026-03-29 15:12:38 +08:00
|
|
|
#include "Core/ISelectionManager.h"
|
2026-03-20 17:08:06 +08:00
|
|
|
#include "SceneViewPanel.h"
|
2026-04-03 13:22:30 +08:00
|
|
|
#include "Viewport/SceneViewportEditorOverlayData.h"
|
2026-04-03 17:04:34 +08:00
|
|
|
#include "Viewport/SceneViewportHudOverlay.h"
|
2026-04-04 00:21:25 +08:00
|
|
|
#include "Viewport/SceneViewportInteractionActions.h"
|
2026-04-03 17:16:16 +08:00
|
|
|
#include "Viewport/SceneViewportInteractionResolver.h"
|
2026-04-03 13:22:30 +08:00
|
|
|
#include "Viewport/SceneViewportMath.h"
|
2026-04-04 00:45:13 +08:00
|
|
|
#include "Viewport/SceneViewportTransformGizmoCoordinator.h"
|
2026-03-28 17:04:14 +08:00
|
|
|
#include "ViewportPanelContent.h"
|
2026-04-01 16:42:57 +08:00
|
|
|
#include "Platform/Win32Utf8.h"
|
2026-03-26 21:18:33 +08:00
|
|
|
#include "UI/UI.h"
|
2026-03-28 17:50:54 +08:00
|
|
|
|
2026-03-20 17:08:06 +08:00
|
|
|
#include <imgui.h>
|
|
|
|
|
|
2026-04-03 13:22:30 +08:00
|
|
|
#include <algorithm>
|
2026-04-01 16:42:57 +08:00
|
|
|
#include <filesystem>
|
2026-04-03 13:22:30 +08:00
|
|
|
#include <vector>
|
2026-04-01 16:42:57 +08:00
|
|
|
|
2026-03-24 20:02:38 +08:00
|
|
|
namespace XCEngine {
|
|
|
|
|
namespace Editor {
|
2026-03-20 17:08:06 +08:00
|
|
|
|
2026-03-29 16:18:13 +08:00
|
|
|
namespace {
|
|
|
|
|
|
2026-04-01 16:42:57 +08:00
|
|
|
struct SceneViewportToolOverlayResult {
|
|
|
|
|
bool hovered = false;
|
|
|
|
|
bool clicked = false;
|
|
|
|
|
SceneViewportToolMode clickedTool = SceneViewportToolMode::Move;
|
|
|
|
|
};
|
|
|
|
|
|
2026-04-03 13:22:30 +08:00
|
|
|
const char* GetSceneViewportPivotModeLabel(SceneViewportPivotMode mode) {
|
|
|
|
|
return mode == SceneViewportPivotMode::Pivot ? "Pivot" : "Center";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char* GetSceneViewportTransformSpaceModeLabel(SceneViewportTransformSpaceMode mode) {
|
|
|
|
|
return mode == SceneViewportTransformSpaceMode::Global ? "Global" : "Local";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float GetSceneToolbarToggleWidth(const char* firstLabel, const char* secondLabel) {
|
|
|
|
|
constexpr float kHorizontalPadding = 10.0f;
|
|
|
|
|
constexpr float kMinWidth = 68.0f;
|
|
|
|
|
const float maxLabelWidth = (std::max)(
|
|
|
|
|
ImGui::CalcTextSize(firstLabel).x,
|
|
|
|
|
ImGui::CalcTextSize(secondLabel).x);
|
|
|
|
|
return (std::max)(kMinWidth, maxLabelWidth + kHorizontalPadding * 2.0f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RenderSceneViewportTopBar(
|
|
|
|
|
SceneViewportPivotMode& pivotMode,
|
|
|
|
|
SceneViewportTransformSpaceMode& transformSpaceMode) {
|
|
|
|
|
constexpr float kSceneToolbarHeight = 24.0f;
|
|
|
|
|
constexpr float kSceneToolbarPaddingY = 0.0f;
|
|
|
|
|
constexpr float kSceneToolbarButtonHeight = kSceneToolbarHeight - kSceneToolbarPaddingY * 2.0f;
|
|
|
|
|
constexpr ImVec2 kSceneToolbarButtonFramePadding(8.0f, 1.0f);
|
|
|
|
|
|
|
|
|
|
UI::PanelToolbarScope toolbar(
|
|
|
|
|
"SceneToolbar",
|
|
|
|
|
kSceneToolbarHeight,
|
|
|
|
|
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse,
|
|
|
|
|
true,
|
|
|
|
|
ImVec2(UI::ToolbarPadding().x, kSceneToolbarPaddingY),
|
|
|
|
|
ImVec2(0.0f, UI::ToolbarItemSpacing().y),
|
|
|
|
|
ImVec4(0.23f, 0.23f, 0.23f, 1.0f));
|
|
|
|
|
if (!toolbar.IsOpen()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, kSceneToolbarButtonFramePadding);
|
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0.0f);
|
|
|
|
|
|
|
|
|
|
if (UI::ToolbarButton(
|
|
|
|
|
GetSceneViewportPivotModeLabel(pivotMode),
|
|
|
|
|
true,
|
|
|
|
|
ImVec2(GetSceneToolbarToggleWidth("Pivot", "Center"), kSceneToolbarButtonHeight))) {
|
|
|
|
|
pivotMode = pivotMode == SceneViewportPivotMode::Pivot
|
|
|
|
|
? SceneViewportPivotMode::Center
|
|
|
|
|
: SceneViewportPivotMode::Pivot;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ImGui::SameLine(0.0f, 0.0f);
|
|
|
|
|
|
|
|
|
|
if (UI::ToolbarButton(
|
|
|
|
|
GetSceneViewportTransformSpaceModeLabel(transformSpaceMode),
|
|
|
|
|
true,
|
|
|
|
|
ImVec2(GetSceneToolbarToggleWidth("Global", "Local"), kSceneToolbarButtonHeight))) {
|
|
|
|
|
transformSpaceMode = transformSpaceMode == SceneViewportTransformSpaceMode::Global
|
|
|
|
|
? SceneViewportTransformSpaceMode::Local
|
|
|
|
|
: SceneViewportTransformSpaceMode::Global;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ImGui::PopStyleVar(2);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-01 16:42:57 +08:00
|
|
|
const char* GetSceneViewportToolTooltip(SceneViewportToolMode toolMode) {
|
|
|
|
|
switch (toolMode) {
|
|
|
|
|
case SceneViewportToolMode::ViewMove:
|
|
|
|
|
return "View Move";
|
|
|
|
|
case SceneViewportToolMode::Move:
|
|
|
|
|
return "Move";
|
|
|
|
|
case SceneViewportToolMode::Rotate:
|
|
|
|
|
return "Rotate";
|
|
|
|
|
case SceneViewportToolMode::Scale:
|
|
|
|
|
return "Scale";
|
|
|
|
|
case SceneViewportToolMode::Transform:
|
|
|
|
|
return "Transform";
|
|
|
|
|
default:
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char* GetSceneViewportToolIconBaseName(SceneViewportToolMode toolMode) {
|
|
|
|
|
switch (toolMode) {
|
|
|
|
|
case SceneViewportToolMode::ViewMove:
|
|
|
|
|
return "view_move_tool";
|
|
|
|
|
case SceneViewportToolMode::Move:
|
|
|
|
|
return "move_tool";
|
|
|
|
|
case SceneViewportToolMode::Rotate:
|
|
|
|
|
return "rotate_tool";
|
|
|
|
|
case SceneViewportToolMode::Scale:
|
|
|
|
|
return "scale_tool";
|
|
|
|
|
case SceneViewportToolMode::Transform:
|
|
|
|
|
return "transform_tool";
|
|
|
|
|
default:
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-03 13:22:30 +08:00
|
|
|
std::string BuildSceneViewportIconPath(const char* iconBaseName, bool active = false) {
|
|
|
|
|
const std::filesystem::path exeDir(
|
|
|
|
|
XCEngine::Editor::Platform::Utf8ToWide(XCEngine::Editor::Platform::GetExecutableDirectoryUtf8()));
|
|
|
|
|
std::filesystem::path iconPath =
|
|
|
|
|
exeDir / L".." / L".." / L"resources" / L"Icons" /
|
|
|
|
|
std::filesystem::path(XCEngine::Editor::Platform::Utf8ToWide(iconBaseName));
|
|
|
|
|
if (active) {
|
|
|
|
|
iconPath += L"_on";
|
|
|
|
|
}
|
|
|
|
|
iconPath += L".png";
|
|
|
|
|
return XCEngine::Editor::Platform::WideToUtf8(iconPath.lexically_normal().wstring());
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-01 16:42:57 +08:00
|
|
|
const std::string& GetSceneViewportToolIconPath(SceneViewportToolMode toolMode, bool active) {
|
|
|
|
|
static std::string cachedPaths[5][2] = {};
|
|
|
|
|
const size_t toolIndex = static_cast<size_t>(toolMode);
|
|
|
|
|
const size_t stateIndex = active ? 1u : 0u;
|
|
|
|
|
std::string& cachedPath = cachedPaths[toolIndex][stateIndex];
|
|
|
|
|
if (!cachedPath.empty()) {
|
|
|
|
|
return cachedPath;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-03 13:22:30 +08:00
|
|
|
cachedPath = BuildSceneViewportIconPath(GetSceneViewportToolIconBaseName(toolMode), active);
|
2026-04-01 16:42:57 +08:00
|
|
|
return cachedPath;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SceneViewportToolOverlayResult RenderSceneViewportToolOverlay(
|
|
|
|
|
const ViewportPanelContentResult& content,
|
|
|
|
|
SceneViewportToolMode activeTool) {
|
|
|
|
|
SceneViewportToolOverlayResult result = {};
|
|
|
|
|
if (!content.hasViewportArea) {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constexpr float kButtonExtent = 30.0f;
|
|
|
|
|
constexpr float kButtonSpacing = 6.0f;
|
|
|
|
|
constexpr float kPanelPadding = 6.0f;
|
|
|
|
|
constexpr float kViewportInset = 10.0f;
|
|
|
|
|
constexpr float kIconInset = 4.0f;
|
|
|
|
|
constexpr size_t kToolCount = 5;
|
|
|
|
|
|
|
|
|
|
ImDrawList* drawList = ImGui::GetWindowDrawList();
|
|
|
|
|
if (drawList == nullptr) {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const ImVec2 panelMin(
|
|
|
|
|
content.itemMin.x + kViewportInset,
|
|
|
|
|
content.itemMin.y + kViewportInset);
|
|
|
|
|
const ImVec2 panelMax(
|
|
|
|
|
panelMin.x + kPanelPadding * 2.0f + kButtonExtent,
|
|
|
|
|
panelMin.y + kPanelPadding * 2.0f + kToolCount * kButtonExtent + (kToolCount - 1) * kButtonSpacing);
|
|
|
|
|
drawList->AddRectFilled(panelMin, panelMax, IM_COL32(24, 26, 29, 220), 7.0f);
|
|
|
|
|
drawList->AddRect(panelMin, panelMax, IM_COL32(255, 255, 255, 28), 7.0f, 0, 1.0f);
|
|
|
|
|
|
|
|
|
|
const SceneViewportToolMode toolModes[kToolCount] = {
|
|
|
|
|
SceneViewportToolMode::ViewMove,
|
|
|
|
|
SceneViewportToolMode::Move,
|
|
|
|
|
SceneViewportToolMode::Rotate,
|
|
|
|
|
SceneViewportToolMode::Scale,
|
|
|
|
|
SceneViewportToolMode::Transform
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for (size_t index = 0; index < kToolCount; ++index) {
|
|
|
|
|
const SceneViewportToolMode toolMode = toolModes[index];
|
|
|
|
|
const bool active = toolMode == activeTool;
|
|
|
|
|
const ImVec2 buttonMin(
|
|
|
|
|
panelMin.x + kPanelPadding,
|
|
|
|
|
panelMin.y + kPanelPadding + index * (kButtonExtent + kButtonSpacing));
|
|
|
|
|
const ImVec2 buttonMax(buttonMin.x + kButtonExtent, buttonMin.y + kButtonExtent);
|
|
|
|
|
|
|
|
|
|
ImGui::SetCursorScreenPos(buttonMin);
|
|
|
|
|
const std::string buttonId = std::string("##SceneToolButton") + std::to_string(static_cast<int>(toolMode));
|
|
|
|
|
const bool clicked = ImGui::InvisibleButton(
|
|
|
|
|
buttonId.c_str(),
|
|
|
|
|
ImVec2(kButtonExtent, kButtonExtent),
|
|
|
|
|
ImGuiButtonFlags_MouseButtonLeft |
|
|
|
|
|
ImGuiButtonFlags_PressedOnClick |
|
|
|
|
|
ImGuiButtonFlags_AllowOverlap);
|
|
|
|
|
|
|
|
|
|
const bool hovered = ImGui::IsItemHovered();
|
|
|
|
|
const bool held = ImGui::IsItemActive();
|
|
|
|
|
result.hovered = result.hovered || hovered;
|
|
|
|
|
result.clicked = result.clicked || clicked;
|
|
|
|
|
if (clicked) {
|
|
|
|
|
result.clickedTool = toolMode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const ImU32 backgroundColor = ImGui::GetColorU32(
|
|
|
|
|
held ? UI::ToolbarButtonActiveColor()
|
|
|
|
|
: hovered ? UI::ToolbarButtonHoveredColor(active)
|
|
|
|
|
: UI::ToolbarButtonColor(active));
|
|
|
|
|
drawList->AddRectFilled(buttonMin, buttonMax, backgroundColor, 5.0f);
|
|
|
|
|
drawList->AddRect(buttonMin, buttonMax, IM_COL32(255, 255, 255, active ? 48 : 24), 5.0f, 0, 1.0f);
|
|
|
|
|
|
|
|
|
|
const ImVec2 iconMin(buttonMin.x + kIconInset, buttonMin.y + kIconInset);
|
|
|
|
|
const ImVec2 iconMax(buttonMax.x - kIconInset, buttonMax.y - kIconInset);
|
|
|
|
|
if (!UI::DrawTextureAssetPreview(
|
|
|
|
|
drawList,
|
|
|
|
|
iconMin,
|
|
|
|
|
iconMax,
|
|
|
|
|
GetSceneViewportToolIconPath(toolMode, active))) {
|
|
|
|
|
drawList->AddText(
|
|
|
|
|
ImVec2(buttonMin.x + 8.0f, buttonMin.y + 7.0f),
|
|
|
|
|
IM_COL32(230, 230, 230, 255),
|
|
|
|
|
GetSceneViewportToolTooltip(toolMode));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hovered) {
|
|
|
|
|
ImGui::SetTooltip("%s", GetSceneViewportToolTooltip(toolMode));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ShouldBeginSceneViewportNavigationDrag(
|
|
|
|
|
bool hasInteractiveViewport,
|
|
|
|
|
bool hovered,
|
|
|
|
|
bool activeDrag,
|
|
|
|
|
bool otherDrag,
|
|
|
|
|
bool gizmoActive,
|
|
|
|
|
ImGuiMouseButton button) {
|
|
|
|
|
return hasInteractiveViewport &&
|
|
|
|
|
hovered &&
|
|
|
|
|
!activeDrag &&
|
|
|
|
|
!otherDrag &&
|
|
|
|
|
!gizmoActive &&
|
|
|
|
|
ImGui::IsMouseClicked(button);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-29 16:18:13 +08:00
|
|
|
} // namespace
|
|
|
|
|
|
2026-03-20 17:08:06 +08:00
|
|
|
SceneViewPanel::SceneViewPanel() : Panel("Scene") {}
|
|
|
|
|
|
|
|
|
|
void SceneViewPanel::Render() {
|
2026-03-29 16:18:13 +08:00
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
2026-03-26 16:43:06 +08:00
|
|
|
UI::PanelWindowScope panel(m_name.c_str());
|
2026-03-29 16:18:13 +08:00
|
|
|
ImGui::PopStyleVar();
|
2026-03-26 22:31:22 +08:00
|
|
|
if (!panel.IsOpen()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-03 13:22:30 +08:00
|
|
|
RenderSceneViewportTopBar(m_pivotMode, m_transformSpaceMode);
|
2026-03-28 17:40:14 +08:00
|
|
|
const ViewportPanelContentResult content = RenderViewportPanelContent(*m_context, EditorViewportKind::Scene);
|
|
|
|
|
if (IViewportHostService* viewportHostService = m_context->GetViewportHostService()) {
|
|
|
|
|
const ImGuiIO& io = ImGui::GetIO();
|
2026-03-29 16:18:13 +08:00
|
|
|
const bool hasInteractiveViewport = content.hasViewportArea && content.frame.hasTexture;
|
2026-04-01 16:42:57 +08:00
|
|
|
const SceneViewportToolOverlayResult toolOverlay = RenderSceneViewportToolOverlay(content, m_toolMode);
|
|
|
|
|
const bool viewportContentHovered = content.hovered && !toolOverlay.hovered;
|
|
|
|
|
|
|
|
|
|
if (toolOverlay.clicked) {
|
|
|
|
|
if (m_moveGizmo.IsActive()) {
|
|
|
|
|
m_moveGizmo.CancelDrag(&m_context->GetUndoManager());
|
|
|
|
|
}
|
|
|
|
|
if (m_rotateGizmo.IsActive()) {
|
|
|
|
|
m_rotateGizmo.CancelDrag(&m_context->GetUndoManager());
|
|
|
|
|
}
|
|
|
|
|
if (m_scaleGizmo.IsActive()) {
|
|
|
|
|
m_scaleGizmo.CancelDrag(&m_context->GetUndoManager());
|
|
|
|
|
}
|
|
|
|
|
m_toolMode = toolOverlay.clickedTool;
|
|
|
|
|
}
|
2026-03-31 23:45:08 +08:00
|
|
|
|
2026-04-03 13:22:30 +08:00
|
|
|
const bool allowToolShortcut = !io.WantTextInput && !m_lookDragging && !m_panDragging;
|
|
|
|
|
if (allowToolShortcut) {
|
|
|
|
|
if (ImGui::IsKeyPressed(ImGuiKey_Q, false)) {
|
|
|
|
|
if (m_moveGizmo.IsActive()) {
|
|
|
|
|
m_moveGizmo.CancelDrag(&m_context->GetUndoManager());
|
|
|
|
|
}
|
|
|
|
|
if (m_rotateGizmo.IsActive()) {
|
|
|
|
|
m_rotateGizmo.CancelDrag(&m_context->GetUndoManager());
|
|
|
|
|
}
|
|
|
|
|
if (m_scaleGizmo.IsActive()) {
|
|
|
|
|
m_scaleGizmo.CancelDrag(&m_context->GetUndoManager());
|
|
|
|
|
}
|
|
|
|
|
m_toolMode = SceneViewportToolMode::ViewMove;
|
|
|
|
|
} else if (ImGui::IsKeyPressed(ImGuiKey_W, false)) {
|
2026-03-31 23:45:08 +08:00
|
|
|
if (m_rotateGizmo.IsActive()) {
|
|
|
|
|
m_rotateGizmo.CancelDrag(&m_context->GetUndoManager());
|
|
|
|
|
}
|
2026-04-01 16:42:57 +08:00
|
|
|
if (m_scaleGizmo.IsActive()) {
|
|
|
|
|
m_scaleGizmo.CancelDrag(&m_context->GetUndoManager());
|
|
|
|
|
}
|
|
|
|
|
m_toolMode = SceneViewportToolMode::Move;
|
2026-03-31 23:45:08 +08:00
|
|
|
} else if (ImGui::IsKeyPressed(ImGuiKey_E, false)) {
|
|
|
|
|
if (m_moveGizmo.IsActive()) {
|
|
|
|
|
m_moveGizmo.CancelDrag(&m_context->GetUndoManager());
|
|
|
|
|
}
|
2026-04-01 16:42:57 +08:00
|
|
|
if (m_scaleGizmo.IsActive()) {
|
|
|
|
|
m_scaleGizmo.CancelDrag(&m_context->GetUndoManager());
|
|
|
|
|
}
|
|
|
|
|
m_toolMode = SceneViewportToolMode::Rotate;
|
|
|
|
|
} else if (ImGui::IsKeyPressed(ImGuiKey_R, false)) {
|
|
|
|
|
if (m_moveGizmo.IsActive()) {
|
|
|
|
|
m_moveGizmo.CancelDrag(&m_context->GetUndoManager());
|
|
|
|
|
}
|
|
|
|
|
if (m_rotateGizmo.IsActive()) {
|
|
|
|
|
m_rotateGizmo.CancelDrag(&m_context->GetUndoManager());
|
|
|
|
|
}
|
|
|
|
|
m_toolMode = SceneViewportToolMode::Scale;
|
2026-03-31 23:45:08 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-01 16:42:57 +08:00
|
|
|
const bool usingViewMoveTool = m_toolMode == SceneViewportToolMode::ViewMove;
|
|
|
|
|
const bool usingTransformTool = m_toolMode == SceneViewportToolMode::Transform;
|
|
|
|
|
const bool showingMoveGizmo = m_toolMode == SceneViewportToolMode::Move || usingTransformTool;
|
|
|
|
|
const bool showingRotateGizmo = m_toolMode == SceneViewportToolMode::Rotate || usingTransformTool;
|
|
|
|
|
const bool showingScaleGizmo = m_toolMode == SceneViewportToolMode::Scale || usingTransformTool;
|
2026-04-03 13:22:30 +08:00
|
|
|
const bool useCenterPivot = m_pivotMode == SceneViewportPivotMode::Center;
|
|
|
|
|
const bool localSpace = m_transformSpaceMode == SceneViewportTransformSpaceMode::Local;
|
2026-04-04 01:09:02 +08:00
|
|
|
const SceneViewportTransformGizmoFrameOptions gizmoFrameOptions =
|
|
|
|
|
BuildSceneViewportTransformGizmoFrameOptions(
|
|
|
|
|
useCenterPivot,
|
|
|
|
|
localSpace,
|
|
|
|
|
usingTransformTool,
|
|
|
|
|
showingMoveGizmo,
|
|
|
|
|
showingRotateGizmo,
|
|
|
|
|
showingScaleGizmo);
|
2026-04-03 13:22:30 +08:00
|
|
|
const Math::Vector2 viewportSize(content.availableSize.x, content.availableSize.y);
|
|
|
|
|
const Math::Vector2 localMousePosition(
|
|
|
|
|
io.MousePos.x - content.itemMin.x,
|
|
|
|
|
io.MousePos.y - content.itemMin.y);
|
2026-03-29 16:18:13 +08:00
|
|
|
SceneViewportOverlayData overlay = {};
|
2026-04-04 01:09:02 +08:00
|
|
|
SceneViewportTransformGizmoFrameUpdate interactionGizmoFrame = {};
|
2026-04-03 13:22:30 +08:00
|
|
|
SceneViewportTransformGizmoFrameState gizmoFrameState = {};
|
|
|
|
|
SceneViewportOverlayFrameData emptySceneOverlayFrameData = {};
|
2026-03-29 16:18:13 +08:00
|
|
|
|
|
|
|
|
if (hasInteractiveViewport) {
|
|
|
|
|
overlay = viewportHostService->GetSceneViewOverlayData();
|
2026-04-04 01:09:02 +08:00
|
|
|
interactionGizmoFrame = RefreshAndSubmitSceneViewportTransformGizmoFrame(
|
|
|
|
|
*viewportHostService,
|
|
|
|
|
BuildSceneViewportTransformGizmoRefreshRequest(
|
|
|
|
|
*m_context,
|
|
|
|
|
overlay,
|
|
|
|
|
viewportSize,
|
|
|
|
|
localMousePosition,
|
|
|
|
|
gizmoFrameOptions),
|
2026-04-03 13:22:30 +08:00
|
|
|
m_moveGizmo,
|
|
|
|
|
m_rotateGizmo,
|
|
|
|
|
m_scaleGizmo);
|
2026-04-04 01:09:02 +08:00
|
|
|
gizmoFrameState = interactionGizmoFrame.frameState;
|
2026-03-31 23:45:08 +08:00
|
|
|
} else {
|
2026-04-04 01:09:02 +08:00
|
|
|
CancelSceneViewportTransformGizmoFrame(
|
|
|
|
|
*m_context,
|
|
|
|
|
m_moveGizmo,
|
|
|
|
|
m_rotateGizmo,
|
|
|
|
|
m_scaleGizmo);
|
2026-03-29 16:18:13 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-04 00:45:13 +08:00
|
|
|
const SceneViewportTransformGizmoOverlaySubmission interactionGizmoSubmission =
|
2026-04-03 13:22:30 +08:00
|
|
|
hasInteractiveViewport
|
2026-04-04 01:09:02 +08:00
|
|
|
? interactionGizmoFrame.overlaySubmission
|
2026-04-04 00:45:13 +08:00
|
|
|
: SceneViewportTransformGizmoOverlaySubmission{};
|
2026-04-03 13:22:30 +08:00
|
|
|
const SceneViewportOverlayFrameData& interactionOverlayFrameData =
|
|
|
|
|
hasInteractiveViewport
|
2026-04-03 16:50:46 +08:00
|
|
|
? viewportHostService->GetSceneViewEditorOverlayFrameData(*m_context)
|
2026-04-03 13:22:30 +08:00
|
|
|
: emptySceneOverlayFrameData;
|
2026-04-04 00:45:13 +08:00
|
|
|
const SceneViewportActiveGizmoKind activeGizmoKind = interactionGizmoSubmission.activeGizmoKind;
|
|
|
|
|
const bool gizmoActive = interactionGizmoSubmission.GizmoActive();
|
2026-04-03 17:04:34 +08:00
|
|
|
const SceneViewportHudOverlayData interactionHudOverlay =
|
|
|
|
|
BuildSceneViewportHudOverlayData(overlay);
|
2026-04-03 17:16:16 +08:00
|
|
|
SceneViewportInteractionResult hoveredInteraction = {};
|
2026-04-03 13:22:30 +08:00
|
|
|
const bool canResolveViewportInteraction =
|
2026-03-29 16:18:13 +08:00
|
|
|
hasInteractiveViewport &&
|
2026-04-03 13:22:30 +08:00
|
|
|
viewportContentHovered &&
|
|
|
|
|
!usingViewMoveTool &&
|
2026-03-29 16:18:13 +08:00
|
|
|
!m_lookDragging &&
|
|
|
|
|
!m_panDragging &&
|
2026-04-01 16:42:57 +08:00
|
|
|
!toolOverlay.hovered &&
|
2026-04-03 13:22:30 +08:00
|
|
|
!gizmoActive;
|
|
|
|
|
if (canResolveViewportInteraction) {
|
2026-04-03 17:16:16 +08:00
|
|
|
SceneViewportInteractionResolveRequest interactionRequest = {};
|
|
|
|
|
interactionRequest.overlayFrameData = &interactionOverlayFrameData;
|
|
|
|
|
interactionRequest.viewportSize = viewportSize;
|
|
|
|
|
interactionRequest.localMousePosition = localMousePosition;
|
|
|
|
|
interactionRequest.hudOverlay = &interactionHudOverlay;
|
|
|
|
|
interactionRequest.viewportMin = content.itemMin;
|
|
|
|
|
interactionRequest.viewportMax = content.itemMax;
|
|
|
|
|
interactionRequest.absoluteMousePosition = io.MousePos;
|
|
|
|
|
hoveredInteraction = ResolveSceneViewportInteraction(interactionRequest);
|
2026-04-03 13:22:30 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-04 00:21:25 +08:00
|
|
|
ApplySceneViewportHoveredHandleState(
|
|
|
|
|
BuildSceneViewportHoveredHandleState(hoveredInteraction),
|
|
|
|
|
gizmoActive,
|
|
|
|
|
showingMoveGizmo,
|
|
|
|
|
m_moveGizmo,
|
|
|
|
|
showingRotateGizmo,
|
|
|
|
|
m_rotateGizmo,
|
|
|
|
|
showingScaleGizmo,
|
|
|
|
|
m_scaleGizmo);
|
|
|
|
|
|
|
|
|
|
const SceneViewportInteractionActions interactionActions =
|
|
|
|
|
BuildSceneViewportInteractionActions(
|
|
|
|
|
hoveredInteraction,
|
|
|
|
|
hasInteractiveViewport,
|
|
|
|
|
content.clickedLeft,
|
|
|
|
|
canResolveViewportInteraction);
|
2026-04-01 16:42:57 +08:00
|
|
|
const bool beginLeftPanDrag = usingViewMoveTool
|
|
|
|
|
? ShouldBeginSceneViewportNavigationDrag(
|
|
|
|
|
hasInteractiveViewport,
|
|
|
|
|
viewportContentHovered,
|
|
|
|
|
m_panDragging,
|
|
|
|
|
m_lookDragging,
|
|
|
|
|
gizmoActive,
|
|
|
|
|
ImGuiMouseButton_Left)
|
|
|
|
|
: false;
|
|
|
|
|
const bool beginLookDrag = ShouldBeginSceneViewportNavigationDrag(
|
|
|
|
|
hasInteractiveViewport,
|
|
|
|
|
viewportContentHovered,
|
|
|
|
|
m_lookDragging,
|
|
|
|
|
m_panDragging,
|
|
|
|
|
gizmoActive,
|
|
|
|
|
ImGuiMouseButton_Right);
|
|
|
|
|
const bool beginMiddlePanDrag = ShouldBeginSceneViewportNavigationDrag(
|
|
|
|
|
hasInteractiveViewport,
|
|
|
|
|
viewportContentHovered,
|
|
|
|
|
m_panDragging,
|
|
|
|
|
m_lookDragging,
|
|
|
|
|
gizmoActive,
|
|
|
|
|
ImGuiMouseButton_Middle);
|
|
|
|
|
const bool beginPanDrag = beginLeftPanDrag || beginMiddlePanDrag;
|
|
|
|
|
|
2026-04-04 00:21:25 +08:00
|
|
|
if (toolOverlay.clicked || interactionActions.HasClickAction() || beginLookDrag ||
|
2026-04-01 16:42:57 +08:00
|
|
|
beginPanDrag) {
|
2026-03-29 15:12:38 +08:00
|
|
|
ImGui::SetWindowFocus();
|
|
|
|
|
}
|
2026-03-28 18:21:18 +08:00
|
|
|
|
2026-04-04 00:45:13 +08:00
|
|
|
ExecuteSceneViewportTransformGizmoLifecycleCommand(
|
|
|
|
|
BuildBeginSceneViewportTransformGizmoLifecycleCommand(interactionActions),
|
|
|
|
|
m_context->GetUndoManager(),
|
|
|
|
|
gizmoFrameState,
|
|
|
|
|
m_moveGizmo,
|
|
|
|
|
m_rotateGizmo,
|
|
|
|
|
m_scaleGizmo);
|
2026-03-29 16:18:13 +08:00
|
|
|
|
2026-04-04 00:21:25 +08:00
|
|
|
DispatchSceneViewportInteractionActions(
|
|
|
|
|
interactionActions,
|
|
|
|
|
*m_context,
|
|
|
|
|
*viewportHostService,
|
|
|
|
|
content.availableSize,
|
|
|
|
|
localMousePosition);
|
2026-03-29 15:12:38 +08:00
|
|
|
|
2026-04-04 00:45:13 +08:00
|
|
|
ExecuteSceneViewportTransformGizmoLifecycleCommand(
|
|
|
|
|
BuildFrameSceneViewportTransformGizmoLifecycleCommand(
|
|
|
|
|
activeGizmoKind,
|
|
|
|
|
ImGui::IsMouseDown(ImGuiMouseButton_Left)),
|
|
|
|
|
m_context->GetUndoManager(),
|
|
|
|
|
gizmoFrameState,
|
|
|
|
|
m_moveGizmo,
|
|
|
|
|
m_rotateGizmo,
|
|
|
|
|
m_scaleGizmo);
|
2026-03-29 16:18:13 +08:00
|
|
|
|
2026-03-29 15:12:38 +08:00
|
|
|
if (beginLookDrag) {
|
2026-03-28 18:21:18 +08:00
|
|
|
m_lookDragging = true;
|
2026-03-31 21:26:40 +08:00
|
|
|
m_panDragging = false;
|
2026-04-01 16:42:57 +08:00
|
|
|
m_loggedLookDelta = false;
|
|
|
|
|
m_loggedPanDelta = false;
|
2026-03-28 18:21:18 +08:00
|
|
|
}
|
2026-03-29 15:12:38 +08:00
|
|
|
if (beginPanDrag) {
|
2026-03-28 17:40:14 +08:00
|
|
|
m_panDragging = true;
|
2026-03-31 21:26:40 +08:00
|
|
|
m_lookDragging = false;
|
2026-04-01 16:42:57 +08:00
|
|
|
m_panDragButton = beginLeftPanDrag ? ImGuiMouseButton_Left : ImGuiMouseButton_Middle;
|
|
|
|
|
m_loggedPanDelta = false;
|
|
|
|
|
m_loggedLookDelta = false;
|
2026-03-28 17:40:14 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-28 18:37:18 +08:00
|
|
|
if (m_lookDragging && !ImGui::IsMouseDown(ImGuiMouseButton_Right)) {
|
2026-03-28 18:21:18 +08:00
|
|
|
m_lookDragging = false;
|
2026-04-01 16:42:57 +08:00
|
|
|
m_loggedLookDelta = false;
|
2026-03-28 18:21:18 +08:00
|
|
|
}
|
2026-04-01 16:42:57 +08:00
|
|
|
if (m_panDragging && !ImGui::IsMouseDown(m_panDragButton)) {
|
2026-03-28 17:40:14 +08:00
|
|
|
m_panDragging = false;
|
2026-04-01 16:42:57 +08:00
|
|
|
m_panDragButton = ImGuiMouseButton_Middle;
|
|
|
|
|
m_loggedPanDelta = false;
|
2026-03-29 15:12:38 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-01 16:42:57 +08:00
|
|
|
if (m_lookDragging || m_panDragging || m_moveGizmo.IsActive() || m_rotateGizmo.IsActive() ||
|
|
|
|
|
m_scaleGizmo.IsActive()) {
|
2026-03-29 15:12:38 +08:00
|
|
|
ImGui::SetNextFrameWantCaptureMouse(true);
|
|
|
|
|
}
|
|
|
|
|
if (m_lookDragging) {
|
|
|
|
|
ImGui::SetNextFrameWantCaptureKeyboard(true);
|
2026-03-28 17:40:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SceneViewportInput input = {};
|
|
|
|
|
input.viewportSize = content.availableSize;
|
2026-03-28 18:28:11 +08:00
|
|
|
input.deltaTime = io.DeltaTime;
|
2026-04-01 16:42:57 +08:00
|
|
|
input.hovered = viewportContentHovered;
|
2026-03-29 15:12:38 +08:00
|
|
|
input.focused = content.focused || m_lookDragging || m_panDragging;
|
2026-04-01 16:42:57 +08:00
|
|
|
input.mouseWheel = (viewportContentHovered && !m_lookDragging) ? io.MouseWheel : 0.0f;
|
|
|
|
|
input.flySpeedDelta = (viewportContentHovered && m_lookDragging) ? io.MouseWheel : 0.0f;
|
2026-03-28 18:21:18 +08:00
|
|
|
input.looking = m_lookDragging;
|
2026-03-28 18:37:18 +08:00
|
|
|
input.orbiting = false;
|
2026-03-28 17:40:14 +08:00
|
|
|
input.panning = m_panDragging;
|
2026-03-28 18:28:11 +08:00
|
|
|
input.fastMove = io.KeyShift;
|
2026-03-28 17:40:14 +08:00
|
|
|
input.focusSelectionRequested =
|
2026-03-29 15:12:38 +08:00
|
|
|
input.focused && !io.WantTextInput && ImGui::IsKeyPressed(ImGuiKey_F, false);
|
2026-03-28 17:40:14 +08:00
|
|
|
|
2026-03-29 15:12:38 +08:00
|
|
|
if (m_lookDragging && !io.WantTextInput) {
|
2026-03-28 18:28:11 +08:00
|
|
|
input.moveForward =
|
|
|
|
|
(ImGui::IsKeyDown(ImGuiKey_W) ? 1.0f : 0.0f) -
|
|
|
|
|
(ImGui::IsKeyDown(ImGuiKey_S) ? 1.0f : 0.0f);
|
|
|
|
|
input.moveRight =
|
|
|
|
|
(ImGui::IsKeyDown(ImGuiKey_D) ? 1.0f : 0.0f) -
|
|
|
|
|
(ImGui::IsKeyDown(ImGuiKey_A) ? 1.0f : 0.0f);
|
|
|
|
|
input.moveUp =
|
|
|
|
|
(ImGui::IsKeyDown(ImGuiKey_E) ? 1.0f : 0.0f) -
|
|
|
|
|
(ImGui::IsKeyDown(ImGuiKey_Q) ? 1.0f : 0.0f);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-28 18:37:18 +08:00
|
|
|
if (m_lookDragging || m_panDragging) {
|
2026-03-29 15:12:38 +08:00
|
|
|
if (m_lookDragging) {
|
2026-04-01 16:42:57 +08:00
|
|
|
input.mouseDelta = io.MouseDelta;
|
|
|
|
|
if (!m_loggedLookDelta &&
|
|
|
|
|
(input.mouseDelta.x != 0.0f || input.mouseDelta.y != 0.0f)) {
|
|
|
|
|
m_loggedLookDelta = true;
|
|
|
|
|
}
|
2026-03-29 15:12:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_panDragging) {
|
2026-04-01 16:42:57 +08:00
|
|
|
input.mouseDelta = io.MouseDelta;
|
|
|
|
|
if (!m_loggedPanDelta &&
|
|
|
|
|
(input.mouseDelta.x != 0.0f || input.mouseDelta.y != 0.0f)) {
|
|
|
|
|
m_loggedPanDelta = true;
|
2026-03-31 23:45:08 +08:00
|
|
|
}
|
2026-03-29 15:12:38 +08:00
|
|
|
}
|
2026-03-28 17:40:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
viewportHostService->UpdateSceneViewInput(*m_context, input);
|
2026-03-28 17:50:54 +08:00
|
|
|
|
|
|
|
|
if (content.hasViewportArea && content.frame.hasTexture) {
|
2026-03-29 16:18:13 +08:00
|
|
|
overlay = viewportHostService->GetSceneViewOverlayData();
|
2026-04-04 01:09:02 +08:00
|
|
|
RefreshAndSubmitSceneViewportTransformGizmoFrame(
|
|
|
|
|
*viewportHostService,
|
|
|
|
|
BuildSceneViewportTransformGizmoRefreshRequest(
|
2026-04-03 13:22:30 +08:00
|
|
|
*m_context,
|
|
|
|
|
overlay,
|
|
|
|
|
viewportSize,
|
|
|
|
|
localMousePosition,
|
2026-04-04 01:09:02 +08:00
|
|
|
gizmoFrameOptions),
|
|
|
|
|
m_moveGizmo,
|
|
|
|
|
m_rotateGizmo,
|
|
|
|
|
m_scaleGizmo);
|
2026-03-31 23:45:08 +08:00
|
|
|
|
2026-04-03 17:04:34 +08:00
|
|
|
DrawSceneViewportHudOverlay(
|
2026-03-28 17:50:54 +08:00
|
|
|
ImGui::GetWindowDrawList(),
|
2026-04-03 17:04:34 +08:00
|
|
|
BuildSceneViewportHudOverlayData(overlay),
|
2026-03-28 17:50:54 +08:00
|
|
|
content.itemMin,
|
2026-04-03 17:04:34 +08:00
|
|
|
content.itemMax);
|
2026-03-28 17:50:54 +08:00
|
|
|
}
|
2026-03-28 17:40:14 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-26 22:31:22 +08:00
|
|
|
Actions::ObserveInactiveActionRoute(*m_context);
|
2026-03-20 17:08:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2026-03-24 20:02:38 +08:00
|
|
|
}
|