#include "SceneViewportOverlayRenderer.h" #include "SceneViewportMath.h" namespace XCEngine { namespace Editor { namespace { Math::Matrix4x4 BuildOverlayViewMatrix(const SceneViewportOverlayData& overlay) { return BuildSceneViewportViewMatrix(overlay); } ImU32 ToImGuiColor(const Math::Color& color) { const auto toChannel = [](float value) -> int { return static_cast(std::clamp(value, 0.0f, 1.0f) * 255.0f + 0.5f); }; return IM_COL32( toChannel(color.r), toChannel(color.g), toChannel(color.b), toChannel(color.a)); } void DrawAxisLabel(ImDrawList* drawList, const ImVec2& position, const char* label, ImU32 color) { if (drawList == nullptr || label == nullptr) { return; } const ImVec2 labelSize = ImGui::CalcTextSize(label); drawList->AddText( ImVec2(position.x - labelSize.x * 0.5f, position.y - labelSize.y * 0.5f), color, label); } void DrawSceneAxisWidget( ImDrawList* drawList, const SceneViewportOverlayData& overlay, const ImVec2& viewportMin, const ImVec2& viewportMax) { if (drawList == nullptr || !overlay.valid) { return; } const Math::Matrix4x4 view = BuildOverlayViewMatrix(overlay); const ImVec2 center(viewportMax.x - 52.0f, viewportMin.y + 52.0f); const float radius = 25.0f; drawList->AddCircleFilled(center, radius + 12.0f, IM_COL32(17, 19, 22, 178), 24); drawList->AddCircle(center, radius + 12.0f, IM_COL32(255, 255, 255, 30), 24, 1.0f); struct AxisLine { Math::Vector3 axis; const char* label; ImU32 color; }; const AxisLine axes[] = { { Math::Vector3::Right(), "x", IM_COL32(239, 83, 80, 255) }, { Math::Vector3::Up(), "y", IM_COL32(102, 187, 106, 255) }, { Math::Vector3::Forward(), "z", IM_COL32(66, 165, 245, 255) } }; for (const AxisLine& axis : axes) { const Math::Vector3 viewAxis = view.MultiplyVector(axis.axis); const ImVec2 end( center.x + viewAxis.x * radius, center.y - viewAxis.y * radius); drawList->AddLine(center, end, axis.color, 2.0f); drawList->AddCircleFilled(end, 6.0f, axis.color, 16); DrawAxisLabel(drawList, end, axis.label, IM_COL32(245, 245, 245, 255)); } } void DrawSceneMoveGizmo( ImDrawList* drawList, const SceneViewportMoveGizmoDrawData& moveGizmo) { if (drawList == nullptr || !moveGizmo.visible) { return; } const ImVec2 pivot(moveGizmo.pivot.x, moveGizmo.pivot.y); drawList->AddCircleFilled(pivot, moveGizmo.pivotRadius + 1.0f, IM_COL32(20, 22, 24, 220), 20); drawList->AddCircle(pivot, moveGizmo.pivotRadius + 1.0f, IM_COL32(255, 255, 255, 48), 20, 1.0f); for (const SceneViewportMoveGizmoHandleDrawData& handle : moveGizmo.handles) { if (!handle.visible) { continue; } const ImU32 color = ToImGuiColor(handle.color); const float thickness = handle.active ? 4.0f : (handle.hovered ? 3.0f : 2.0f); const ImVec2 start(handle.start.x, handle.start.y); const ImVec2 end(handle.end.x, handle.end.y); drawList->AddLine(start, end, color, thickness); drawList->AddCircleFilled(end, handle.active ? 6.5f : 5.5f, color, 20); } } } // namespace void DrawSceneViewportOverlay( ImDrawList* drawList, const SceneViewportOverlayData& overlay, const ImVec2& viewportMin, const ImVec2& viewportMax, const ImVec2& viewportSize, const SceneViewportMoveGizmoDrawData* moveGizmo) { if (drawList == nullptr || viewportSize.x <= 1.0f || viewportSize.y <= 1.0f) { return; } drawList->PushClipRect(viewportMin, viewportMax, true); if (overlay.valid) { DrawSceneAxisWidget(drawList, overlay, viewportMin, viewportMax); } if (moveGizmo != nullptr) { DrawSceneMoveGizmo(drawList, *moveGizmo); } drawList->PopClipRect(); } } // namespace Editor } // namespace XCEngine