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

348 lines
14 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"
2026-03-26 21:18:33 +08:00
#include "UI/UI.h"
2026-03-28 17:50:54 +08:00
#include <imgui.h>
namespace XCEngine {
namespace Editor {
2026-03-29 16:18:13 +08:00
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;
}
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;
}
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();
2026-03-29 16:18:13 +08:00
const bool hasInteractiveViewport = content.hasViewportArea && content.frame.hasTexture;
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());
}
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;
2026-03-29 16:18:13 +08:00
SceneViewportOverlayData overlay = {};
SceneViewportMoveGizmoContext moveGizmoContext = {};
2026-03-31 23:45:08 +08:00
SceneViewportRotateGizmoContext rotateGizmoContext = {};
2026-03-29 16:18:13 +08:00
if (hasInteractiveViewport) {
overlay = viewportHostService->GetSceneViewOverlayData();
2026-03-31 23:45:08 +08:00
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()) {
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());
}
2026-03-29 16:18:13 +08:00
}
2026-03-31 23:45:08 +08:00
const bool gizmoHovering = usingMoveGizmo ? m_moveGizmo.IsHoveringHandle() : m_rotateGizmo.IsHoveringHandle();
const bool gizmoActive = usingMoveGizmo ? m_moveGizmo.IsActive() : m_rotateGizmo.IsActive();
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 &&
2026-03-31 23:45:08 +08:00
gizmoHovering;
const SceneViewportOrientationAxis orientationAxisHit =
hasInteractiveViewport &&
content.hovered &&
!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 &&
!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 beginLookDrag =
hasInteractiveViewport &&
2026-03-29 16:18:13 +08:00
content.hovered &&
!m_lookDragging &&
2026-03-31 23:45:08 +08:00
!gizmoActive &&
2026-03-29 16:18:13 +08:00
ImGui::IsMouseClicked(ImGuiMouseButton_Right);
const bool beginPanDrag =
hasInteractiveViewport &&
2026-03-29 16:18:13 +08:00
content.hovered &&
!m_panDragging &&
2026-03-31 23:45:08 +08:00
!gizmoActive &&
!m_lookDragging &&
2026-03-29 16:18:13 +08:00
ImGui::IsMouseClicked(ImGuiMouseButton_Middle);
2026-03-31 23:45:08 +08:00
if (beginTransformGizmo || orientationGizmoClick || selectClick || beginLookDrag || beginPanDrag) {
ImGui::SetWindowFocus();
}
2026-03-31 23:45:08 +08:00
if (beginTransformGizmo) {
if (usingMoveGizmo) {
m_moveGizmo.TryBeginDrag(moveGizmoContext, m_context->GetUndoManager());
} else {
m_rotateGizmo.TryBeginDrag(rotateGizmoContext, m_context->GetUndoManager());
}
2026-03-29 16:18:13 +08:00
}
if (orientationGizmoClick) {
viewportHostService->AlignSceneViewToOrientationAxis(orientationAxisHit);
overlay = viewportHostService->GetSceneViewOverlayData();
2026-03-31 23:45:08 +08:00
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();
}
}
2026-03-31 23:45:08 +08:00
if (gizmoActive) {
2026-03-29 16:18:13 +08:00
if (ImGui::IsMouseDown(ImGuiMouseButton_Left)) {
2026-03-31 23:45:08 +08:00
if (usingMoveGizmo) {
m_moveGizmo.UpdateDrag(moveGizmoContext);
} else {
m_rotateGizmo.UpdateDrag(rotateGizmoContext);
}
2026-03-29 16:18:13 +08:00
} else {
2026-03-31 23:45:08 +08:00
if (usingMoveGizmo) {
m_moveGizmo.EndDrag(m_context->GetUndoManager());
} else {
m_rotateGizmo.EndDrag(m_context->GetUndoManager());
}
2026-03-29 16:18:13 +08:00
}
}
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);
}
2026-03-31 23:45:08 +08:00
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;
2026-03-28 18:28:11 +08:00
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;
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) {
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);
2026-03-31 23:45:08 +08:00
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);
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-03-31 23:45:08 +08:00
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(
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,
2026-03-31 23:45:08 +08:00
usingMoveGizmo ? &m_moveGizmo.GetDrawData() : nullptr,
usingMoveGizmo ? nullptr : &m_rotateGizmo.GetDrawData());
2026-03-28 17:50:54 +08:00
}
}
2026-03-26 22:31:22 +08:00
Actions::ObserveInactiveActionRoute(*m_context);
}
}
}