You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/Sandbox/Editor/RotateTool.h

284 lines
12 KiB
C++

/*
* 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
#if !defined(Q_MOC_RUN)
#include "EditTool.h"
#include "IObjectManager.h"
#include "EditMode/ObjectMode.h"
#include "Objects/BaseObject.h" // for CBaseObject::EventListener
#include "Objects/DisplayContext.h"
#include "Include/HitContext.h"
#endif
//! 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<Vec3> 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
, public CBaseObject::EventListener
{
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([[maybe_unused]] CBaseObject* pObject, [[maybe_unused]] 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
};
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
//! 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) override;
//! 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);
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
//! 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:
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
HitContext m_hc; //!< HACK: Cache the hitcontext given that it's values may differ depending on the viewport they are coming from.
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
};
//! 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