#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 "Platform/Win32Utf8.h" #include "UI/UI.h" #include #include #include #include #include namespace XCEngine { namespace Editor { 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(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(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); } 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; } 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; } } // 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(); auto& logger = Debug::Logger::Get(); 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; } 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; } 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; } } 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; SceneViewportOverlayData overlay = {}; SceneViewportMoveGizmoContext moveGizmoContext = {}; SceneViewportRotateGizmoContext rotateGizmoContext = {}; SceneViewportScaleGizmoContext scaleGizmoContext = {}; SceneViewportActiveGizmoKind activeGizmoKind = SceneViewportActiveGizmoKind::None; if (hasInteractiveViewport) { overlay = viewportHostService->GetSceneViewOverlayData(); if (showingMoveGizmo) { 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()); } if (showingRotateGizmo) { 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); } } else { 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()); } } 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; const bool beginTransformGizmo = hasInteractiveViewport && content.clickedLeft && !m_lookDragging && !m_panDragging && !toolOverlay.hovered && gizmoHovering; const SceneViewportOrientationAxis orientationAxisHit = hasInteractiveViewport && viewportContentHovered && !usingViewMoveTool && !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 && viewportContentHovered && !usingViewMoveTool && !m_lookDragging && !m_panDragging && !orientationGizmoClick && !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(); } if (beginTransformGizmo) { if (hoveredGizmoKind == SceneViewportActiveGizmoKind::Scale) { m_scaleGizmo.TryBeginDrag(scaleGizmoContext, m_context->GetUndoManager()); } else if (hoveredGizmoKind == SceneViewportActiveGizmoKind::Move) { m_moveGizmo.TryBeginDrag(moveGizmoContext, m_context->GetUndoManager()); } else if (hoveredGizmoKind == SceneViewportActiveGizmoKind::Rotate) { m_rotateGizmo.TryBeginDrag(rotateGizmoContext, m_context->GetUndoManager()); } } if (orientationGizmoClick) { viewportHostService->AlignSceneViewToOrientationAxis(orientationAxisHit); overlay = viewportHostService->GetSceneViewOverlayData(); if (showingMoveGizmo) { moveGizmoContext = BuildMoveGizmoContext(*m_context, overlay, content, io.MousePos); m_moveGizmo.Update(moveGizmoContext); } if (showingRotateGizmo) { 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(); } } if (gizmoActive) { if (ImGui::IsMouseDown(ImGuiMouseButton_Left)) { if (activeGizmoKind == SceneViewportActiveGizmoKind::Move) { m_moveGizmo.UpdateDrag(moveGizmoContext); } else if (activeGizmoKind == SceneViewportActiveGizmoKind::Rotate) { m_rotateGizmo.UpdateDrag(rotateGizmoContext); } else if (activeGizmoKind == SceneViewportActiveGizmoKind::Scale) { m_scaleGizmo.UpdateDrag(scaleGizmoContext); } } else { if (activeGizmoKind == SceneViewportActiveGizmoKind::Move) { m_moveGizmo.EndDrag(m_context->GetUndoManager()); } else if (activeGizmoKind == SceneViewportActiveGizmoKind::Rotate) { m_rotateGizmo.EndDrag(m_context->GetUndoManager()); } else if (activeGizmoKind == SceneViewportActiveGizmoKind::Scale) { m_scaleGizmo.EndDrag(m_context->GetUndoManager()); } } } 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; 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; 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) { 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; } } } viewportHostService->UpdateSceneViewInput(*m_context, input); if (content.hasViewportArea && content.frame.hasTexture) { 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) { 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) { 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); } DrawSceneViewportOverlay( ImGui::GetWindowDrawList(), overlay, content.itemMin, content.itemMax, content.availableSize, showingMoveGizmo ? &m_moveGizmo.GetDrawData() : nullptr, showingRotateGizmo ? &m_rotateGizmo.GetDrawData() : nullptr, showingScaleGizmo ? &m_scaleGizmo.GetDrawData() : nullptr); } } Actions::ObserveInactiveActionRoute(*m_context); } } }