Radium Engine  1.5.0
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 
13 namespace Ra {
14 namespace Gui {
15 
16 RotateGizmo::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 
48 void 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 
69 void 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 
88 Core::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 
170 void 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
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
Definition: Cage.cpp:3