Radium Engine  1.5.20
Loading...
Searching...
No Matches
RotateGizmo.cpp
1#include <Gui/Viewer/Gizmo/RotateGizmo.hpp>
2
3#include <Core/Containers/VectorArray.hpp>
4#include <Core/Geometry/MeshPrimitives.hpp>
5#include <Core/Utils/Color.hpp>
6
7#include <Core/Asset/Camera.hpp>
8#include <Engine/Data/Mesh.hpp>
9#include <Engine/Rendering/RenderObject.hpp>
10#include <Engine/Rendering/RenderTechnique.hpp>
11#include <Engine/Scene/CameraComponent.hpp>
12
13namespace Ra {
14namespace Gui {
15
16RotateGizmo::RotateGizmo( Engine::Scene::Component* c,
17 const Core::Transform& worldTo,
18 const Core::Transform& t,
19 Mode mode ) :
20 Gizmo( c, worldTo, t, mode ), m_initialPix( Core::Vector2::Zero() ), m_selectedAxis( -1 ) {
21 constexpr Scalar torusOutRadius = .1_ra;
22 constexpr Scalar torusAspectRatio = .08_ra;
23 // For x,y,z
24 for ( uint i = 0; i < 3; ++i ) {
25 Core::Geometry::TriangleMesh torus = Core::Geometry::makeParametricTorus<32>(
26 torusOutRadius, torusAspectRatio * torusOutRadius );
27 // Transform the torus from z-axis to axis i.
28 auto& data = torus.verticesWithLock();
29 for ( auto& v : data ) {
30 v = .5_ra * v;
31 if ( i < 2 ) { std::swap( v[2], v[i] ); }
32 }
33 torus.verticesUnlock();
34
35 auto mesh = std::shared_ptr<Engine::Data::Mesh>( new Engine::Data::Mesh( "Gizmo Torus" ) );
36 mesh->loadGeometry( std::move( torus ) );
37
38 auto torusDrawable = new Engine::Rendering::RenderObject(
39 "Gizmo Torus", m_comp, Engine::Rendering::RenderObjectType::UI );
40 auto rt = std::shared_ptr<Engine::Rendering::RenderTechnique>( makeRenderTechnique( i ) );
41 torusDrawable->setRenderTechnique( rt );
42 torusDrawable->setMesh( mesh );
43 addRenderObject( torusDrawable );
44 }
45 updateTransform( mode, m_worldTo, m_transform );
46}
47
48void RotateGizmo::updateTransform( Gizmo::Mode mode,
49 const Core::Transform& worldTo,
50 const Core::Transform& t ) {
51 m_mode = mode;
52 m_worldTo = worldTo;
53 m_transform = t;
54 Core::Transform displayTransform = Core::Transform::Identity();
55 displayTransform.translate( m_transform.translation() );
56 if ( m_mode == LOCAL ) {
57 Core::Matrix3 R = m_transform.rotation();
58 R.col( 0 ).normalize();
59 R.col( 1 ).normalize();
60 R.col( 2 ).normalize();
61 displayTransform.rotate( R );
62 }
63
64 for ( auto ro : ros() ) {
65 ro->setLocalTransform( m_worldTo * displayTransform );
66 }
67}
68
69void RotateGizmo::selectConstraint( int drawableIdx ) {
70 // deselect previously selected axis
71 if ( m_selectedAxis != -1 ) {
72 getControler( m_selectedAxis )->clearState();
73 m_selectedAxis = -1;
74 }
75 // Return if no component is selected
76 if ( drawableIdx < 0 ) { return; }
77
78 // update the state of the selected component
79 auto found = std::find_if( ros().cbegin(), ros().cend(), [drawableIdx]( const auto& ro ) {
80 return ro->getIndex() == Core::Utils::Index( drawableIdx );
81 } );
82 if ( found != ros().cend() ) {
83 m_selectedAxis = int( std::distance( ros().cbegin(), found ) );
84 getControler( m_selectedAxis )->setState();
85 }
86}
87
88Core::Transform RotateGizmo::mouseMove( const Core::Asset::Camera& cam,
89 const Core::Vector2& nextXY,
90 bool stepped,
91 bool /*whole*/ ) {
92 static const Scalar step = Ra::Core::Math::Pi / 10_ra;
93
94 if ( m_selectedAxis == -1 ) return m_transform;
95
96 // Decompose the current transform's linear part into rotation and scale
97 Core::Matrix3 rotationMat;
98 Core::Matrix3 scaleMat;
99 m_transform.computeRotationScaling( &rotationMat, &scaleMat );
100
101 // Get gizmo center and rotation axis
102 const Core::Vector3 origin = m_transform.translation();
103 Core::Vector3 rotationAxis = Core::Vector3::Unit( m_selectedAxis );
104 if ( m_mode == LOCAL ) { rotationAxis = rotationMat * rotationAxis; }
105 rotationAxis.normalize();
106 const Core::Vector3 originW = m_worldTo * origin;
107 const Core::Vector3 rotationAxisW = m_worldTo * rotationAxis;
108
109 // Initialize rotation
110 if ( !m_start ) {
111 m_start = true;
112 m_totalAngle = 0;
113 m_initialRot = rotationMat;
114 }
115
116 // Project the clicked points against the plane defined by the rotation circles.
117 std::vector<Scalar> hits1, hits2;
118 Core::Vector3 originalHit, currentHit;
119 bool hit1 = findPointOnPlane( cam, originW, rotationAxisW, m_initialPix, originalHit, hits1 );
120 bool hit2 = findPointOnPlane( cam, originW, rotationAxisW, nextXY, currentHit, hits2 );
121
122 // Compute the rotation angle
123 Scalar angle;
124 // standard check + guard against precision issues
125 if ( hit1 && hit2 && hits1[0] > .2_ra && hits2[0] > .2_ra ) {
126 // Do the calculations relative to the circle center.
127 originalHit -= originW;
128 currentHit -= originW;
129
130 // Get the angle between the two vectors with the correct sign
131 // (since we already know our current rotation axis).
132 auto c = originalHit.cross( currentHit );
133 Scalar d = originalHit.dot( currentHit );
134
135 angle = Core::Math::sign( c.dot( rotationAxisW ) ) * std::atan2( c.norm(), d );
136 }
137 else {
138 // Rotation plane is orthogonal to the image plane
139 Core::Vector2 dir = ( cam.projectToScreen( originW + rotationAxisW ).head<2>() -
140 cam.projectToScreen( originW ).head<2>() )
141 .normalized();
142 if ( std::abs( dir( 0 ) ) < 1e-3_ra ) { dir << 1, 0; }
143 else if ( std::abs( dir( 1 ) ) < 1e-3_ra ) { dir << 0, 1; }
144 else { dir = Core::Vector2( dir( 1 ), -dir( 0 ) ); }
145 Scalar diag = std::min( cam.getWidth(), cam.getHeight() );
146 angle = dir.dot( ( nextXY - m_initialPix ) ) * 8_ra / diag;
147 }
148 if ( std::isnan( angle ) ) { angle = 0_ra; }
149 // Apply rotation
150 Core::Vector2 nextXY_ = nextXY;
151 if ( stepped ) {
152 angle = int( angle / step ) * step;
153 if ( Core::Math::areApproxEqual( angle, 0_ra ) ) { nextXY_ = m_initialPix; }
154 if ( !m_stepped ) {
155 Scalar diff = m_totalAngle - int( m_totalAngle / step ) * step;
156 angle -= diff;
157 }
158 }
159 m_stepped = stepped;
160 m_totalAngle += angle;
161 if ( !Core::Math::areApproxEqual( angle, 0_ra ) ) {
162 auto newRot = Core::AngleAxis( angle, rotationAxis ) * rotationMat;
163 m_transform.fromPositionOrientationScale( origin, newRot, scaleMat.diagonal() );
164 }
165 m_initialPix = nextXY_;
166
167 return m_transform;
168}
169
170void RotateGizmo::setInitialState( const Core::Asset::Camera& /*cam*/,
171 const Core::Vector2& initialXY ) {
172 m_initialPix = initialXY;
173 m_start = false;
174 m_stepped = false;
175}
176
177} // namespace Gui
178} // namespace Ra
T atan2(T... args)
Camera class storing the Camera frame and the projection properties The view direction is -z in camer...
Definition Camera.hpp:16
Scalar getWidth() const
Return the width of the viewport.
Definition Camera.hpp:311
Scalar getHeight() const
Return the height of the viewport.
Definition Camera.hpp:315
Core::Vector3 projectToScreen(const Core::Vector3 &p) const
Return the screen coordinates + depth of the given point p (in world coordinates).
Definition Camera.cpp:191
T distance(T... args)
T find_if(T... args)
T isnan(T... args)
T min(T... args)
T move(T... args)
hepler function to manage enum as underlying types in VariableSet
Definition Cage.cpp:3
T swap(T... args)