2 * Copyright (c) 2018 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali/internal/update/render-tasks/scene-graph-camera.h>
25 #include <dali/integration-api/debug.h>
26 #include <dali/public-api/common/dali-common.h>
27 #include <dali/public-api/math/math-utils.h>
28 #include <dali/internal/update/nodes/node.h>
30 namespace // unnamed namespace
32 const uint32_t UPDATE_COUNT = 2u; // Update projection or view matrix this many frames after a change
33 const uint32_t COPY_PREVIOUS_MATRIX = 1u; // Copy view or projection matrix from previous frame
48 template< typename T >
51 return T( T(0) < value ) - T( value < T(0) );
54 void LookAt(Matrix& result, const Vector3& eye, const Vector3& target, const Vector3& up)
56 Vector3 vZ = target - eye;
59 Vector3 vX = up.Cross(vZ);
62 Vector3 vY = vZ.Cross(vX);
65 result.SetInverseTransformComponents(vX, vY, vZ, eye);
68 void Frustum(Matrix& result, float left, float right, float bottom, float top, float near, float far, bool invertYAxis)
70 float deltaZ = far - near;
71 if ((near <= 0.0f) || (far <= 0.0f) || Equals(right, left) || Equals(bottom, top) || (deltaZ <= 0.0f))
73 DALI_LOG_ERROR("Invalid parameters passed into Frustum!\n");
74 DALI_ASSERT_DEBUG("Invalid parameters passed into Frustum!");
78 float deltaX = right - left;
79 float deltaY = invertYAxis ? bottom - top : top - bottom;
83 float* m = result.AsFloat();
84 m[0] = -2.0f * near / deltaX;
85 m[1] = m[2] = m[3] = 0.0f;
87 m[5] = -2.0f * near / deltaY;
88 m[4] = m[6] = m[7] = 0.0f;
90 m[8] = (right + left) / deltaX;
91 m[9] = (top + bottom) / deltaY;
92 m[10] = (near + far) / deltaZ;
95 m[14] = -2.0f * near * far / deltaZ;
96 m[12] = m[13] = m[15] = 0.0f;
99 void Perspective(Matrix& result, float fovy, float aspect, float near, float far, bool invertYAxis )
101 float frustumH = tanf( fovy * 0.5f ) * near;
102 float frustumW = frustumH * aspect;
104 Frustum(result, -frustumW, frustumW, -frustumH, frustumH, near, far, invertYAxis);
107 void Orthographic(Matrix& result, float left, float right, float bottom, float top, float near, float far, bool invertYAxis)
109 if ( Equals(right, left) || Equals(top, bottom) || Equals(far, near) )
111 DALI_LOG_ERROR( "Cannot create orthographic projection matrix with a zero dimension.\n" );
112 DALI_ASSERT_DEBUG( "Cannot create orthographic projection matrix with a zero dimension." );
116 float deltaX = right - left;
117 float deltaY = invertYAxis ? bottom - top : top - bottom;
118 float deltaZ = far - near;
120 float *m = result.AsFloat();
121 m[0] = -2.0f / deltaX;
127 m[5] = -2.0f / deltaY;
133 m[10] = 2.0f / deltaZ;
135 m[12] = -(right + left) / deltaX;
136 m[13] = -(top + bottom) / deltaY;
137 m[14] = -(near + far) / deltaZ;
141 } // unnamed namespace
143 const Dali::Camera::Type Camera::DEFAULT_TYPE( Dali::Camera::FREE_LOOK );
144 const Dali::Camera::ProjectionMode Camera::DEFAULT_MODE( Dali::Camera::PERSPECTIVE_PROJECTION );
145 const bool Camera::DEFAULT_INVERT_Y_AXIS( false );
146 const float Camera::DEFAULT_FIELD_OF_VIEW( 45.0f*(Math::PI/180.0f) );
147 const float Camera::DEFAULT_ASPECT_RATIO( 4.0f/3.0f );
148 const float Camera::DEFAULT_LEFT_CLIPPING_PLANE(-240.0f);
149 const float Camera::DEFAULT_RIGHT_CLIPPING_PLANE(240.0f);
150 const float Camera::DEFAULT_TOP_CLIPPING_PLANE(-400.0f);
151 const float Camera::DEFAULT_BOTTOM_CLIPPING_PLANE(400.0f);
152 const float Camera::DEFAULT_NEAR_CLIPPING_PLANE( 800.0f ); // default height of the screen
153 const float Camera::DEFAULT_FAR_CLIPPING_PLANE( DEFAULT_NEAR_CLIPPING_PLANE + 2.f * DEFAULT_NEAR_CLIPPING_PLANE );
154 const Vector3 Camera::DEFAULT_TARGET_POSITION( 0.0f, 0.0f, 0.0f );
158 : mUpdateViewFlag( UPDATE_COUNT ),
159 mUpdateProjectionFlag( UPDATE_COUNT ),
160 mProjectionRotation( 0 ),
162 mType( DEFAULT_TYPE ),
163 mProjectionMode( DEFAULT_MODE ),
164 mInvertYAxis( DEFAULT_INVERT_Y_AXIS ),
165 mFieldOfView( DEFAULT_FIELD_OF_VIEW ),
166 mAspectRatio( DEFAULT_ASPECT_RATIO ),
167 mLeftClippingPlane( DEFAULT_LEFT_CLIPPING_PLANE ),
168 mRightClippingPlane( DEFAULT_RIGHT_CLIPPING_PLANE ),
169 mTopClippingPlane( DEFAULT_TOP_CLIPPING_PLANE ),
170 mBottomClippingPlane( DEFAULT_BOTTOM_CLIPPING_PLANE ),
171 mNearClippingPlane( DEFAULT_NEAR_CLIPPING_PLANE ),
172 mFarClippingPlane( DEFAULT_FAR_CLIPPING_PLANE ),
173 mTargetPosition( DEFAULT_TARGET_POSITION ),
176 mInverseViewProjection( Matrix::IDENTITY ),
177 mFinalProjection( Matrix::IDENTITY )
181 Camera* Camera::New()
190 void Camera::SetNode( const Node* node )
195 void Camera::SetType( Dali::Camera::Type type )
200 void Camera::SetProjectionMode( Dali::Camera::ProjectionMode mode )
202 mProjectionMode = mode;
203 mUpdateProjectionFlag = UPDATE_COUNT;
206 void Camera::SetInvertYAxis( bool invertYAxis )
208 mInvertYAxis = invertYAxis;
209 mUpdateProjectionFlag = UPDATE_COUNT;
212 void Camera::SetFieldOfView( float fieldOfView )
214 mFieldOfView = fieldOfView;
215 mUpdateProjectionFlag = UPDATE_COUNT;
218 void Camera::SetAspectRatio( float aspectRatio )
220 mAspectRatio = aspectRatio;
221 mUpdateProjectionFlag = UPDATE_COUNT;
224 void Camera::SetLeftClippingPlane( float leftClippingPlane )
226 mLeftClippingPlane = leftClippingPlane;
227 mUpdateProjectionFlag = UPDATE_COUNT;
230 void Camera::SetRightClippingPlane( float rightClippingPlane )
232 mRightClippingPlane = rightClippingPlane;
233 mUpdateProjectionFlag = UPDATE_COUNT;
236 void Camera::SetTopClippingPlane( float topClippingPlane )
238 mTopClippingPlane = topClippingPlane;
239 mUpdateProjectionFlag = UPDATE_COUNT;
242 void Camera::SetBottomClippingPlane( float bottomClippingPlane )
244 mBottomClippingPlane = bottomClippingPlane;
245 mUpdateProjectionFlag = UPDATE_COUNT;
248 void Camera::SetNearClippingPlane( float nearClippingPlane )
250 mNearClippingPlane = nearClippingPlane;
251 mUpdateProjectionFlag = UPDATE_COUNT;
254 void Camera::SetFarClippingPlane( float farClippingPlane )
256 mFarClippingPlane = farClippingPlane;
257 mUpdateProjectionFlag = UPDATE_COUNT;
260 void Camera::SetTargetPosition( const Vector3& targetPosition )
262 mTargetPosition = targetPosition;
263 mUpdateViewFlag = UPDATE_COUNT;
266 void Camera::RotateProjection( int rotationAngle )
268 mProjectionRotation = rotationAngle;
269 mUpdateViewFlag = UPDATE_COUNT;
272 const Matrix& Camera::GetProjectionMatrix( BufferIndex bufferIndex ) const
274 return mProjectionMatrix[ bufferIndex ];
277 const Matrix& Camera::GetViewMatrix( BufferIndex bufferIndex ) const
279 return mViewMatrix[ bufferIndex ];
282 const Matrix& Camera::GetInverseViewProjectionMatrix( BufferIndex bufferIndex ) const
284 return mInverseViewProjection[ bufferIndex ];
287 const Matrix& Camera::GetFinalProjectionMatrix( BufferIndex bufferIndex ) const
289 return mFinalProjection[ bufferIndex ];
292 const PropertyInputImpl* Camera::GetProjectionMatrix() const
294 return &mProjectionMatrix;
297 const PropertyInputImpl* Camera::GetViewMatrix() const
302 void Camera::Update( BufferIndex updateBufferIndex )
304 // if owning node has changes in world position we need to update camera for next 2 frames
305 if( mNode->IsLocalMatrixDirty() )
307 mUpdateViewFlag = UPDATE_COUNT;
309 if( mNode->GetDirtyFlags() & NodePropertyFlags::VISIBLE )
311 // If the visibility changes, the projection matrix needs to be re-calculated.
312 // It may happen the first time an actor is rendered it's rendered only once and becomes invisible,
313 // in the following update the node will be skipped leaving the projection matrix (double buffered)
314 // with the Identity.
315 mUpdateProjectionFlag = UPDATE_COUNT;
318 // if either matrix changed, we need to recalculate the inverse matrix for hit testing to work
319 uint32_t viewUpdateCount = UpdateViewMatrix( updateBufferIndex );
320 uint32_t projectionUpdateCount = UpdateProjection( updateBufferIndex );
322 // if model or view matrix changed we need to either recalculate the inverse VP or copy previous
323 if( viewUpdateCount > COPY_PREVIOUS_MATRIX || projectionUpdateCount > COPY_PREVIOUS_MATRIX )
325 // either has actually changed so recalculate
326 Matrix::Multiply( mInverseViewProjection[ updateBufferIndex ], mViewMatrix[ updateBufferIndex ], mProjectionMatrix[ updateBufferIndex ] );
327 UpdateFrustum( updateBufferIndex );
329 // ignore the error, if the view projection is incorrect (non inversible) then you will have tough times anyways
330 static_cast< void >( mInverseViewProjection[ updateBufferIndex ].Invert() );
332 else if( viewUpdateCount == COPY_PREVIOUS_MATRIX || projectionUpdateCount == COPY_PREVIOUS_MATRIX )
334 // neither has actually changed, but we might copied previous frames value so need to
335 // copy the previous inverse and frustum as well
336 mInverseViewProjection[updateBufferIndex] = mInverseViewProjection[updateBufferIndex ? 0 : 1];
337 mFrustum[ updateBufferIndex ] = mFrustum[ updateBufferIndex ? 0 : 1 ];
341 bool Camera::ViewMatrixUpdated()
343 return 0u != mUpdateViewFlag;
346 uint32_t Camera::UpdateViewMatrix( BufferIndex updateBufferIndex )
348 uint32_t retval( mUpdateViewFlag );
349 if( 0u != mUpdateViewFlag )
351 if( COPY_PREVIOUS_MATRIX == mUpdateViewFlag )
353 // The projection matrix was updated in the previous frame; copy it
354 mViewMatrix.CopyPrevious( updateBufferIndex );
356 else // UPDATE_COUNT == mUpdateViewFlag
360 // camera orientation taken from node - i.e. look in abitrary, unconstrained direction
361 case Dali::Camera::FREE_LOOK:
363 Matrix& viewMatrix = mViewMatrix.Get( updateBufferIndex );
364 viewMatrix = mNode->GetWorldMatrix( updateBufferIndex );
366 mViewMatrix.SetDirty( updateBufferIndex );
369 // camera orientation constrained to look at a target
370 case Dali::Camera::LOOK_AT_TARGET:
372 const Matrix& owningNodeMatrix( mNode->GetWorldMatrix( updateBufferIndex ) );
373 Vector3 position, scale;
374 Quaternion orientation;
375 owningNodeMatrix.GetTransformComponents( position, orientation, scale );
376 Matrix& viewMatrix = mViewMatrix.Get( updateBufferIndex );
377 LookAt( viewMatrix, position, mTargetPosition, orientation.Rotate( Vector3::YAXIS ) );
378 mViewMatrix.SetDirty( updateBufferIndex );
388 void Camera::UpdateFrustum( BufferIndex updateBufferIndex, bool normalize )
391 // Extract the clip matrix planes
393 Matrix::Multiply( clipMatrix, mViewMatrix[ updateBufferIndex ], mProjectionMatrix[ updateBufferIndex ] );
395 const float* cm = clipMatrix.AsFloat();
396 FrustumPlanes& planes = mFrustum[ updateBufferIndex ];
399 planes.mPlanes[ 0 ].mNormal.x = cm[ 3 ] + cm[ 0 ]; // column 4 + column 1
400 planes.mPlanes[ 0 ].mNormal.y = cm[ 7 ] + cm[ 4 ];
401 planes.mPlanes[ 0 ].mNormal.z = cm[ 11 ] + cm[ 8 ];
402 planes.mPlanes[ 0 ].mDistance = cm[ 15 ] + cm[ 12 ];
405 planes.mPlanes[ 1 ].mNormal.x = cm[ 3 ] - cm[ 0 ]; // column 4 - column 1
406 planes.mPlanes[ 1 ].mNormal.y = cm[ 7 ] - cm[ 4 ];
407 planes.mPlanes[ 1 ].mNormal.z = cm[ 11 ] - cm[ 8 ];
408 planes.mPlanes[ 1 ].mDistance = cm[ 15 ] - cm[ 12 ];
411 planes.mPlanes[ 2 ].mNormal.x = cm[ 3 ] + cm[ 1 ]; // column 4 + column 2
412 planes.mPlanes[ 2 ].mNormal.y = cm[ 7 ] + cm[ 5 ];
413 planes.mPlanes[ 2 ].mNormal.z = cm[ 11 ] + cm[ 9 ];
414 planes.mPlanes[ 2 ].mDistance = cm[ 15 ] + cm[ 13 ];
417 planes.mPlanes[ 3 ].mNormal.x = cm[ 3 ] - cm[ 1 ]; // column 4 - column 2
418 planes.mPlanes[ 3 ].mNormal.y = cm[ 7 ] - cm[ 5 ];
419 planes.mPlanes[ 3 ].mNormal.z = cm[ 11 ] - cm[ 9 ];
420 planes.mPlanes[ 3 ].mDistance = cm[ 15 ] - cm[ 13 ];
423 planes.mPlanes[ 4 ].mNormal.x = cm[ 3 ] + cm[ 2 ]; // column 4 + column 3
424 planes.mPlanes[ 4 ].mNormal.y = cm[ 7 ] + cm[ 6 ];
425 planes.mPlanes[ 4 ].mNormal.z = cm[ 11 ] + cm[ 10 ];
426 planes.mPlanes[ 4 ].mDistance = cm[ 15 ] + cm[ 14 ];
429 planes.mPlanes[ 5 ].mNormal.x = cm[ 3 ] - cm[ 2 ]; // column 4 - column 3
430 planes.mPlanes[ 5 ].mNormal.y = cm[ 7 ] - cm[ 6 ];
431 planes.mPlanes[ 5 ].mNormal.z = cm[ 11 ] - cm[ 10 ];
432 planes.mPlanes[ 5 ].mDistance = cm[ 15 ] - cm[ 14 ];
436 for ( uint32_t i = 0; i < 6; ++i )
438 // Normalize planes to ensure correct bounding distance checking
439 Plane& plane = planes.mPlanes[ i ];
440 float l = 1.0f / plane.mNormal.Length();
442 plane.mDistance *= l;
444 planes.mSign[i] = Vector3( Sign(plane.mNormal.x), Sign(plane.mNormal.y), Sign(plane.mNormal.z) );
449 for ( uint32_t i = 0; i < 6; ++i )
451 planes.mSign[i] = Vector3( Sign(planes.mPlanes[ i ].mNormal.x), Sign(planes.mPlanes[ i ].mNormal.y), Sign(planes.mPlanes[ i ].mNormal.z) );
454 mFrustum[ updateBufferIndex ? 0 : 1 ] = planes;
457 bool Camera::CheckSphereInFrustum( BufferIndex bufferIndex, const Vector3& origin, float radius )
459 const FrustumPlanes& planes = mFrustum[ bufferIndex ];
460 for ( uint32_t i = 0; i < 6; ++i )
462 if ( ( planes.mPlanes[ i ].mDistance + planes.mPlanes[ i ].mNormal.Dot( origin ) ) < -radius )
470 bool Camera::CheckAABBInFrustum( BufferIndex bufferIndex, const Vector3& origin, const Vector3& halfExtents )
472 const FrustumPlanes& planes = mFrustum[ bufferIndex ];
473 for ( uint32_t i = 0; i < 6; ++i )
475 if( planes.mPlanes[ i ].mNormal.Dot( origin + (halfExtents * planes.mSign[i]) ) > -(planes.mPlanes[ i ].mDistance) )
485 uint32_t Camera::UpdateProjection( BufferIndex updateBufferIndex )
487 uint32_t retval( mUpdateProjectionFlag );
488 // Early-exit if no update required
489 if ( 0u != mUpdateProjectionFlag )
491 if ( COPY_PREVIOUS_MATRIX == mUpdateProjectionFlag )
493 // The projection matrix was updated in the previous frame; copy it
494 mProjectionMatrix.CopyPrevious( updateBufferIndex );
496 else // UPDATE_COUNT == mUpdateProjectionFlag
498 switch( mProjectionMode )
500 case Dali::Camera::PERSPECTIVE_PROJECTION:
502 Matrix &projectionMatrix = mProjectionMatrix.Get( updateBufferIndex );
503 Perspective( projectionMatrix,
511 case Dali::Camera::ORTHOGRAPHIC_PROJECTION:
513 Matrix &projectionMatrix = mProjectionMatrix.Get( updateBufferIndex );
514 Orthographic( projectionMatrix,
515 mLeftClippingPlane, mRightClippingPlane,
516 mBottomClippingPlane, mTopClippingPlane,
517 mNearClippingPlane, mFarClippingPlane,
523 mProjectionMatrix.SetDirty( updateBufferIndex );
525 Matrix &finalProjection = mFinalProjection[ updateBufferIndex ];
526 finalProjection.SetIdentity();
528 Quaternion rotationAngle;
529 switch( mProjectionRotation )
533 rotationAngle = Quaternion( Dali::ANGLE_90, Vector3::ZAXIS );
538 rotationAngle = Quaternion( Dali::ANGLE_180, Vector3::ZAXIS );
543 rotationAngle = Quaternion( Dali::ANGLE_270, Vector3::ZAXIS );
547 rotationAngle = Quaternion( Dali::ANGLE_0, Vector3::ZAXIS );
552 rotation.SetIdentity();
553 rotation.SetTransformComponents( Vector3( 1.0f, 1.0f, 1.0f ), rotationAngle, Vector3( 0.0f, 0.0f, 0.0f ) );
555 Matrix::Multiply( finalProjection, mProjectionMatrix.Get( updateBufferIndex ), rotation );
557 --mUpdateProjectionFlag;
562 } // namespace SceneGraph
564 } // namespace Internal