#include "Actions/ActionRouting.h" #include "Core/IEditorContext.h" #include "Core/ISceneManager.h" #include "Core/ISelectionManager.h" #include "SceneViewPanel.h" #include "Viewport/SceneViewportOrientationGizmo.h" #include "Viewport/SceneViewportOverlayRenderer.h" #include "ViewportPanelContent.h" #include "UI/UI.h" #include namespace XCEngine { namespace Editor { namespace { 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; } 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; } } // namespace SceneViewPanel::SceneViewPanel() : Panel("Scene") {} void SceneViewPanel::Render() { ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); UI::PanelWindowScope panel(m_name.c_str()); ImGui::PopStyleVar(); if (!panel.IsOpen()) { return; } const ViewportPanelContentResult content = RenderViewportPanelContent(*m_context, EditorViewportKind::Scene); if (IViewportHostService* viewportHostService = m_context->GetViewportHostService()) { const ImGuiIO& io = ImGui::GetIO(); const bool hasInteractiveViewport = content.hasViewportArea && content.frame.hasTexture; 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()); } m_transformTool = SceneViewportTransformTool::Move; } else if (ImGui::IsKeyPressed(ImGuiKey_E, false)) { if (m_moveGizmo.IsActive()) { m_moveGizmo.CancelDrag(&m_context->GetUndoManager()); } m_transformTool = SceneViewportTransformTool::Rotate; } } const bool usingMoveGizmo = m_transformTool == SceneViewportTransformTool::Move; SceneViewportOverlayData overlay = {}; SceneViewportMoveGizmoContext moveGizmoContext = {}; SceneViewportRotateGizmoContext rotateGizmoContext = {}; if (hasInteractiveViewport) { overlay = viewportHostService->GetSceneViewOverlayData(); if (usingMoveGizmo) { if (m_rotateGizmo.IsActive()) { m_rotateGizmo.CancelDrag(&m_context->GetUndoManager()); } 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()); } m_moveGizmo.Update(moveGizmoContext); } else { if (m_moveGizmo.IsActive()) { m_moveGizmo.CancelDrag(&m_context->GetUndoManager()); } 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()); } m_rotateGizmo.Update(rotateGizmoContext); } } else { if (m_moveGizmo.IsActive()) { m_moveGizmo.CancelDrag(&m_context->GetUndoManager()); } if (m_rotateGizmo.IsActive()) { m_rotateGizmo.CancelDrag(&m_context->GetUndoManager()); } } const bool gizmoHovering = usingMoveGizmo ? m_moveGizmo.IsHoveringHandle() : m_rotateGizmo.IsHoveringHandle(); const bool gizmoActive = usingMoveGizmo ? m_moveGizmo.IsActive() : m_rotateGizmo.IsActive(); const bool beginTransformGizmo = hasInteractiveViewport && content.clickedLeft && !m_lookDragging && !m_panDragging && gizmoHovering; const SceneViewportOrientationAxis orientationAxisHit = hasInteractiveViewport && content.hovered && !m_lookDragging && !m_panDragging && !gizmoHovering && !gizmoActive ? HitTestSceneViewportOrientationGizmo( overlay, content.itemMin, content.itemMax, io.MousePos) : SceneViewportOrientationAxis::None; const bool orientationGizmoClick = hasInteractiveViewport && content.clickedLeft && orientationAxisHit != SceneViewportOrientationAxis::None; const bool selectClick = hasInteractiveViewport && content.clickedLeft && !m_lookDragging && !m_panDragging && !orientationGizmoClick && !gizmoHovering && !gizmoActive; const bool beginLookDrag = hasInteractiveViewport && content.hovered && !m_lookDragging && !gizmoActive && ImGui::IsMouseClicked(ImGuiMouseButton_Right); const bool beginPanDrag = hasInteractiveViewport && content.hovered && !m_panDragging && !gizmoActive && !m_lookDragging && ImGui::IsMouseClicked(ImGuiMouseButton_Middle); if (beginTransformGizmo || orientationGizmoClick || selectClick || beginLookDrag || beginPanDrag) { ImGui::SetWindowFocus(); } if (beginTransformGizmo) { if (usingMoveGizmo) { m_moveGizmo.TryBeginDrag(moveGizmoContext, m_context->GetUndoManager()); } else { m_rotateGizmo.TryBeginDrag(rotateGizmoContext, m_context->GetUndoManager()); } } if (orientationGizmoClick) { viewportHostService->AlignSceneViewToOrientationAxis(orientationAxisHit); overlay = viewportHostService->GetSceneViewOverlayData(); if (usingMoveGizmo) { moveGizmoContext = BuildMoveGizmoContext(*m_context, overlay, content, io.MousePos); m_moveGizmo.Update(moveGizmoContext); } else { rotateGizmoContext = BuildRotateGizmoContext(*m_context, overlay, content, io.MousePos); m_rotateGizmo.Update(rotateGizmoContext); } } 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(); } } if (gizmoActive) { if (ImGui::IsMouseDown(ImGuiMouseButton_Left)) { if (usingMoveGizmo) { m_moveGizmo.UpdateDrag(moveGizmoContext); } else { m_rotateGizmo.UpdateDrag(rotateGizmoContext); } } else { if (usingMoveGizmo) { m_moveGizmo.EndDrag(m_context->GetUndoManager()); } else { m_rotateGizmo.EndDrag(m_context->GetUndoManager()); } } } if (beginLookDrag) { m_lookDragging = true; m_panDragging = false; m_lastLookDragDelta = ImVec2(0.0f, 0.0f); m_lastPanDragDelta = ImVec2(0.0f, 0.0f); } if (beginPanDrag) { m_panDragging = true; m_lookDragging = false; m_lastPanDragDelta = ImVec2(0.0f, 0.0f); m_lastLookDragDelta = ImVec2(0.0f, 0.0f); } if (m_lookDragging && !ImGui::IsMouseDown(ImGuiMouseButton_Right)) { m_lookDragging = false; m_lastLookDragDelta = ImVec2(0.0f, 0.0f); } if (m_panDragging && !ImGui::IsMouseDown(ImGuiMouseButton_Middle)) { m_panDragging = false; m_lastPanDragDelta = ImVec2(0.0f, 0.0f); } if (m_lookDragging || m_panDragging || m_moveGizmo.IsActive() || m_rotateGizmo.IsActive()) { ImGui::SetNextFrameWantCaptureMouse(true); } if (m_lookDragging) { ImGui::SetNextFrameWantCaptureKeyboard(true); } SceneViewportInput input = {}; input.viewportSize = content.availableSize; input.deltaTime = io.DeltaTime; input.hovered = content.hovered; input.focused = content.focused || m_lookDragging || m_panDragging; input.mouseWheel = (content.hovered && !m_lookDragging) ? io.MouseWheel : 0.0f; input.flySpeedDelta = (content.hovered && m_lookDragging) ? io.MouseWheel : 0.0f; input.looking = m_lookDragging; input.orbiting = false; input.panning = m_panDragging; input.fastMove = io.KeyShift; input.focusSelectionRequested = input.focused && !io.WantTextInput && ImGui::IsKeyPressed(ImGuiKey_F, false); if (m_lookDragging && !io.WantTextInput) { 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) { const ImVec2 lookDragDelta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Right, 0.0f); input.mouseDelta.x += lookDragDelta.x - m_lastLookDragDelta.x; input.mouseDelta.y += lookDragDelta.y - m_lastLookDragDelta.y; m_lastLookDragDelta = lookDragDelta; } else { m_lastLookDragDelta = ImVec2(0.0f, 0.0f); } if (m_panDragging) { const ImVec2 panDragDelta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Middle, 0.0f); ImVec2 framePanDelta( panDragDelta.x - m_lastPanDragDelta.x, panDragDelta.y - m_lastPanDragDelta.y); // Some middle-button drags report a zero drag delta on the interaction surface. if ((framePanDelta.x == 0.0f && framePanDelta.y == 0.0f) && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)) { framePanDelta = io.MouseDelta; } input.mouseDelta.x += framePanDelta.x; input.mouseDelta.y += framePanDelta.y; m_lastPanDragDelta = panDragDelta; } else { m_lastPanDragDelta = ImVec2(0.0f, 0.0f); } } viewportHostService->UpdateSceneViewInput(*m_context, input); if (content.hasViewportArea && content.frame.hasTexture) { overlay = viewportHostService->GetSceneViewOverlayData(); if (usingMoveGizmo) { moveGizmoContext = BuildMoveGizmoContext(*m_context, overlay, content, io.MousePos); m_moveGizmo.Update(moveGizmoContext); } else { rotateGizmoContext = BuildRotateGizmoContext(*m_context, overlay, content, io.MousePos); m_rotateGizmo.Update(rotateGizmoContext); } DrawSceneViewportOverlay( ImGui::GetWindowDrawList(), overlay, content.itemMin, content.itemMax, content.availableSize, usingMoveGizmo ? &m_moveGizmo.GetDrawData() : nullptr, usingMoveGizmo ? nullptr : &m_rotateGizmo.GetDrawData()); } } Actions::ObserveInactiveActionRoute(*m_context); } } }