Radium Engine  1.5.0
Camera.cpp
1 #include <Core/Asset/Camera.hpp>
2 #include <Core/Containers/MakeShared.hpp>
3 #include <Core/Math/Math.hpp>
4 
5 namespace Ra {
6 
7 using Core::Math::Pi;
8 using Core::Math::PiDiv2;
9 using Core::Math::PiDiv4;
10 
11 namespace Core {
12 
13 namespace Asset {
14 
15 Camera::Camera( Scalar width, Scalar height ) :
16  m_width { width }, m_height { height }, m_aspect { width / height } {}
17 
18 Camera& Camera::operator=( const Camera& rhs ) {
19  m_frame = rhs.getFrame();
20  m_projMatrix = rhs.getProjMatrix();
21  m_projType = rhs.getType();
22  m_zoomFactor = rhs.getZoomFactor();
23  m_fov = rhs.getFOV();
24  m_zNear = rhs.getZNear();
25  m_zFar = rhs.getZFar();
26  m_xmag = rhs.m_xmag;
27  m_ymag = rhs.m_ymag;
28  setViewport( rhs.getWidth(), rhs.getHeight() );
29  return *this;
30 }
31 
32 void Camera::setDirection( const Core::Vector3& direction ) {
33  Core::Transform T = Core::Transform::Identity();
34 
35  Core::Vector3 d0 = getDirection();
36  Core::Vector3 d1 = direction.normalized();
37 
38  Core::Vector3 c = d0.cross( d1 );
39  Scalar d = d0.dot( d1 );
40 
41  // Special case if two directions are exactly opposites we constrain.
42  // to rotate around the up vector.
43  if ( c.isApprox( Core::Vector3::Zero() ) && d < 0.0 ) {
44  T.rotate( Core::AngleAxis( Core::Math::Pi, getUpVector() ) );
45  }
46  else { T.rotate( Core::Quaternion::FromTwoVectors( d0, d1 ) ); }
47  applyTransform( T );
48 }
49 
50 void Camera::setViewport( Scalar width, Scalar height ) {
51  m_width = width;
52  m_height = height;
53  m_aspect = width / height;
54  updateProjMatrix();
55 }
56 
57 void Camera::applyTransform( const Core::Transform& T ) {
58  Core::Transform t1 = Core::Transform::Identity();
59  Core::Transform t2 = Core::Transform::Identity();
60  t1.translation() = -getPosition();
61  t2.translation() = getPosition();
62 
63  m_frame = t2 * T * t1 * m_frame;
64 }
65 
66 void Camera::updateProjMatrix() {
67  switch ( m_projType ) {
68  case ProjType::ORTHOGRAPHIC: {
69  const Scalar r = m_xmag * m_zoomFactor;
70  const Scalar t = m_ymag * m_zoomFactor / m_aspect;
71  m_projMatrix = ortho( -r, r, -t, t, m_zNear, m_zFar );
72  } break;
73 
74  case ProjType::PERSPECTIVE: {
75  Scalar fov = std::clamp( m_zoomFactor * m_fov, 0.001_ra, Math::Pi - 0.1_ra );
76  m_projMatrix = perspective( m_aspect, fov, m_zNear, m_zFar );
77  } break;
78 
79  default:
80  break;
81  }
82 }
83 
84 Core::Matrix4 Camera::perspective( Scalar a, Scalar fov, Scalar n, Scalar f ) {
85  Core::Matrix4 projMatrix;
86  Scalar tanf = std::tan( fov * .5_ra );
87 
88  // clang-format off
89  projMatrix << 1_ra / tanf, 0_ra, 0_ra, 0_ra,
90  0_ra, a / tanf, 0_ra, 0_ra,
91  0_ra, 0_ra, ( f + n ) / ( n - f ), ( 2_ra * f * n ) / ( n - f ),
92  0_ra, 0_ra, -1_ra, 0_ra;
93  // clang-format on
94 
95  return projMatrix;
96 }
97 
98 Core::Matrix4 Camera::frustum( Scalar l, Scalar r, Scalar b, Scalar t, Scalar n, Scalar f ) {
99  Core::Matrix4 projMatrix;
100  projMatrix.setZero();
101  const Scalar A = ( r + l ) / ( r - l );
102 
103  const Scalar B = ( t + b ) / ( t - b );
104  const Scalar C = ( f + n ) / ( n - f );
105  const Scalar D = ( 2_ra * f * n ) / ( n - f );
106  // clang-format off
107  projMatrix << 2_ra * n / ( r - l ), 0_ra, A, 0_ra,
108  0_ra, 2_ra * n / ( t - b ), B, 0_ra,
109  0_ra, 0_ra, C, D,
110  0_ra, 0_ra, -1_ra, 0_ra;
111  // clang-format on
112 
113  return projMatrix;
114 }
115 
116 Core::Matrix4 Camera::ortho( Scalar l, Scalar r, Scalar b, Scalar t, Scalar n, Scalar f ) {
117  Core::Matrix4 projMatrix;
118  projMatrix.setZero();
119 
120  Core::Vector4 tr(
121  -( r + l ) / ( r - l ), -( t + b ) / ( t - b ), -( f + n ) / ( f - n ), 1_ra );
122 
123  projMatrix.setIdentity();
124  projMatrix.coeffRef( 0, 0 ) = 2_ra / ( r - l );
125  projMatrix.coeffRef( 1, 1 ) = 2_ra / ( t - b );
126  projMatrix.coeffRef( 2, 2 ) = 2_ra / ( n - f );
127  projMatrix.block<4, 1>( 0, 3 ) = tr;
128 
129  return projMatrix;
130 }
131 
132 void Camera::fitZRange( const Core::Aabb& aabb ) {
133 #if 1
134  const Core::Vector3& position = getPosition();
135  Core::Vector3 direction = -m_frame.affine().block<3, 1>( 0, 2 );
136 
137  const Core::Vector3& minAabb = aabb.min();
138  const Core::Vector3& maxAabb = aabb.max();
139  this->m_zNear = this->m_zFar = direction.dot( minAabb - position );
140 
141  auto adaptRange = [position, direction, this]( Scalar x, Scalar y, Scalar z ) {
142  Ra::Core::Vector3 corner( x, y, z );
143  auto d = direction.dot( corner - position );
144  this->m_zNear = std::min( d, this->m_zNear );
145  this->m_zFar = std::max( d, this->m_zFar );
146  };
147 #else
148 
149  const auto position = Ra::Core::Vector3 { 0_ra, 0_ra, 0_ra };
150  const auto direction = Ra::Core::Vector3 { 0_ra, 0_ra, -1_ra };
151  const auto view = getFrame().inverse();
152  const auto& minAabb = aabb.min();
153  const auto& maxAabb = aabb.max();
154 
155  m_zNear = m_zFar = direction.dot( view * minAabb - position );
156  auto adaptRange = [view, position, direction, this]( Scalar x, Scalar y, Scalar z ) {
157  auto corner = Core::Vector3 { x, y, z };
158  auto d = direction.dot( view * corner - position );
159  this->m_zNear = std::min( d, this->m_zNear );
160  this->m_zFar = std::max( d, this->m_zFar );
161  };
162 #endif
163 
164  adaptRange( minAabb[0], minAabb[1], maxAabb[2] );
165  adaptRange( minAabb[0], maxAabb[1], minAabb[2] );
166  adaptRange( minAabb[0], maxAabb[1], maxAabb[2] );
167  adaptRange( maxAabb[0], maxAabb[1], maxAabb[2] );
168  adaptRange( maxAabb[0], maxAabb[1], minAabb[2] );
169  adaptRange( maxAabb[0], minAabb[1], maxAabb[2] );
170  adaptRange( maxAabb[0], minAabb[1], minAabb[2] );
171 
172  if ( m_zNear < m_minZNear ) { m_zNear = m_minZNear; }
173  if ( ( m_zFar - m_zNear ) < m_minZRange ) { m_zFar = m_zNear + m_minZRange; }
174 
175  updateProjMatrix();
176 }
177 
178 Core::Ray Camera::getRayFromScreen( const Core::Vector2& pix ) const {
179  // Ray starts from the camera's current position.
180  return Core::Ray::Through( getPosition(), unProjectFromScreen( pix ) );
181 }
182 
183 Core::Vector3 Camera::projectToNDC( const Core::Vector3& p ) const {
184  Core::Vector4 point = Core::Vector4::Ones();
185  point.head<3>() = p;
186  Core::Vector4 vpPoint = getProjMatrix() * getViewMatrix() * point;
187  vpPoint = vpPoint / vpPoint.w();
188  return vpPoint.head<3>();
189 }
190 
191 Core::Vector3 Camera::projectToScreen( const Core::Vector3& p ) const {
192  Core::Vector3 vpPoint = projectToNDC( p );
193  return Core::Vector3( getWidth() * 0.5_ra * ( 1_ra + vpPoint.x() ),
194  getHeight() * 0.5_ra * ( 1_ra - vpPoint.y() ),
195  vpPoint.z() * .5_ra + .5_ra );
196 }
197 
198 Core::Vector3 Camera::unProjectFromScreen( const Core::Vector2& pix ) const {
199  return unProjectFromScreen( Vector3 { pix.x(), pix.y(), 0_ra } );
200 }
201 
202 Core::Vector3 Camera::unProjectFromScreen( const Core::Vector3& pix ) const {
203  Core::Vector3 ndc { pix.cwiseProduct( Core::Vector3( 2_ra, -2_ra, 2_ra ) )
204  .cwiseQuotient( Core::Vector3( getWidth(), getHeight(), 1_ra ) ) +
205  Core::Vector3 { -1_ra, 1_ra, -1_ra } };
206  return unProjectFromNDC( ndc );
207 }
208 
209 Core::Vector3 Camera::unProjectFromNDC( const Core::Vector3& ndc ) const {
210  Core::Vector4 localPoint4;
211  localPoint4 << ndc, 1_ra;
212  const Core::Vector4 unproj = getProjMatrix().inverse() * localPoint4;
213  return getFrame() * ( unproj.head<3>() / unproj.w() );
214 }
215 } // namespace Asset
216 } // namespace Core
217 } // namespace Ra
Definition: Cage.cpp:3