/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #ifndef CRYINCLUDE_EDITOR_ROTATETOOL_H #define CRYINCLUDE_EDITOR_ROTATETOOL_H #pragma once #include "EditTool.h" #include "IObjectManager.h" #include "EditMode/ObjectMode.h" //! Provides rendering utilities to support CRotateTool namespace RotationDrawHelper { //! Circle drawing and hit testing functionality over arbitrary axes class Axis { public: //! \param defaultColor Color used to draw the camera aligned portion of the axis. //! \param highlightColor Color used to draw the circle when it is in focus. Axis(const ColorF& defaultColor = Col_White, const ColorF& highlightColor = Col_Yellow); //! Draws an axis aligned circle. //! \param dc DisplayContext to use for rendering. //! \param position World space position used as the center of the circle. //! \param axis The axis by which to align the circle. //! \param angleRadians The angle towards which the circle will be highlighted. //! \param radius The radius of the circle. //! \param highlighted If true it will draw the circle in the specified highlightColor. void Draw(DisplayContext& dc, const Vec3& position, const Vec3& axis, float angleRadians, float angleStepRadians, float radius, bool highlighted, CBaseObject* object, float screenScale); //! Calculates a hit testing mesh (invisible) used for intersection testing. //! \param object The object selected if hit testing return true. //! \param hc The HitContext in which the hit object is set if an intersection is true. //! \param radius The radius for the axis' circle. //! \param angleStepRadians The angle for the step used to calculate the circle, a smaller angle results in a higher quality circle. //! \param axis The axis by which to align the intersection geometry. //! \param screenScale This is an internal parameter used to deduce the view distance ratio in order to scale the tool. bool HitTest(CBaseObject* object, HitContext& hc, float radius, float angleStepRadians, const Vec3& axis, float screenScale); //! Draws the generated hit testing geometry, good for diagnostics and debugging. //! \param dc DisplayContext to use for rendering. //! \param hc The HitContext that contains the view direction raycast. //! \param position World space position used as the center of the circle. //! \param radius The radius for the axis' circle. //! \param angleStepRadians The angle for the step used to calculate the circle, a smaller angle results in a higher quality circle. //! \param axis The axis by which to align the intersection geometry. //! \param screenScale This is an internal parameter used to deduce the view distance ratio in order to scale the tool. void DebugDrawHitTestSurface(DisplayContext& dc, HitContext& hc, const Vec3& position, float radius, float angleStepRadians, const Vec3& axis, float screenScale); protected: enum States { StateDefault, StateHighlight, StateCount }; ColorF m_colors[StateCount]; //! Defines the width of the generated hit testing geometry. float m_hitTestWidth = 0.4f; //! Contains the vertices that make up the ring for the intersection testing geometry. //! \remark Only contains the center positions, quads are generated by calculating the four vertices offset by m_hitTestWidth. std::vector m_vertices; //! Generates the world space geometry necessary to perform hit testing. //! \param hc The HitContext data. //! \param position The world space position around which the geometry will be centered. //! \param radius The radius of the ring. //! \param angleStepRadians The angle for the step used to calculate the circle, a smaller angle results in a higher quality circle. //! \param axis The axis to which the geometry will be aligned to. //! \param screenScale This is an internal parameter used to deduce the view distance ratio in order to scale the tool. void GenerateHitTestGeometry(HitContext& hc, const Vec3& position, float radius, float angleStepRadians, const Vec3& axis, float screenScale); //! Performs intersection testing between a ray and both sides of a quad //! \param ray The ray to test (in world space) //! \param quad An array of four Vec3 points in world space. //! \param[out] contact The intersection position in world space at which the intersection occurred. bool IntersectRayWithQuad(const Ray&ray, Vec3 quad[4], Vec3 & contact); }; //! Provides the means to set and restore DisplayContext settings within a given scope. class DisplayContextScope { public: DisplayContextScope(DisplayContext& dc) : m_dc(dc) { m_dc.DepthTestOff(); m_dc.CullOff(); } ~DisplayContextScope() { m_dc.DepthTestOn(); m_dc.CullOn(); } DisplayContext& m_dc; }; //! Helper function that draws the representation of the inner angle of a rotation. namespace AngleDecorator { //! \param dc //! \param position World space position of the center of the decorator. //! \param axisToAlign Axis to which the decorator will be aligned to. //! \param startAngleRadians The starting angle from which the rotation will be performed. //! \param sweepAngleRadians An angle that represents the sweep of the rotation arc. //! \param angleStepRadians The angle for the step used to calculate the circle, a smaller angle results in a higher quality circle. //! \param radius The radius of the decorator. //! \param screenScale This is an internal parameter used to deduce the view distance ratio in order to scale the tool. void Draw(DisplayContext& dc, const Vec3& position, const Vec3& axisToAlign, float startAngleRadians, float sweepAngleRadians, float stepAngleRadians, float radius, float screenScale); } } //! Provides rotation manipulation controls. class SANDBOX_API CRotateTool : public CObjectMode , public IObjectSelectCallback { Q_OBJECT public: Q_INVOKABLE CRotateTool(CBaseObject* pObject = nullptr, QWidget* parent = nullptr); virtual ~CRotateTool(); static const GUID& GetClassID(); // Registration function. static void RegisterTool(CRegistrationContext& rc); void Display(DisplayContext& dc) override; void DrawObjectHelpers(CBaseObject* pObject, DisplayContext& dc) override {} bool HitTest(CBaseObject* pObject, HitContext& hc) override; void DeleteThis() override; bool OnLButtonDown(CViewport* view, int nFlags, const QPoint& point) override; bool OnLButtonUp(CViewport* view, int nFlags, const QPoint& point) override; bool OnMouseMove(CViewport* view, int nFlags, const QPoint& point) override; protected: //! Utility to calculate the view distance ratio used to scale the tool. float GetScreenScale(IDisplayViewport* view, CCamera* camera = nullptr); enum Axis { AxisNone, AxisX, //! X axis visualization and hit testing AxisY, //! Y axis visualization and hit testing AxisZ, //! Z axis visualization and hit testing AxisView, //! View direction axis, used to rotate along the vector from the camera to the object. AxisCount }; //! Axis visualization and hit testing RotationDrawHelper::Axis m_axes[Axis::AxisCount]; //! We record the starting angle when we begin to drag an object float m_initialViewAxisAngleRadians; //! The angle from the object's (or selection's) center to the mouse cursor. float m_angleToCursor; //! Specified which axis is currently selected. Axis m_highlightAxis; //! True when we are using the view direction rotation axis. bool m_viewAxisRotation; //! True when the mouse has been pressed, becomes false on release. bool m_draggingMouse; //! The last mouse position on screen when rotating. QPoint m_lastPosition; //! Cumulative rotation angle in degrees. Ang3 m_rotationAngles; //! The selected object. CBaseObject* m_object; //! True if there has been a change in rotation that affects the object. bool m_bTransformChanged; //! Sum of the total rotation angles. float m_totalRotationAngle; //! Radius used to draw the XYZ axes float m_basisAxisRadius; //! Radius used to draw the view direction axis float m_viewAxisRadius; //! Rotation step controls the quality of the axes, a smaller angle represents a higher number of vertices. float m_arcRotationStepRadians; //! Thickness of for the axis line rendering. float m_lineThickness = 4.f; //! Draws angle decorator for the current rotation axis. void DrawAngleDecorator(DisplayContext& dc); //! Useful for debugging and visualizing hit testing void DrawHitTestGeometry(DisplayContext& dc, HitContext& hc); //! Diagnostic tool to examine view direction angle (follows mouse cursor) void DrawViewDirectionAngleTracking(DisplayContext& dc, HitContext& hc); //! Callback registered to receive Selection callbacks to set m_object bool OnSelectObject(CBaseObject* object) override; //! Callback to check that an object can be selected bool CanSelectObject(CBaseObject* object) override; //! Callback installed on the object, used to determine destruction or deselection. void OnObjectEvent(CBaseObject* object, int event); //! Handle key down events. bool OnKeyDown(CViewport* view, uint32 nChar, uint32 nRepCnt, uint32 nFlags) override; //! Retrieves the object's transformation according to the specified reference coordinate system. Matrix34 GetTransform(RefCoordSys referenceCoordinateSystem, IDisplayViewport* view); //! Calculate orientation of 3 points on screen, return 1.0f if clockwise, -1.0f if counter-clockwise float CalculateOrientation(const QPoint& p1, const QPoint& p2, const QPoint& p3); private: HitContext m_hc; //!< HACK: Cache the hitcontext given that it's values may differ depending on the viewport they are coming from. }; //! Singleton that holds all the configuration cvars for the different features and debug options //! used by the CRotationControl class RotationControlConfiguration { public: static RotationControlConfiguration& Get() { static RotationControlConfiguration instance; return instance; } //! If enabled it will draw the inner rotation decorator. DeclareConstIntCVar(RotationControl_DrawDecorators, 0); //! If enabled the hit testing geometry is rendered. DeclareConstIntCVar(RotationControl_DebugHitTesting, 0); //! If enabled a sphere will be drawn to represent the view axis angle to the mouse cursor. DeclareConstIntCVar(RotationControl_AngleTracking, 0); private: RotationControlConfiguration(); RotationControlConfiguration(const RotationControlConfiguration&) = delete; RotationControlConfiguration& operator = (const RotationControlConfiguration&) = delete; ~RotationControlConfiguration() {} }; #endif // CRYINCLUDE_EDITOR_ROTATETOOL_H