Radium Engine  1.5.20
Loading...
Searching...
No Matches
Camera.cpp
1#include <Core/Asset/Camera.hpp>
2#include <Core/Containers/MakeShared.hpp>
3#include <Core/Math/Math.hpp>
4
5namespace Ra {
6
7using Core::Math::Pi;
8using Core::Math::PiDiv2;
9using Core::Math::PiDiv4;
10
11namespace Core {
12
13namespace Asset {
14
15Camera::Camera( Scalar width, Scalar height ) :
16 m_width { width }, m_height { height }, m_aspect { width / height } {}
17
18Camera& 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
32void 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 ( Math::areApproxEqual( c.squaredNorm(), 0_ra ) && d < 0_ra ) {
44 T.rotate( Core::AngleAxis( Core::Math::Pi, getUpVector() ) );
45 }
46 else { T.rotate( Core::Quaternion::FromTwoVectors( d0, d1 ) ); }
47 applyTransform( T );
48}
49
50void Camera::setViewport( Scalar width, Scalar height ) {
51 m_width = width;
52 m_height = height;
53 m_aspect = width / height;
54 updateProjMatrix();
55}
56
57void 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
66void 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
84Core::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
98Core::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
116Core::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
132void 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
178Core::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
183Core::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
191Core::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
198Core::Vector3 Camera::unProjectFromScreen( const Core::Vector2& pix ) const {
199 return unProjectFromScreen( Vector3 { pix.x(), pix.y(), 0_ra } );
200}
201
202Core::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
209Core::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
T max(T... args)
T min(T... args)
hepler function to manage enum as underlying types in VariableSet
Definition Cage.cpp:3
T tan(T... args)