1 #include <Core/Asset/Camera.hpp>
2 #include <Core/Containers/MakeShared.hpp>
3 #include <Core/Math/Math.hpp>
8 using Core::Math::PiDiv2;
9 using Core::Math::PiDiv4;
15 Camera::Camera( Scalar width, Scalar height ) :
16 m_width { width }, m_height { height }, m_aspect { width / height } {}
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();
24 m_zNear = rhs.getZNear();
25 m_zFar = rhs.getZFar();
28 setViewport( rhs.getWidth(), rhs.getHeight() );
32 void Camera::setDirection(
const Core::Vector3& direction ) {
33 Core::Transform T = Core::Transform::Identity();
35 Core::Vector3 d0 = getDirection();
36 Core::Vector3 d1 = direction.normalized();
38 Core::Vector3 c = d0.cross( d1 );
39 Scalar d = d0.dot( d1 );
43 if ( c.isApprox( Core::Vector3::Zero() ) && d < 0.0 ) {
44 T.rotate( Core::AngleAxis( Core::Math::Pi, getUpVector() ) );
46 else { T.rotate( Core::Quaternion::FromTwoVectors( d0, d1 ) ); }
50 void Camera::setViewport( Scalar width, Scalar height ) {
53 m_aspect = width / height;
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();
63 m_frame = t2 * T * t1 * m_frame;
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 );
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 );
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 );
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;
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 );
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 );
107 projMatrix << 2_ra * n / ( r - l ), 0_ra, A, 0_ra,
108 0_ra, 2_ra * n / ( t - b ), B, 0_ra,
110 0_ra, 0_ra, -1_ra, 0_ra;
116 Core::Matrix4 Camera::ortho( Scalar l, Scalar r, Scalar b, Scalar t, Scalar n, Scalar f ) {
117 Core::Matrix4 projMatrix;
118 projMatrix.setZero();
121 -( r + l ) / ( r - l ), -( t + b ) / ( t - b ), -( f + n ) / ( f - n ), 1_ra );
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;
132 void Camera::fitZRange(
const Core::Aabb& aabb ) {
134 const Core::Vector3& position = getPosition();
135 Core::Vector3 direction = -m_frame.affine().block<3, 1>( 0, 2 );
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 );
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 );
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();
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 );
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] );
172 if ( m_zNear < m_minZNear ) { m_zNear = m_minZNear; }
173 if ( ( m_zFar - m_zNear ) < m_minZRange ) { m_zFar = m_zNear + m_minZRange; }
178 Core::Ray Camera::getRayFromScreen(
const Core::Vector2& pix )
const {
180 return Core::Ray::Through( getPosition(), unProjectFromScreen( pix ) );
183 Core::Vector3 Camera::projectToNDC(
const Core::Vector3& p )
const {
184 Core::Vector4 point = Core::Vector4::Ones();
186 Core::Vector4 vpPoint = getProjMatrix() * getViewMatrix() * point;
187 vpPoint = vpPoint / vpPoint.w();
188 return vpPoint.head<3>();
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 );
198 Core::Vector3 Camera::unProjectFromScreen(
const Core::Vector2& pix )
const {
199 return unProjectFromScreen( Vector3 { pix.x(), pix.y(), 0_ra } );
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 );
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() );