2 * Copyright (c) 2014 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/node-attachments/scene-graph-camera-attachment.h>
22 #include <dali/public-api/common/dali-common.h>
23 #include <dali/internal/update/nodes/node.h>
24 #include <dali/internal/update/controllers/scene-controller.h>
25 #include <dali/internal/update/resources/resource-manager.h>
26 #include <dali/integration-api/debug.h>
28 namespace // unnamed namespace
30 const unsigned int UPDATE_COUNT = 2u; // Update projection or view matrix this many frames after a change
31 const unsigned int COPY_PREVIOUS_MATRIX = 1u; // Copy view or projection matrix from previous frame
46 void LookAt(Matrix& result, const Vector3& eye, const Vector3& target, const Vector3& up)
48 Vector3 vZ = target - eye;
51 Vector3 vX = up.Cross(vZ);
54 Vector3 vY = vZ.Cross(vX);
57 result.SetInverseTransformComponents(vX, vY, vZ, eye);
61 void Frustum(Matrix& result, float left, float right, float bottom, float top, float near, float far, bool invertYAxis)
63 float deltaZ = far - near;
64 if ((near <= 0.0f) || (far <= 0.0f) || Equals(right, left) || Equals(bottom, top) || (deltaZ <= 0.0f))
66 DALI_LOG_ERROR("Invalid parameters passed into Frustum!");
67 DALI_ASSERT_DEBUG("Invalid parameters passed into Frustum!");
71 float deltaX = right - left;
72 float deltaY = invertYAxis ? bottom - top : top - bottom;
76 float* m = result.AsFloat();
77 m[0] = -2.0f * near / deltaX;
78 m[1] = m[2] = m[3] = 0.0f;
80 m[5] = -2.0f * near / deltaY;
81 m[4] = m[6] = m[7] = 0.0f;
83 m[8] = (right + left) / deltaX;
84 m[9] = (top + bottom) / deltaY;
85 m[10] = (near + far) / deltaZ;
88 m[14] = -2.0f * near * far / deltaZ;
89 m[12] = m[13] = m[15] = 0.0f;
92 void Perspective(Matrix& result, float fovy, float aspect, float near, float far, bool invertYAxis, const Vector2& stereoBias )
94 float frustumH = tanf( fovy * 0.5f ) * near;
95 float frustumW = frustumH * aspect;
96 Vector2 bias = stereoBias * 0.5f;
98 Frustum(result, -(frustumW + bias.x), frustumW - bias.x, -(frustumH + bias.y), frustumH - bias.y, near, far, invertYAxis);
101 void Orthographic(Matrix& result, float left, float right, float bottom, float top, float near, float far, bool invertYAxis)
103 if ( Equals(right, left) || Equals(top, bottom) || Equals(far, near) )
105 DALI_LOG_ERROR( "Cannot create orthographic projection matrix with a zero dimension." );
106 DALI_ASSERT_DEBUG( "Cannot create orthographic projection matrix with a zero dimension." );
110 float deltaX = right - left;
111 float deltaY = invertYAxis ? bottom - top : top - bottom;
112 float deltaZ = far - near;
114 float *m = result.AsFloat();
115 m[0] = -2.0f / deltaX;
121 m[5] = -2.0f / deltaY;
127 m[10] = 2.0f / deltaZ;
129 m[12] = -(right + left) / deltaX;
130 m[13] = -(top + bottom) / deltaY;
131 m[14] = -(near + far) / deltaZ;
135 } // unnamed namespace
137 const Dali::Camera::Type CameraAttachment::DEFAULT_TYPE( Dali::Camera::FREE_LOOK );
138 const Dali::Camera::ProjectionMode CameraAttachment::DEFAULT_MODE( Dali::Camera::PERSPECTIVE_PROJECTION );
139 const bool CameraAttachment::DEFAULT_INVERT_Y_AXIS( false );
140 const float CameraAttachment::DEFAULT_FIELD_OF_VIEW( 45.0f*(M_PI/180.0f) );
141 const float CameraAttachment::DEFAULT_ASPECT_RATIO( 4.0f/3.0f );
142 const float CameraAttachment::DEFAULT_LEFT_CLIPPING_PLANE(-240.0f);
143 const float CameraAttachment::DEFAULT_RIGHT_CLIPPING_PLANE(240.0f);
144 const float CameraAttachment::DEFAULT_TOP_CLIPPING_PLANE(-400.0f);
145 const float CameraAttachment::DEFAULT_BOTTOM_CLIPPING_PLANE(400.0f);
146 const float CameraAttachment::DEFAULT_NEAR_CLIPPING_PLANE( 800.0f ); // default height of the screen
147 const float CameraAttachment::DEFAULT_FAR_CLIPPING_PLANE( DEFAULT_NEAR_CLIPPING_PLANE + 2.f * DEFAULT_NEAR_CLIPPING_PLANE );
148 const Vector2 CameraAttachment::DEFAULT_STEREO_BIAS( 0.0f, 0.0f );
149 const Vector3 CameraAttachment::DEFAULT_TARGET_POSITION( 0.0f, 0.0f, 0.0f );
152 CameraAttachment::CameraAttachment()
154 mUpdateViewFlag( UPDATE_COUNT ),
155 mUpdateProjectionFlag( UPDATE_COUNT ),
156 mType( DEFAULT_TYPE ),
157 mProjectionMode( DEFAULT_MODE ),
158 mInvertYAxis( DEFAULT_INVERT_Y_AXIS ),
159 mFieldOfView( DEFAULT_FIELD_OF_VIEW ),
160 mAspectRatio( DEFAULT_ASPECT_RATIO ),
161 mLeftClippingPlane( DEFAULT_LEFT_CLIPPING_PLANE ),
162 mRightClippingPlane( DEFAULT_RIGHT_CLIPPING_PLANE ),
163 mTopClippingPlane( DEFAULT_TOP_CLIPPING_PLANE ),
164 mBottomClippingPlane( DEFAULT_BOTTOM_CLIPPING_PLANE ),
165 mNearClippingPlane( DEFAULT_NEAR_CLIPPING_PLANE ),
166 mFarClippingPlane( DEFAULT_FAR_CLIPPING_PLANE ),
167 mStereoBias( DEFAULT_STEREO_BIAS ),
168 mTargetPosition( DEFAULT_TARGET_POSITION ),
171 mInverseViewProjection( Matrix::IDENTITY )
175 CameraAttachment* CameraAttachment::New()
177 return new CameraAttachment();
180 void CameraAttachment::Initialize( SceneController& sceneController, BufferIndex updateBufferIndex )
185 void CameraAttachment::OnDestroy()
190 void CameraAttachment::ConnectedToSceneGraph()
195 void CameraAttachment::DisconnectedFromSceneGraph()
200 CameraAttachment::~CameraAttachment()
204 RenderableAttachment* CameraAttachment::GetRenderable()
209 void CameraAttachment::SetType( Dali::Camera::Type type )
214 void CameraAttachment::SetProjectionMode( Dali::Camera::ProjectionMode mode )
216 mProjectionMode = mode;
217 mUpdateProjectionFlag = UPDATE_COUNT;
220 void CameraAttachment::SetInvertYAxis( bool invertYAxis )
222 mInvertYAxis = invertYAxis;
223 mUpdateProjectionFlag = UPDATE_COUNT;
226 void CameraAttachment::SetFieldOfView( float fieldOfView )
228 mFieldOfView = fieldOfView;
229 mUpdateProjectionFlag = UPDATE_COUNT;
232 void CameraAttachment::SetAspectRatio( float aspectRatio )
234 mAspectRatio = aspectRatio;
235 mUpdateProjectionFlag = UPDATE_COUNT;
238 void CameraAttachment::SetStereoBias( const Vector2& stereoBias )
240 mStereoBias = stereoBias;
241 mUpdateProjectionFlag = UPDATE_COUNT;
244 void CameraAttachment::SetLeftClippingPlane( float leftClippingPlane )
246 mLeftClippingPlane = leftClippingPlane;
247 mUpdateProjectionFlag = UPDATE_COUNT;
250 void CameraAttachment::SetRightClippingPlane( float rightClippingPlane )
252 mRightClippingPlane = rightClippingPlane;
253 mUpdateProjectionFlag = UPDATE_COUNT;
256 void CameraAttachment::SetTopClippingPlane( float topClippingPlane )
258 mTopClippingPlane = topClippingPlane;
259 mUpdateProjectionFlag = UPDATE_COUNT;
262 void CameraAttachment::SetBottomClippingPlane( float bottomClippingPlane )
264 mBottomClippingPlane = bottomClippingPlane;
265 mUpdateProjectionFlag = UPDATE_COUNT;
268 void CameraAttachment::SetNearClippingPlane( float nearClippingPlane )
270 mNearClippingPlane = nearClippingPlane;
271 mUpdateProjectionFlag = UPDATE_COUNT;
274 void CameraAttachment::SetFarClippingPlane( float farClippingPlane )
276 mFarClippingPlane = farClippingPlane;
277 mUpdateProjectionFlag = UPDATE_COUNT;
280 void CameraAttachment::SetTargetPosition( const Vector3& targetPosition )
282 mTargetPosition = targetPosition;
283 mUpdateViewFlag = UPDATE_COUNT;
286 const Matrix& CameraAttachment::GetProjectionMatrix( BufferIndex bufferIndex ) const
288 return mProjectionMatrix[ bufferIndex ];
291 const Matrix& CameraAttachment::GetViewMatrix( BufferIndex bufferIndex ) const
293 return mViewMatrix[ bufferIndex ];
296 const Matrix& CameraAttachment::GetInverseViewProjectionMatrix( BufferIndex bufferIndex ) const
298 return mInverseViewProjection[ bufferIndex ];
301 const PropertyInputImpl* CameraAttachment::GetProjectionMatrix() const
303 return &mProjectionMatrix;
306 const PropertyInputImpl* CameraAttachment::GetViewMatrix() const
311 void CameraAttachment::Update( BufferIndex updateBufferIndex, const Node& owningNode, int nodeDirtyFlags )
313 // if owning node has changes in world position we need to update camera for next 2 frames
314 if( nodeDirtyFlags & TransformFlag )
316 mUpdateViewFlag = UPDATE_COUNT;
318 if( nodeDirtyFlags & VisibleFlag )
320 // If the visibility changes, the projection matrix needs to be re-calculated.
321 // It may happen the first time an actor is rendered it's rendered only once and becomes invisible,
322 // in the following update the node will be skipped leaving the projection matrix (double buffered)
323 // with the Identity.
324 mUpdateProjectionFlag = UPDATE_COUNT;
327 // if either matrix changed, we need to recalculate the inverse matrix for hit testing to work
328 unsigned int viewUpdateCount = UpdateViewMatrix( updateBufferIndex, owningNode );
329 unsigned int projectionUpdateCount = UpdateProjection( updateBufferIndex );
331 // if model or view matrix changed we need to either recalculate the inverse VP or copy previous
332 if( viewUpdateCount > COPY_PREVIOUS_MATRIX || projectionUpdateCount > COPY_PREVIOUS_MATRIX )
334 // either has actually changed so recalculate
335 Matrix::Multiply( mInverseViewProjection[ updateBufferIndex ], mViewMatrix[ updateBufferIndex ], mProjectionMatrix[ updateBufferIndex ] );
336 UpdateFrustum( updateBufferIndex );
338 // ignore the error, if the view projection is incorrect (non inversible) then you will have tough times anyways
339 static_cast< void >( mInverseViewProjection[ updateBufferIndex ].Invert() );
341 else if( viewUpdateCount == COPY_PREVIOUS_MATRIX || projectionUpdateCount == COPY_PREVIOUS_MATRIX )
343 // neither has actually changed, but we might copied previous frames value so need to
344 // copy the previous inverse and frustum as well
345 mInverseViewProjection[updateBufferIndex] = mInverseViewProjection[updateBufferIndex ? 0 : 1];
346 mFrustum[ updateBufferIndex ] = mFrustum[ updateBufferIndex ? 0 : 1 ];
350 bool CameraAttachment::ViewMatrixUpdated()
352 return 0u != mUpdateViewFlag;
355 unsigned int CameraAttachment::UpdateViewMatrix( BufferIndex updateBufferIndex, const Node& owningNode )
357 unsigned int retval( mUpdateViewFlag );
358 if( 0u != mUpdateViewFlag )
360 if( COPY_PREVIOUS_MATRIX == mUpdateViewFlag )
362 // The projection matrix was updated in the previous frame; copy it
363 mViewMatrix.CopyPrevious( updateBufferIndex );
365 else // UPDATE_COUNT == mUpdateViewFlag
369 // camera orientation taken from node - i.e. look in abitrary, unconstrained direction
370 case Dali::Camera::FREE_LOOK:
372 Matrix& viewMatrix = mViewMatrix.Get( updateBufferIndex );
373 viewMatrix.SetInverseTransformComponents( Vector3::ONE, owningNode.GetWorldOrientation( updateBufferIndex ),
374 owningNode.GetWorldPosition( updateBufferIndex ) );
375 mViewMatrix.SetDirty( updateBufferIndex );
378 // camera orientation constrained to look at a target
379 case Dali::Camera::LOOK_AT_TARGET:
381 Matrix& viewMatrix = mViewMatrix.Get( updateBufferIndex );
382 LookAt( viewMatrix, owningNode.GetWorldPosition( updateBufferIndex ), mTargetPosition,
383 owningNode.GetWorldOrientation( updateBufferIndex ).Rotate( Vector3::YAXIS ) );
384 mViewMatrix.SetDirty( updateBufferIndex );
394 void CameraAttachment::UpdateFrustum( BufferIndex updateBufferIndex, bool normalize )
397 // Extract the clip matrix planes
399 Matrix::Multiply( clipMatrix, mViewMatrix[ updateBufferIndex ], mProjectionMatrix[ updateBufferIndex ] );
401 const float* cm = clipMatrix.AsFloat();
402 FrustumPlanes& planes = mFrustum[ updateBufferIndex ];
405 planes.mPlanes[ 0 ].mNormal.x = cm[ 3 ] + cm[ 0 ]; // column 4 + column 1
406 planes.mPlanes[ 0 ].mNormal.y = cm[ 7 ] + cm[ 4 ];
407 planes.mPlanes[ 0 ].mNormal.z = cm[ 11 ] + cm[ 8 ];
408 planes.mPlanes[ 0 ].mDistance = cm[ 15 ] + cm[ 12 ];
411 planes.mPlanes[ 1 ].mNormal.x = cm[ 3 ] - cm[ 0 ]; // column 4 - column 1
412 planes.mPlanes[ 1 ].mNormal.y = cm[ 7 ] - cm[ 4 ];
413 planes.mPlanes[ 1 ].mNormal.z = cm[ 11 ] - cm[ 8 ];
414 planes.mPlanes[ 1 ].mDistance = cm[ 15 ] - cm[ 12 ];
417 planes.mPlanes[ 2 ].mNormal.x = cm[ 3 ] + cm[ 1 ]; // column 4 + column 2
418 planes.mPlanes[ 2 ].mNormal.y = cm[ 7 ] + cm[ 5 ];
419 planes.mPlanes[ 2 ].mNormal.z = cm[ 11 ] + cm[ 9 ];
420 planes.mPlanes[ 2 ].mDistance = cm[ 15 ] + cm[ 13 ];
423 planes.mPlanes[ 3 ].mNormal.x = cm[ 3 ] - cm[ 1 ]; // column 4 - column 2
424 planes.mPlanes[ 3 ].mNormal.y = cm[ 7 ] - cm[ 5 ];
425 planes.mPlanes[ 3 ].mNormal.z = cm[ 11 ] - cm[ 9 ];
426 planes.mPlanes[ 3 ].mDistance = cm[ 15 ] - cm[ 13 ];
429 planes.mPlanes[ 4 ].mNormal.x = cm[ 3 ] + cm[ 2 ]; // column 4 + column 3
430 planes.mPlanes[ 4 ].mNormal.y = cm[ 7 ] + cm[ 6 ];
431 planes.mPlanes[ 4 ].mNormal.z = cm[ 11 ] + cm[ 10 ];
432 planes.mPlanes[ 4 ].mDistance = cm[ 15 ] + cm[ 14 ];
435 planes.mPlanes[ 5 ].mNormal.x = cm[ 3 ] - cm[ 2 ]; // column 4 - column 3
436 planes.mPlanes[ 5 ].mNormal.y = cm[ 7 ] - cm[ 6 ];
437 planes.mPlanes[ 5 ].mNormal.z = cm[ 11 ] - cm[ 10 ];
438 planes.mPlanes[ 5 ].mDistance = cm[ 15 ] - cm[ 14 ];
442 for ( unsigned int i = 0; i < 6; ++i )
444 // Normalize planes to ensure correct bounding distance checking
445 Plane& plane = planes.mPlanes[ i ];
446 float l = 1.0f / plane.mNormal.Length();
448 plane.mDistance *= l;
451 mFrustum[ updateBufferIndex ? 0 : 1 ] = planes;
454 bool CameraAttachment::CheckSphereInFrustum( BufferIndex bufferIndex, const Vector3& origin, float radius )
456 const FrustumPlanes& planes = mFrustum[ bufferIndex ];
457 for ( uint32_t i = 0; i < 6; ++i )
459 if ( ( planes.mPlanes[ i ].mDistance + planes.mPlanes[ i ].mNormal.Dot( origin ) ) < -radius )
467 bool CameraAttachment::CheckAABBInFrustum( BufferIndex bufferIndex, const Vector3& origin, const Vector3& halfExtents )
469 const FrustumPlanes& planes = mFrustum[ bufferIndex ];
470 Vector3 tln = origin + Vector3( -halfExtents );
471 Vector3 trn = origin + Vector3( halfExtents.x, -halfExtents.y, -halfExtents.z );
472 Vector3 bln = origin + Vector3( -halfExtents.x, halfExtents.y, -halfExtents.z );
473 Vector3 brn = origin + Vector3( halfExtents.x, halfExtents.y, -halfExtents.z );
474 Vector3 tlf = origin + Vector3( -halfExtents.x, -halfExtents.y, halfExtents.z );
475 Vector3 trf = origin + Vector3( halfExtents.x, -halfExtents.y, halfExtents.z );
476 Vector3 blf = origin + Vector3( -halfExtents.x, halfExtents.y, halfExtents.z );
477 Vector3 brf = origin + Vector3( halfExtents.x, halfExtents.y, halfExtents.z );
479 for ( uint32_t i = 0; i < 6; ++i )
481 if ( planes.mPlanes[ i ].mDistance + planes.mPlanes[ i ].mNormal.Dot( tln ) >= 0 )
486 if ( planes.mPlanes[ i ].mDistance + planes.mPlanes[ i ].mNormal.Dot( trn ) >= 0 )
491 if ( planes.mPlanes[ i ].mDistance + planes.mPlanes[ i ].mNormal.Dot( bln ) >= 0 )
496 if ( planes.mPlanes[ i ].mDistance + planes.mPlanes[ i ].mNormal.Dot( brn ) >= 0 )
501 if ( planes.mPlanes[ i ].mDistance + planes.mPlanes[ i ].mNormal.Dot( tlf ) >= 0 )
506 if ( planes.mPlanes[ i ].mDistance + planes.mPlanes[ i ].mNormal.Dot( trf ) >= 0 )
511 if ( planes.mPlanes[ i ].mDistance + planes.mPlanes[ i ].mNormal.Dot( blf ) >= 0 )
516 if ( planes.mPlanes[ i ].mDistance + planes.mPlanes[ i ].mNormal.Dot( brf ) >= 0 )
525 unsigned int CameraAttachment::UpdateProjection( BufferIndex updateBufferIndex )
527 unsigned int retval( mUpdateProjectionFlag );
528 // Early-exit if no update required
529 if ( 0u != mUpdateProjectionFlag )
531 if ( COPY_PREVIOUS_MATRIX == mUpdateProjectionFlag )
533 // The projection matrix was updated in the previous frame; copy it
534 mProjectionMatrix.CopyPrevious( updateBufferIndex );
536 else // UPDATE_COUNT == mUpdateProjectionFlag
538 switch( mProjectionMode )
540 case Dali::Camera::PERSPECTIVE_PROJECTION:
542 Matrix &projectionMatrix = mProjectionMatrix.Get(updateBufferIndex);
543 Perspective( projectionMatrix,
552 case Dali::Camera::ORTHOGRAPHIC_PROJECTION:
554 Matrix &projectionMatrix = mProjectionMatrix.Get(updateBufferIndex);
555 Orthographic( projectionMatrix,
556 mLeftClippingPlane, mRightClippingPlane,
557 mBottomClippingPlane, mTopClippingPlane,
558 mNearClippingPlane, mFarClippingPlane,
564 mProjectionMatrix.SetDirty(updateBufferIndex);
566 --mUpdateProjectionFlag;
571 } // namespace SceneGraph
573 } // namespace Internal