Files
XCEngine/editor/src/panels/SceneViewPanel.cpp

764 lines
32 KiB
C++
Raw Normal View History

2026-03-26 22:31:22 +08:00
#include "Actions/ActionRouting.h"
#include "Core/IEditorContext.h"
2026-03-29 16:18:13 +08:00
#include "Core/ISceneManager.h"
#include "Core/ISelectionManager.h"
#include "SceneViewPanel.h"
#include "Viewport/SceneViewportOrientationGizmo.h"
#include "Viewport/SceneViewportOverlayRenderer.h"
2026-03-28 17:04:14 +08:00
#include "ViewportPanelContent.h"
#include "Platform/Win32Utf8.h"
2026-03-26 21:18:33 +08:00
#include "UI/UI.h"
2026-03-28 17:50:54 +08:00
#include <XCEngine/Debug/Logger.h>
#include <imgui.h>
#include <cstdarg>
#include <cstdio>
#include <filesystem>
namespace XCEngine {
namespace Editor {
2026-03-29 16:18:13 +08:00
namespace {
struct SceneViewportToolOverlayResult {
bool hovered = false;
bool clicked = false;
SceneViewportToolMode clickedTool = SceneViewportToolMode::Move;
};
enum class SceneViewportActiveGizmoKind : uint8_t {
None = 0,
Move,
Rotate,
Scale
};
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 "";
}
}
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;
}
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(GetSceneViewportToolIconBaseName(toolMode))));
if (active) {
iconPath += L"_on";
}
iconPath += L".png";
cachedPath = XCEngine::Editor::Platform::WideToUtf8(iconPath.lexically_normal().wstring());
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;
}
void LogSceneViewNavigation(Debug::Logger& logger, const char* format, ...) {
char buffer[512] = {};
va_list args;
va_start(args, format);
std::vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
logger.Info(Debug::LogCategory::General, buffer);
}
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
SceneViewportMoveGizmoContext BuildMoveGizmoContext(
IEditorContext& context,
const SceneViewportOverlayData& overlay,
const ViewportPanelContentResult& content,
const ImVec2& mousePosition) {
SceneViewportMoveGizmoContext gizmoContext = {};
gizmoContext.overlay = overlay;
gizmoContext.viewportSize = Math::Vector2(content.availableSize.x, content.availableSize.y);
gizmoContext.mousePosition = Math::Vector2(
mousePosition.x - content.itemMin.x,
mousePosition.y - content.itemMin.y);
if (context.GetSelectionManager().GetSelectionCount() == 1) {
const uint64_t selectedEntity = context.GetSelectionManager().GetSelectedEntity();
if (selectedEntity != 0) {
gizmoContext.selectedObject = context.GetSceneManager().GetEntity(selectedEntity);
}
}
return gizmoContext;
}
2026-03-31 23:45:08 +08:00
SceneViewportRotateGizmoContext BuildRotateGizmoContext(
IEditorContext& context,
const SceneViewportOverlayData& overlay,
const ViewportPanelContentResult& content,
const ImVec2& mousePosition) {
SceneViewportRotateGizmoContext gizmoContext = {};
gizmoContext.overlay = overlay;
gizmoContext.viewportSize = Math::Vector2(content.availableSize.x, content.availableSize.y);
gizmoContext.mousePosition = Math::Vector2(
mousePosition.x - content.itemMin.x,
mousePosition.y - content.itemMin.y);
if (context.GetSelectionManager().GetSelectionCount() == 1) {
const uint64_t selectedEntity = context.GetSelectionManager().GetSelectedEntity();
if (selectedEntity != 0) {
gizmoContext.selectedObject = context.GetSceneManager().GetEntity(selectedEntity);
}
}
return gizmoContext;
}
SceneViewportScaleGizmoContext BuildScaleGizmoContext(
IEditorContext& context,
const SceneViewportOverlayData& overlay,
const ViewportPanelContentResult& content,
const ImVec2& mousePosition) {
SceneViewportScaleGizmoContext gizmoContext = {};
gizmoContext.overlay = overlay;
gizmoContext.viewportSize = Math::Vector2(content.availableSize.x, content.availableSize.y);
gizmoContext.mousePosition = Math::Vector2(
mousePosition.x - content.itemMin.x,
mousePosition.y - content.itemMin.y);
if (context.GetSelectionManager().GetSelectionCount() == 1) {
const uint64_t selectedEntity = context.GetSelectionManager().GetSelectedEntity();
if (selectedEntity != 0) {
gizmoContext.selectedObject = context.GetSceneManager().GetEntity(selectedEntity);
}
}
return gizmoContext;
}
2026-03-29 16:18:13 +08:00
} // namespace
SceneViewPanel::SceneViewPanel() : Panel("Scene") {}
void SceneViewPanel::Render() {
2026-03-29 16:18:13 +08:00
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
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;
}
const ViewportPanelContentResult content = RenderViewportPanelContent(*m_context, EditorViewportKind::Scene);
if (IViewportHostService* viewportHostService = m_context->GetViewportHostService()) {
const ImGuiIO& io = ImGui::GetIO();
auto& logger = Debug::Logger::Get();
2026-03-29 16:18:13 +08:00
const bool hasInteractiveViewport = content.hasViewportArea && content.frame.hasTexture;
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
if (content.focused && !io.WantTextInput && !m_lookDragging && !m_panDragging) {
if (ImGui::IsKeyPressed(ImGuiKey_W, false)) {
if (m_rotateGizmo.IsActive()) {
m_rotateGizmo.CancelDrag(&m_context->GetUndoManager());
}
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());
}
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
}
}
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-03-29 16:18:13 +08:00
SceneViewportOverlayData overlay = {};
SceneViewportMoveGizmoContext moveGizmoContext = {};
2026-03-31 23:45:08 +08:00
SceneViewportRotateGizmoContext rotateGizmoContext = {};
SceneViewportScaleGizmoContext scaleGizmoContext = {};
SceneViewportActiveGizmoKind activeGizmoKind = SceneViewportActiveGizmoKind::None;
2026-03-29 16:18:13 +08:00
if (hasInteractiveViewport) {
overlay = viewportHostService->GetSceneViewOverlayData();
if (showingMoveGizmo) {
2026-03-31 23:45:08 +08:00
moveGizmoContext = BuildMoveGizmoContext(*m_context, overlay, content, io.MousePos);
if (m_moveGizmo.IsActive() &&
(moveGizmoContext.selectedObject == nullptr ||
m_context->GetSelectionManager().GetSelectedEntity() != m_moveGizmo.GetActiveEntityId())) {
m_moveGizmo.CancelDrag(&m_context->GetUndoManager());
}
} else if (m_moveGizmo.IsActive()) {
m_moveGizmo.CancelDrag(&m_context->GetUndoManager());
}
2026-03-31 23:45:08 +08:00
if (showingRotateGizmo) {
2026-03-31 23:45:08 +08:00
rotateGizmoContext = BuildRotateGizmoContext(*m_context, overlay, content, io.MousePos);
if (m_rotateGizmo.IsActive() &&
(rotateGizmoContext.selectedObject == nullptr ||
m_context->GetSelectionManager().GetSelectedEntity() != m_rotateGizmo.GetActiveEntityId())) {
m_rotateGizmo.CancelDrag(&m_context->GetUndoManager());
}
} else if (m_rotateGizmo.IsActive()) {
m_rotateGizmo.CancelDrag(&m_context->GetUndoManager());
}
if (showingScaleGizmo) {
scaleGizmoContext = BuildScaleGizmoContext(*m_context, overlay, content, io.MousePos);
scaleGizmoContext.uniformOnly = usingTransformTool;
if (m_scaleGizmo.IsActive() &&
(scaleGizmoContext.selectedObject == nullptr ||
m_context->GetSelectionManager().GetSelectedEntity() != m_scaleGizmo.GetActiveEntityId())) {
m_scaleGizmo.CancelDrag(&m_context->GetUndoManager());
}
} else if (m_scaleGizmo.IsActive()) {
m_scaleGizmo.CancelDrag(&m_context->GetUndoManager());
}
if (m_moveGizmo.IsActive()) {
activeGizmoKind = SceneViewportActiveGizmoKind::Move;
} else if (m_rotateGizmo.IsActive()) {
activeGizmoKind = SceneViewportActiveGizmoKind::Rotate;
} else if (m_scaleGizmo.IsActive()) {
activeGizmoKind = SceneViewportActiveGizmoKind::Scale;
}
if (showingMoveGizmo) {
SceneViewportMoveGizmoContext updateContext = moveGizmoContext;
if (activeGizmoKind != SceneViewportActiveGizmoKind::None &&
activeGizmoKind != SceneViewportActiveGizmoKind::Move) {
updateContext.mousePosition = Math::Vector2(-1.0f, -1.0f);
}
m_moveGizmo.Update(updateContext);
}
if (showingRotateGizmo) {
SceneViewportRotateGizmoContext updateContext = rotateGizmoContext;
if (activeGizmoKind != SceneViewportActiveGizmoKind::None &&
activeGizmoKind != SceneViewportActiveGizmoKind::Rotate) {
updateContext.mousePosition = Math::Vector2(-1.0f, -1.0f);
}
m_rotateGizmo.Update(updateContext);
}
if (showingScaleGizmo) {
SceneViewportScaleGizmoContext updateContext = scaleGizmoContext;
if (activeGizmoKind != SceneViewportActiveGizmoKind::None &&
activeGizmoKind != SceneViewportActiveGizmoKind::Scale) {
updateContext.mousePosition = Math::Vector2(-1.0f, -1.0f);
}
m_scaleGizmo.Update(updateContext);
2026-03-31 23:45:08 +08:00
}
} else {
if (m_moveGizmo.IsActive()) {
2026-03-29 16:18:13 +08:00
m_moveGizmo.CancelDrag(&m_context->GetUndoManager());
}
2026-03-31 23:45:08 +08:00
if (m_rotateGizmo.IsActive()) {
m_rotateGizmo.CancelDrag(&m_context->GetUndoManager());
}
if (m_scaleGizmo.IsActive()) {
m_scaleGizmo.CancelDrag(&m_context->GetUndoManager());
}
2026-03-29 16:18:13 +08:00
}
const bool moveGizmoHovering = showingMoveGizmo && m_moveGizmo.IsHoveringHandle();
const bool rotateGizmoHovering = showingRotateGizmo && m_rotateGizmo.IsHoveringHandle();
const bool scaleGizmoHovering = showingScaleGizmo && m_scaleGizmo.IsHoveringHandle();
const bool moveGizmoActive = showingMoveGizmo && m_moveGizmo.IsActive();
const bool rotateGizmoActive = showingRotateGizmo && m_rotateGizmo.IsActive();
const bool scaleGizmoActive = showingScaleGizmo && m_scaleGizmo.IsActive();
const SceneViewportActiveGizmoKind hoveredGizmoKind = scaleGizmoHovering
? SceneViewportActiveGizmoKind::Scale
: (moveGizmoHovering ? SceneViewportActiveGizmoKind::Move
: (rotateGizmoHovering ? SceneViewportActiveGizmoKind::Rotate
: SceneViewportActiveGizmoKind::None));
if (moveGizmoActive) {
activeGizmoKind = SceneViewportActiveGizmoKind::Move;
} else if (rotateGizmoActive) {
activeGizmoKind = SceneViewportActiveGizmoKind::Rotate;
} else if (scaleGizmoActive) {
activeGizmoKind = SceneViewportActiveGizmoKind::Scale;
} else {
activeGizmoKind = SceneViewportActiveGizmoKind::None;
}
const bool gizmoHovering = hoveredGizmoKind != SceneViewportActiveGizmoKind::None;
const bool gizmoActive = activeGizmoKind != SceneViewportActiveGizmoKind::None;
2026-03-31 23:45:08 +08:00
const bool beginTransformGizmo =
2026-03-29 16:18:13 +08:00
hasInteractiveViewport &&
2026-03-31 23:45:08 +08:00
content.clickedLeft &&
2026-03-29 16:18:13 +08:00
!m_lookDragging &&
!m_panDragging &&
!toolOverlay.hovered &&
2026-03-31 23:45:08 +08:00
gizmoHovering;
const SceneViewportOrientationAxis orientationAxisHit =
hasInteractiveViewport &&
viewportContentHovered &&
!usingViewMoveTool &&
!m_lookDragging &&
!m_panDragging &&
2026-03-31 23:45:08 +08:00
!gizmoHovering &&
!gizmoActive
? HitTestSceneViewportOrientationGizmo(
overlay,
content.itemMin,
content.itemMax,
io.MousePos)
: SceneViewportOrientationAxis::None;
const bool orientationGizmoClick =
hasInteractiveViewport &&
2026-03-31 23:45:08 +08:00
content.clickedLeft &&
orientationAxisHit != SceneViewportOrientationAxis::None;
const bool selectClick =
2026-03-29 16:18:13 +08:00
hasInteractiveViewport &&
2026-03-31 23:45:08 +08:00
content.clickedLeft &&
viewportContentHovered &&
!usingViewMoveTool &&
!m_lookDragging &&
2026-03-29 16:18:13 +08:00
!m_panDragging &&
!orientationGizmoClick &&
2026-03-31 23:45:08 +08:00
!gizmoHovering &&
!gizmoActive;
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;
if (ImGui::IsMouseClicked(ImGuiMouseButton_Right) || ImGui::IsMouseClicked(ImGuiMouseButton_Middle)) {
LogSceneViewNavigation(
logger,
"SceneView nav click hovered=%d focused=%d hasViewport=%d clickedR=%d clickedM=%d downR=%d downM=%d "
"look=%d pan=%d gizmoActive=%d ioDelta=(%.2f, %.2f)",
content.hovered ? 1 : 0,
content.focused ? 1 : 0,
hasInteractiveViewport ? 1 : 0,
ImGui::IsMouseClicked(ImGuiMouseButton_Right) ? 1 : 0,
ImGui::IsMouseClicked(ImGuiMouseButton_Middle) ? 1 : 0,
ImGui::IsMouseDown(ImGuiMouseButton_Right) ? 1 : 0,
ImGui::IsMouseDown(ImGuiMouseButton_Middle) ? 1 : 0,
m_lookDragging ? 1 : 0,
m_panDragging ? 1 : 0,
gizmoActive ? 1 : 0,
io.MouseDelta.x,
io.MouseDelta.y);
}
if (toolOverlay.clicked || beginTransformGizmo || orientationGizmoClick || selectClick || beginLookDrag ||
beginPanDrag) {
ImGui::SetWindowFocus();
}
2026-03-31 23:45:08 +08:00
if (beginTransformGizmo) {
if (hoveredGizmoKind == SceneViewportActiveGizmoKind::Scale) {
m_scaleGizmo.TryBeginDrag(scaleGizmoContext, m_context->GetUndoManager());
} else if (hoveredGizmoKind == SceneViewportActiveGizmoKind::Move) {
2026-03-31 23:45:08 +08:00
m_moveGizmo.TryBeginDrag(moveGizmoContext, m_context->GetUndoManager());
} else if (hoveredGizmoKind == SceneViewportActiveGizmoKind::Rotate) {
2026-03-31 23:45:08 +08:00
m_rotateGizmo.TryBeginDrag(rotateGizmoContext, m_context->GetUndoManager());
}
2026-03-29 16:18:13 +08:00
}
if (orientationGizmoClick) {
viewportHostService->AlignSceneViewToOrientationAxis(orientationAxisHit);
overlay = viewportHostService->GetSceneViewOverlayData();
if (showingMoveGizmo) {
2026-03-31 23:45:08 +08:00
moveGizmoContext = BuildMoveGizmoContext(*m_context, overlay, content, io.MousePos);
m_moveGizmo.Update(moveGizmoContext);
}
if (showingRotateGizmo) {
2026-03-31 23:45:08 +08:00
rotateGizmoContext = BuildRotateGizmoContext(*m_context, overlay, content, io.MousePos);
m_rotateGizmo.Update(rotateGizmoContext);
}
if (showingScaleGizmo) {
scaleGizmoContext = BuildScaleGizmoContext(*m_context, overlay, content, io.MousePos);
scaleGizmoContext.uniformOnly = usingTransformTool;
m_scaleGizmo.Update(scaleGizmoContext);
}
}
if (selectClick) {
const ImVec2 localMousePosition(
io.MousePos.x - content.itemMin.x,
io.MousePos.y - content.itemMin.y);
const uint64_t selectedEntity = viewportHostService->PickSceneViewEntity(
*m_context,
content.availableSize,
localMousePosition);
if (selectedEntity != 0) {
m_context->GetSelectionManager().SetSelectedEntity(selectedEntity);
} else {
m_context->GetSelectionManager().ClearSelection();
}
}
2026-03-31 23:45:08 +08:00
if (gizmoActive) {
2026-03-29 16:18:13 +08:00
if (ImGui::IsMouseDown(ImGuiMouseButton_Left)) {
if (activeGizmoKind == SceneViewportActiveGizmoKind::Move) {
2026-03-31 23:45:08 +08:00
m_moveGizmo.UpdateDrag(moveGizmoContext);
} else if (activeGizmoKind == SceneViewportActiveGizmoKind::Rotate) {
2026-03-31 23:45:08 +08:00
m_rotateGizmo.UpdateDrag(rotateGizmoContext);
} else if (activeGizmoKind == SceneViewportActiveGizmoKind::Scale) {
m_scaleGizmo.UpdateDrag(scaleGizmoContext);
2026-03-31 23:45:08 +08:00
}
2026-03-29 16:18:13 +08:00
} else {
if (activeGizmoKind == SceneViewportActiveGizmoKind::Move) {
2026-03-31 23:45:08 +08:00
m_moveGizmo.EndDrag(m_context->GetUndoManager());
} else if (activeGizmoKind == SceneViewportActiveGizmoKind::Rotate) {
2026-03-31 23:45:08 +08:00
m_rotateGizmo.EndDrag(m_context->GetUndoManager());
} else if (activeGizmoKind == SceneViewportActiveGizmoKind::Scale) {
m_scaleGizmo.EndDrag(m_context->GetUndoManager());
2026-03-31 23:45:08 +08:00
}
2026-03-29 16:18:13 +08:00
}
}
if (beginLookDrag) {
m_lookDragging = true;
m_panDragging = false;
m_loggedLookDelta = false;
m_loggedPanDelta = false;
LogSceneViewNavigation(
logger,
"SceneView begin look drag hovered=%d focused=%d downR=%d downM=%d gizmoActive=%d",
content.hovered ? 1 : 0,
content.focused ? 1 : 0,
ImGui::IsMouseDown(ImGuiMouseButton_Right) ? 1 : 0,
ImGui::IsMouseDown(ImGuiMouseButton_Middle) ? 1 : 0,
gizmoActive ? 1 : 0);
}
if (beginPanDrag) {
m_panDragging = true;
m_lookDragging = false;
m_panDragButton = beginLeftPanDrag ? ImGuiMouseButton_Left : ImGuiMouseButton_Middle;
m_loggedPanDelta = false;
m_loggedLookDelta = false;
LogSceneViewNavigation(
logger,
"SceneView begin pan drag hovered=%d focused=%d button=%d downR=%d downM=%d downL=%d gizmoActive=%d",
content.hovered ? 1 : 0,
content.focused ? 1 : 0,
m_panDragButton,
ImGui::IsMouseDown(ImGuiMouseButton_Right) ? 1 : 0,
ImGui::IsMouseDown(ImGuiMouseButton_Middle) ? 1 : 0,
ImGui::IsMouseDown(ImGuiMouseButton_Left) ? 1 : 0,
gizmoActive ? 1 : 0);
}
if (m_lookDragging && !ImGui::IsMouseDown(ImGuiMouseButton_Right)) {
LogSceneViewNavigation(logger, "SceneView end look drag");
m_lookDragging = false;
m_loggedLookDelta = false;
}
if (m_panDragging && !ImGui::IsMouseDown(m_panDragButton)) {
LogSceneViewNavigation(logger, "SceneView end pan drag");
m_panDragging = false;
m_panDragButton = ImGuiMouseButton_Middle;
m_loggedPanDelta = false;
}
if (m_lookDragging || m_panDragging || m_moveGizmo.IsActive() || m_rotateGizmo.IsActive() ||
m_scaleGizmo.IsActive()) {
ImGui::SetNextFrameWantCaptureMouse(true);
}
if (m_lookDragging) {
ImGui::SetNextFrameWantCaptureKeyboard(true);
}
SceneViewportInput input = {};
input.viewportSize = content.availableSize;
2026-03-28 18:28:11 +08:00
input.deltaTime = io.DeltaTime;
input.hovered = viewportContentHovered;
input.focused = content.focused || m_lookDragging || m_panDragging;
input.mouseWheel = (viewportContentHovered && !m_lookDragging) ? io.MouseWheel : 0.0f;
input.flySpeedDelta = (viewportContentHovered && m_lookDragging) ? io.MouseWheel : 0.0f;
input.looking = m_lookDragging;
input.orbiting = false;
input.panning = m_panDragging;
2026-03-28 18:28:11 +08:00
input.fastMove = io.KeyShift;
input.focusSelectionRequested =
input.focused && !io.WantTextInput && ImGui::IsKeyPressed(ImGuiKey_F, false);
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);
}
if (m_lookDragging || m_panDragging) {
if (m_lookDragging) {
input.mouseDelta = io.MouseDelta;
if (!m_loggedLookDelta &&
(input.mouseDelta.x != 0.0f || input.mouseDelta.y != 0.0f)) {
LogSceneViewNavigation(
logger,
"SceneView look delta=(%.2f, %.2f) hovered=%d downR=%d",
input.mouseDelta.x,
input.mouseDelta.y,
content.hovered ? 1 : 0,
ImGui::IsMouseDown(ImGuiMouseButton_Right) ? 1 : 0);
m_loggedLookDelta = true;
}
}
if (m_panDragging) {
input.mouseDelta = io.MouseDelta;
if (!m_loggedPanDelta &&
(input.mouseDelta.x != 0.0f || input.mouseDelta.y != 0.0f)) {
LogSceneViewNavigation(
logger,
"SceneView pan delta=(%.2f, %.2f) hovered=%d downM=%d",
input.mouseDelta.x,
input.mouseDelta.y,
content.hovered ? 1 : 0,
ImGui::IsMouseDown(ImGuiMouseButton_Middle) ? 1 : 0);
m_loggedPanDelta = true;
2026-03-31 23:45:08 +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();
SceneViewportActiveGizmoKind drawActiveGizmoKind = SceneViewportActiveGizmoKind::None;
if (m_moveGizmo.IsActive()) {
drawActiveGizmoKind = SceneViewportActiveGizmoKind::Move;
} else if (m_rotateGizmo.IsActive()) {
drawActiveGizmoKind = SceneViewportActiveGizmoKind::Rotate;
} else if (m_scaleGizmo.IsActive()) {
drawActiveGizmoKind = SceneViewportActiveGizmoKind::Scale;
}
if (showingMoveGizmo) {
2026-03-31 23:45:08 +08:00
moveGizmoContext = BuildMoveGizmoContext(*m_context, overlay, content, io.MousePos);
SceneViewportMoveGizmoContext updateContext = moveGizmoContext;
if (drawActiveGizmoKind != SceneViewportActiveGizmoKind::None &&
drawActiveGizmoKind != SceneViewportActiveGizmoKind::Move) {
updateContext.mousePosition = Math::Vector2(-1.0f, -1.0f);
}
m_moveGizmo.Update(updateContext);
}
if (showingRotateGizmo) {
2026-03-31 23:45:08 +08:00
rotateGizmoContext = BuildRotateGizmoContext(*m_context, overlay, content, io.MousePos);
SceneViewportRotateGizmoContext updateContext = rotateGizmoContext;
if (drawActiveGizmoKind != SceneViewportActiveGizmoKind::None &&
drawActiveGizmoKind != SceneViewportActiveGizmoKind::Rotate) {
updateContext.mousePosition = Math::Vector2(-1.0f, -1.0f);
}
m_rotateGizmo.Update(updateContext);
}
if (showingScaleGizmo) {
scaleGizmoContext = BuildScaleGizmoContext(*m_context, overlay, content, io.MousePos);
scaleGizmoContext.uniformOnly = usingTransformTool;
SceneViewportScaleGizmoContext updateContext = scaleGizmoContext;
if (drawActiveGizmoKind != SceneViewportActiveGizmoKind::None &&
drawActiveGizmoKind != SceneViewportActiveGizmoKind::Scale) {
updateContext.mousePosition = Math::Vector2(-1.0f, -1.0f);
}
m_scaleGizmo.Update(updateContext);
2026-03-31 23:45:08 +08:00
}
DrawSceneViewportOverlay(
2026-03-28 17:50:54 +08:00
ImGui::GetWindowDrawList(),
overlay,
content.itemMin,
content.itemMax,
2026-03-29 16:18:13 +08:00
content.availableSize,
showingMoveGizmo ? &m_moveGizmo.GetDrawData() : nullptr,
showingRotateGizmo ? &m_rotateGizmo.GetDrawData() : nullptr,
showingScaleGizmo ? &m_scaleGizmo.GetDrawData() : nullptr);
2026-03-28 17:50:54 +08:00
}
}
2026-03-26 22:31:22 +08:00
Actions::ObserveInactiveActionRoute(*m_context);
}
}
}