#pragma once #include #include #include #include #include #include #include #include #include #include #include #include namespace XCEngine::Components { class GameObject; } // namespace XCEngine::Components namespace XCEngine::UI::Editor::App::SceneViewportGizmoSupport { class IUndoManager { public: virtual ~IUndoManager() = default; virtual void BeginInteractiveChange(const std::string& label) = 0; virtual bool HasPendingInteractiveChange() const = 0; virtual void FinalizeInteractiveChange() = 0; virtual void CancelInteractiveChange() = 0; }; struct SceneViewportOverlayData { bool valid = false; Math::Vector3 cameraPosition = Math::Vector3::Zero(); Math::Vector3 cameraForward = Math::Vector3::Forward(); Math::Vector3 cameraRight = Math::Vector3::Right(); Math::Vector3 cameraUp = Math::Vector3::Up(); float verticalFovDegrees = 60.0f; float nearClipPlane = 0.03f; float farClipPlane = 2000.0f; float orbitDistance = 6.0f; }; struct SceneViewportProjectedPoint { Math::Vector2 screenPosition = Math::Vector2::Zero(); float ndcDepth = 0.0f; bool visible = false; }; struct SceneViewportOverlayScreenTriangleVertex { Math::Vector2 screenPosition = Math::Vector2::Zero(); Math::Color color = Math::Color::White(); }; struct SceneViewportOverlayScreenTrianglePrimitive { std::array vertices = {}; }; struct SceneViewportOverlayFrameData { SceneViewportOverlayData overlay = {}; std::vector screenTriangles = {}; }; Math::Matrix4x4 BuildSceneViewportViewMatrix(const SceneViewportOverlayData& overlay); Math::Matrix4x4 BuildSceneViewportProjectionMatrix( const SceneViewportOverlayData& overlay, float viewportWidth, float viewportHeight); Math::Matrix4x4 BuildSceneViewportViewProjectionMatrix( const SceneViewportOverlayData& overlay, float viewportWidth, float viewportHeight); SceneViewportProjectedPoint ProjectSceneViewportWorldPoint( const SceneViewportOverlayData& overlay, float viewportWidth, float viewportHeight, const Math::Vector3& worldPoint); bool ProjectSceneViewportAxisDirection( const SceneViewportOverlayData& overlay, const Math::Vector3& worldAxis, Math::Vector2& outScreenDirection); bool ProjectSceneViewportAxisDirectionAtPoint( const SceneViewportOverlayData& overlay, float viewportWidth, float viewportHeight, const Math::Vector3& worldPoint, const Math::Vector3& worldAxis, Math::Vector2& outScreenDirection, float sampleDistance = 1.0f); float DistanceToSegmentSquared( const Math::Vector2& point, const Math::Vector2& segmentStart, const Math::Vector2& segmentEnd, float* outSegmentT = nullptr); Math::Plane BuildSceneViewportPlaneFromPointNormal( const Math::Vector3& point, const Math::Vector3& normal); bool BuildSceneViewportAxisDragPlaneNormal( const SceneViewportOverlayData& overlay, const Math::Vector3& worldAxis, Math::Vector3& outPlaneNormal); bool BuildSceneViewportRay( const SceneViewportOverlayData& overlay, const Math::Vector2& viewportSize, const Math::Vector2& viewportPosition, Math::Ray& outRay); enum class SceneViewportGizmoAxis : std::uint8_t { None = 0, X, Y, Z }; enum class SceneViewportGizmoPlane : std::uint8_t { None = 0, XY, XZ, YZ }; struct SceneViewportMoveGizmoHandleDrawData { SceneViewportGizmoAxis axis = SceneViewportGizmoAxis::None; Math::Vector2 start = Math::Vector2::Zero(); Math::Vector2 end = Math::Vector2::Zero(); Math::Color color = Math::Color::White(); bool visible = false; bool hovered = false; bool active = false; }; struct SceneViewportMoveGizmoPlaneDrawData { SceneViewportGizmoPlane plane = SceneViewportGizmoPlane::None; std::array corners = {}; Math::Color fillColor = Math::Color::White(); Math::Color outlineColor = Math::Color::White(); bool visible = false; bool hovered = false; bool active = false; }; struct SceneViewportMoveGizmoDrawData { bool visible = false; Math::Vector2 pivot = Math::Vector2::Zero(); float pivotRadius = 5.0f; std::array handles = {}; std::array planes = {}; }; struct SceneViewportMoveGizmoContext { SceneViewportOverlayData overlay = {}; Math::Vector2 viewportSize = Math::Vector2::Zero(); Math::Vector2 mousePosition = Math::Vector2::Zero(); Components::GameObject* selectedObject = nullptr; std::vector selectedObjects = {}; Math::Vector3 pivotWorldPosition = Math::Vector3::Zero(); Math::Quaternion axisOrientation = Math::Quaternion::Identity(); }; struct SceneViewportMoveGizmoHitResult { SceneViewportGizmoAxis axis = SceneViewportGizmoAxis::None; SceneViewportGizmoPlane plane = SceneViewportGizmoPlane::None; float distanceSq = Math::FLOAT_MAX; bool HasHit() const { return axis != SceneViewportGizmoAxis::None || plane != SceneViewportGizmoPlane::None; } }; class SceneViewportMoveGizmo { public: void Update(const SceneViewportMoveGizmoContext& context); bool TryBeginDrag(const SceneViewportMoveGizmoContext& context, IUndoManager& undoManager); void UpdateDrag(const SceneViewportMoveGizmoContext& context); void EndDrag(IUndoManager& undoManager); void CancelDrag(IUndoManager* undoManager = nullptr); bool IsHoveringHandle() const; bool IsActive() const; std::uint64_t GetActiveEntityId() const; const SceneViewportMoveGizmoDrawData& GetDrawData() const; SceneViewportMoveGizmoHitResult EvaluateHit(const Math::Vector2& mousePosition) const; void SetHoveredHandle(SceneViewportGizmoAxis axis, SceneViewportGizmoPlane plane); private: enum class DragMode : std::uint8_t { None = 0, Axis, Plane }; void BuildDrawData(const SceneViewportMoveGizmoContext& context); void RefreshHandleState(); SceneViewportMoveGizmoDrawData m_drawData = {}; SceneViewportGizmoAxis m_hoveredAxis = SceneViewportGizmoAxis::None; SceneViewportGizmoPlane m_hoveredPlane = SceneViewportGizmoPlane::None; SceneViewportGizmoAxis m_activeAxis = SceneViewportGizmoAxis::None; SceneViewportGizmoPlane m_activePlane = SceneViewportGizmoPlane::None; DragMode m_dragMode = DragMode::None; std::uint64_t m_activeEntityId = 0; Math::Vector3 m_activeAxisDirection = Math::Vector3::Zero(); Math::Vector3 m_activePlaneNormal = Math::Vector3::Zero(); Math::Plane m_dragPlane = {}; Math::Vector3 m_dragStartPivotWorldPosition = Math::Vector3::Zero(); Math::Vector3 m_dragStartHitWorldPosition = Math::Vector3::Zero(); float m_dragStartAxisScalar = 0.0f; std::vector m_dragObjects = {}; std::vector m_dragStartObjectWorldPositions = {}; }; enum class SceneViewportRotateGizmoAxis : std::uint8_t { None = 0, X, Y, Z, View }; constexpr std::size_t kSceneViewportRotateGizmoSegmentCount = 96u; constexpr std::size_t kSceneViewportRotateGizmoAngleFillPointCount = kSceneViewportRotateGizmoSegmentCount + 1u; struct SceneViewportRotateGizmoSegmentDrawData { Math::Vector2 start = Math::Vector2::Zero(); Math::Vector2 end = Math::Vector2::Zero(); float startAngle = 0.0f; float endAngle = 0.0f; bool visible = false; bool frontFacing = true; }; struct SceneViewportRotateGizmoHandleDrawData { SceneViewportRotateGizmoAxis axis = SceneViewportRotateGizmoAxis::None; std::array segments = {}; Math::Color color = Math::Color::White(); bool visible = false; bool hovered = false; bool active = false; }; struct SceneViewportRotateGizmoAngleFillDrawData { SceneViewportRotateGizmoAxis axis = SceneViewportRotateGizmoAxis::None; Math::Vector2 pivot = Math::Vector2::Zero(); std::array arcPoints = {}; std::size_t arcPointCount = 0u; Math::Color fillColor = Math::Color::White(); Math::Color outlineColor = Math::Color::White(); bool visible = false; }; struct SceneViewportRotateGizmoDrawData { bool visible = false; Math::Vector2 pivot = Math::Vector2::Zero(); std::array handles = {}; SceneViewportRotateGizmoAngleFillDrawData angleFill = {}; }; struct SceneViewportRotateGizmoContext { SceneViewportOverlayData overlay = {}; Math::Vector2 viewportSize = Math::Vector2::Zero(); Math::Vector2 mousePosition = Math::Vector2::Zero(); Components::GameObject* selectedObject = nullptr; std::vector selectedObjects = {}; Math::Vector3 pivotWorldPosition = Math::Vector3::Zero(); Math::Quaternion axisOrientation = Math::Quaternion::Identity(); bool localSpace = false; bool rotateAroundSharedPivot = false; }; struct SceneViewportRotateGizmoHitResult { SceneViewportRotateGizmoAxis axis = SceneViewportRotateGizmoAxis::None; float distanceSq = Math::FLOAT_MAX; bool HasHit() const { return axis != SceneViewportRotateGizmoAxis::None; } }; class SceneViewportRotateGizmo { public: void Update(const SceneViewportRotateGizmoContext& context); bool TryBeginDrag(const SceneViewportRotateGizmoContext& context, IUndoManager& undoManager); void UpdateDrag(const SceneViewportRotateGizmoContext& context); void EndDrag(IUndoManager& undoManager); void CancelDrag(IUndoManager* undoManager = nullptr); bool IsHoveringHandle() const; bool IsActive() const; std::uint64_t GetActiveEntityId() const; const SceneViewportRotateGizmoDrawData& GetDrawData() const; SceneViewportRotateGizmoHitResult EvaluateHit(const Math::Vector2& mousePosition) const; void SetHoveredHandle(SceneViewportRotateGizmoAxis axis); private: void BuildDrawData(const SceneViewportRotateGizmoContext& context); void RefreshHandleState(); bool TryGetClosestRingAngle( SceneViewportRotateGizmoAxis axis, const Math::Vector2& mousePosition, bool allowBackFacing, float& outAngle) const; SceneViewportRotateGizmoDrawData m_drawData = {}; SceneViewportRotateGizmoAxis m_hoveredAxis = SceneViewportRotateGizmoAxis::None; SceneViewportRotateGizmoAxis m_activeAxis = SceneViewportRotateGizmoAxis::None; std::uint64_t m_activeEntityId = 0; bool m_screenSpaceDrag = false; bool m_localSpace = false; bool m_rotateAroundSharedPivot = false; Math::Vector3 m_activeWorldAxis = Math::Vector3::Zero(); Math::Plane m_dragPlane = {}; float m_dragStartRingAngle = 0.0f; float m_dragCurrentDeltaRadians = 0.0f; Math::Vector3 m_dragStartPivotWorldPosition = Math::Vector3::Zero(); std::vector m_dragObjects = {}; std::vector m_dragStartWorldPositions = {}; std::vector m_dragStartWorldRotations = {}; }; enum class SceneViewportScaleGizmoHandle : std::uint8_t { None = 0, X, Y, Z, Uniform }; struct SceneViewportScaleGizmoAxisHandleDrawData { SceneViewportScaleGizmoHandle handle = SceneViewportScaleGizmoHandle::None; Math::Vector2 start = Math::Vector2::Zero(); Math::Vector2 end = Math::Vector2::Zero(); Math::Vector2 capCenter = Math::Vector2::Zero(); float capHalfSize = 0.0f; Math::Color color = Math::Color::White(); bool visible = false; bool hovered = false; bool active = false; }; struct SceneViewportScaleGizmoCenterHandleDrawData { Math::Vector2 center = Math::Vector2::Zero(); float halfSize = 0.0f; Math::Color fillColor = Math::Color::White(); Math::Color outlineColor = Math::Color::White(); bool visible = false; bool hovered = false; bool active = false; }; struct SceneViewportScaleGizmoDrawData { bool visible = false; std::array axisHandles = {}; SceneViewportScaleGizmoCenterHandleDrawData centerHandle = {}; }; struct SceneViewportScaleGizmoContext { SceneViewportOverlayData overlay = {}; Math::Vector2 viewportSize = Math::Vector2::Zero(); Math::Vector2 mousePosition = Math::Vector2::Zero(); Components::GameObject* selectedObject = nullptr; Math::Vector3 pivotWorldPosition = Math::Vector3::Zero(); Math::Quaternion axisOrientation = Math::Quaternion::Identity(); bool uniformOnly = false; }; struct SceneViewportScaleGizmoHitResult { SceneViewportScaleGizmoHandle handle = SceneViewportScaleGizmoHandle::None; float distanceSq = Math::FLOAT_MAX; bool HasHit() const { return handle != SceneViewportScaleGizmoHandle::None; } }; class SceneViewportScaleGizmo { public: void Update(const SceneViewportScaleGizmoContext& context); bool TryBeginDrag(const SceneViewportScaleGizmoContext& context, IUndoManager& undoManager); void UpdateDrag(const SceneViewportScaleGizmoContext& context); void EndDrag(IUndoManager& undoManager); void CancelDrag(IUndoManager* undoManager = nullptr); bool IsHoveringHandle() const; bool IsActive() const; std::uint64_t GetActiveEntityId() const; const SceneViewportScaleGizmoDrawData& GetDrawData() const; SceneViewportScaleGizmoHitResult EvaluateHit(const Math::Vector2& mousePosition) const; void SetHoveredHandle(SceneViewportScaleGizmoHandle handle); private: void BuildDrawData(const SceneViewportScaleGizmoContext& context); void RefreshHandleState(); const SceneViewportScaleGizmoAxisHandleDrawData* FindAxisHandleDrawData( SceneViewportScaleGizmoHandle handle) const; SceneViewportScaleGizmoDrawData m_drawData = {}; SceneViewportScaleGizmoHandle m_hoveredHandle = SceneViewportScaleGizmoHandle::None; SceneViewportScaleGizmoHandle m_activeHandle = SceneViewportScaleGizmoHandle::None; std::uint64_t m_activeEntityId = 0; Math::Vector3 m_dragStartLocalScale = Math::Vector3::Zero(); Math::Vector3 m_dragCurrentVisualScale = Math::Vector3::One(); Math::Vector2 m_dragStartMousePosition = Math::Vector2::Zero(); Math::Vector2 m_activeScreenDirection = Math::Vector2::Zero(); }; struct SceneViewportTransformGizmoHandleBuildInputs { const SceneViewportMoveGizmoDrawData* moveGizmo = nullptr; std::uint64_t moveEntityId = 0; const SceneViewportRotateGizmoDrawData* rotateGizmo = nullptr; std::uint64_t rotateEntityId = 0; const SceneViewportScaleGizmoDrawData* scaleGizmo = nullptr; std::uint64_t scaleEntityId = 0; }; SceneViewportOverlayFrameData BuildSceneViewportTransformGizmoOverlayFrameData( const SceneViewportOverlayData& overlay, const SceneViewportTransformGizmoHandleBuildInputs& inputs); } // namespace XCEngine::UI::Editor::App::SceneViewportGizmoSupport