7aca9cbe66e19b31ca03e167fa37555897ae67aa
[platform/core/uifw/dali-core.git] / dali / internal / update / render-tasks / scene-graph-camera.cpp
1 /*
2  * Copyright (c) 2018 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/update/render-tasks/scene-graph-camera.h>
20
21 // EXTERNAL INCLUDES
22 #include <stdint.h>
23
24 // INTERNAL INCLUDES
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>
29
30 namespace // unnamed namespace
31 {
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
34 }
35
36 namespace Dali
37 {
38
39 namespace Internal
40 {
41
42 namespace SceneGraph
43 {
44
45 namespace
46 {
47
48 template< typename T >
49 T Sign( T value )
50 {
51   return T( T(0) < value ) - T( value < T(0) );
52 }
53
54 void LookAt(Matrix& result, const Vector3& eye, const Vector3& target, const Vector3& up)
55 {
56   Vector3 vZ = target - eye;
57   vZ.Normalize();
58
59   Vector3 vX = up.Cross(vZ);
60   vX.Normalize();
61
62   Vector3 vY = vZ.Cross(vX);
63   vY.Normalize();
64
65   result.SetInverseTransformComponents(vX, vY, vZ, eye);
66 }
67
68 void Frustum(Matrix& result, float left, float right, float bottom, float top, float near, float far, bool invertYAxis)
69 {
70   float deltaZ = far - near;
71   if ((near <= 0.0f) || (far <= 0.0f) || Equals(right, left) || Equals(bottom, top) || (deltaZ <= 0.0f))
72   {
73     DALI_LOG_ERROR("Invalid parameters passed into Frustum!\n");
74     DALI_ASSERT_DEBUG("Invalid parameters passed into Frustum!");
75     return;
76   }
77
78   float deltaX = right - left;
79   float deltaY = invertYAxis ? bottom - top : top - bottom;
80
81   result.SetIdentity();
82
83   float* m = result.AsFloat();
84   m[0] = -2.0f * near / deltaX;
85   m[1] = m[2] = m[3] = 0.0f;
86
87   m[5] = -2.0f * near / deltaY;
88   m[4] = m[6] = m[7] = 0.0f;
89
90   m[8] = (right + left) / deltaX;
91   m[9] = (top + bottom) / deltaY;
92   m[10] = (near + far) / deltaZ;
93   m[11] = 1.0f;
94
95   m[14] = -2.0f * near * far / deltaZ;
96   m[12] = m[13] = m[15] = 0.0f;
97 }
98
99 void Perspective(Matrix& result, float fovy, float aspect, float near, float far, bool invertYAxis )
100 {
101   float frustumH = tanf( fovy * 0.5f ) * near;
102   float frustumW = frustumH * aspect;
103
104   Frustum(result, -frustumW, frustumW, -frustumH, frustumH, near, far, invertYAxis);
105 }
106
107 void Orthographic(Matrix& result, float left, float right, float bottom, float top, float near, float far, bool invertYAxis)
108 {
109   if ( Equals(right, left) || Equals(top, bottom) || Equals(far, near) )
110   {
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." );
113     return;
114   }
115
116   float deltaX = right - left;
117   float deltaY = invertYAxis ? bottom - top : top - bottom;
118   float deltaZ = far - near;
119
120   float *m = result.AsFloat();
121   m[0] = -2.0f / deltaX;
122   m[1] = 0.0f;
123   m[2] = 0.0f;
124   m[3] = 0.0f;
125
126   m[4] = 0.0f;
127   m[5] = -2.0f / deltaY;
128   m[6] = 0.0f;
129   m[7] = 0.0f;
130
131   m[8] = 0.0f;
132   m[9] = 0.0f;
133   m[10] = 2.0f / deltaZ;
134   m[11] = 0.0f;
135   m[12] = -(right + left) / deltaX;
136   m[13] = -(top + bottom) / deltaY;
137   m[14] = -(near + far)   / deltaZ;
138   m[15] = 1.0f;
139 }
140
141 } // unnamed namespace
142
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 );
155
156
157 Camera::Camera()
158 : mUpdateViewFlag( UPDATE_COUNT ),
159   mUpdateProjectionFlag( UPDATE_COUNT ),
160   mProjectionRotation( 0 ),
161   mNode( NULL ),
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 ),
174   mViewMatrix(),
175   mProjectionMatrix(),
176   mInverseViewProjection( Matrix::IDENTITY ),
177   mFinalProjection( Matrix::IDENTITY )
178 {
179 }
180
181 Camera* Camera::New()
182 {
183   return new Camera();
184 }
185
186 Camera::~Camera()
187 {
188 }
189
190 void Camera::SetNode( const Node* node )
191 {
192   mNode = node;
193 }
194
195 void Camera::SetType( Dali::Camera::Type type )
196 {
197   mType = type;
198 }
199
200 void Camera::SetProjectionMode( Dali::Camera::ProjectionMode mode )
201 {
202   mProjectionMode = mode;
203   mUpdateProjectionFlag = UPDATE_COUNT;
204 }
205
206 void Camera::SetInvertYAxis( bool invertYAxis )
207 {
208   mInvertYAxis = invertYAxis;
209   mUpdateProjectionFlag = UPDATE_COUNT;
210 }
211
212 void Camera::SetFieldOfView( float fieldOfView )
213 {
214   mFieldOfView = fieldOfView;
215   mUpdateProjectionFlag = UPDATE_COUNT;
216 }
217
218 void Camera::SetAspectRatio( float aspectRatio )
219 {
220   mAspectRatio = aspectRatio;
221   mUpdateProjectionFlag = UPDATE_COUNT;
222 }
223
224 void Camera::SetLeftClippingPlane( float leftClippingPlane )
225 {
226   mLeftClippingPlane = leftClippingPlane;
227   mUpdateProjectionFlag = UPDATE_COUNT;
228 }
229
230 void Camera::SetRightClippingPlane( float rightClippingPlane )
231 {
232   mRightClippingPlane = rightClippingPlane;
233   mUpdateProjectionFlag = UPDATE_COUNT;
234 }
235
236 void Camera::SetTopClippingPlane( float topClippingPlane )
237 {
238   mTopClippingPlane = topClippingPlane;
239   mUpdateProjectionFlag = UPDATE_COUNT;
240 }
241
242 void Camera::SetBottomClippingPlane( float bottomClippingPlane )
243 {
244   mBottomClippingPlane = bottomClippingPlane;
245   mUpdateProjectionFlag = UPDATE_COUNT;
246 }
247
248 void Camera::SetNearClippingPlane( float nearClippingPlane )
249 {
250   mNearClippingPlane = nearClippingPlane;
251   mUpdateProjectionFlag = UPDATE_COUNT;
252 }
253
254 void Camera::SetFarClippingPlane( float farClippingPlane )
255 {
256   mFarClippingPlane = farClippingPlane;
257   mUpdateProjectionFlag = UPDATE_COUNT;
258 }
259
260 void Camera::SetTargetPosition( const Vector3& targetPosition )
261 {
262   mTargetPosition = targetPosition;
263   mUpdateViewFlag = UPDATE_COUNT;
264 }
265
266 void Camera::RotateProjection( int rotationAngle )
267 {
268   mProjectionRotation = rotationAngle;
269   mUpdateViewFlag = UPDATE_COUNT;
270 }
271
272 const Matrix& Camera::GetProjectionMatrix( BufferIndex bufferIndex ) const
273 {
274   return mProjectionMatrix[ bufferIndex ];
275 }
276
277 const Matrix& Camera::GetViewMatrix( BufferIndex bufferIndex ) const
278 {
279   return mViewMatrix[ bufferIndex ];
280 }
281
282 const Matrix& Camera::GetInverseViewProjectionMatrix( BufferIndex bufferIndex ) const
283 {
284   return mInverseViewProjection[ bufferIndex ];
285 }
286
287 const Matrix& Camera::GetFinalProjectionMatrix( BufferIndex bufferIndex ) const
288 {
289   return mFinalProjection[ bufferIndex ];
290 }
291
292 const PropertyInputImpl* Camera::GetProjectionMatrix() const
293 {
294   return &mProjectionMatrix;
295 }
296
297 const PropertyInputImpl* Camera::GetViewMatrix() const
298 {
299   return &mViewMatrix;
300 }
301
302 void Camera::Update( BufferIndex updateBufferIndex )
303 {
304   // if owning node has changes in world position we need to update camera for next 2 frames
305   if( mNode->IsLocalMatrixDirty() )
306   {
307     mUpdateViewFlag = UPDATE_COUNT;
308   }
309   if( mNode->GetDirtyFlags() & NodePropertyFlags::VISIBLE )
310   {
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;
316   }
317
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 );
321
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 )
324   {
325     // either has actually changed so recalculate
326     Matrix::Multiply( mInverseViewProjection[ updateBufferIndex ], mViewMatrix[ updateBufferIndex ], mProjectionMatrix[ updateBufferIndex ] );
327     UpdateFrustum( updateBufferIndex );
328
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() );
331   }
332   else if( viewUpdateCount == COPY_PREVIOUS_MATRIX || projectionUpdateCount == COPY_PREVIOUS_MATRIX )
333   {
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 ];
338   }
339 }
340
341 bool Camera::ViewMatrixUpdated()
342 {
343   return 0u != mUpdateViewFlag;
344 }
345
346 uint32_t Camera::UpdateViewMatrix( BufferIndex updateBufferIndex )
347 {
348   uint32_t retval( mUpdateViewFlag );
349   if( 0u != mUpdateViewFlag )
350   {
351     if( COPY_PREVIOUS_MATRIX == mUpdateViewFlag )
352     {
353       // The projection matrix was updated in the previous frame; copy it
354       mViewMatrix.CopyPrevious( updateBufferIndex );
355     }
356     else // UPDATE_COUNT == mUpdateViewFlag
357     {
358       switch( mType )
359       {
360         // camera orientation taken from node - i.e. look in abitrary, unconstrained direction
361         case Dali::Camera::FREE_LOOK:
362         {
363           Matrix& viewMatrix = mViewMatrix.Get( updateBufferIndex );
364           viewMatrix = mNode->GetWorldMatrix( updateBufferIndex );
365           viewMatrix.Invert();
366           mViewMatrix.SetDirty( updateBufferIndex );
367           break;
368         }
369           // camera orientation constrained to look at a target
370         case Dali::Camera::LOOK_AT_TARGET:
371         {
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 );
379           break;
380         }
381       }
382     }
383     --mUpdateViewFlag;
384   }
385   return retval;
386 }
387
388 void Camera::UpdateFrustum( BufferIndex updateBufferIndex, bool normalize )
389 {
390
391   // Extract the clip matrix planes
392   Matrix clipMatrix;
393   Matrix::Multiply( clipMatrix, mViewMatrix[ updateBufferIndex ], mProjectionMatrix[ updateBufferIndex ] );
394
395   const float* cm = clipMatrix.AsFloat();
396   FrustumPlanes& planes = mFrustum[ updateBufferIndex ];
397
398   // Left
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 ];
403
404   // Right
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 ];
409
410   // Bottom
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 ];
415
416   // Top
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 ];
421
422   // Near
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 ];
427
428   // Far
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 ];
433
434   if ( normalize )
435   {
436     for ( uint32_t i = 0; i < 6; ++i )
437     {
438       // Normalize planes to ensure correct bounding distance checking
439       Plane& plane = planes.mPlanes[ i ];
440       float l = 1.0f / plane.mNormal.Length();
441       plane.mNormal *= l;
442       plane.mDistance *= l;
443
444       planes.mSign[i] = Vector3( Sign(plane.mNormal.x), Sign(plane.mNormal.y), Sign(plane.mNormal.z) );
445     }
446   }
447   else
448   {
449     for ( uint32_t i = 0; i < 6; ++i )
450     {
451       planes.mSign[i] = Vector3( Sign(planes.mPlanes[ i ].mNormal.x), Sign(planes.mPlanes[ i ].mNormal.y), Sign(planes.mPlanes[ i ].mNormal.z) );
452     }
453   }
454   mFrustum[ updateBufferIndex ? 0 : 1 ] = planes;
455 }
456
457 bool Camera::CheckSphereInFrustum( BufferIndex bufferIndex, const Vector3& origin, float radius )
458 {
459   const FrustumPlanes& planes = mFrustum[ bufferIndex ];
460   for ( uint32_t i = 0; i < 6; ++i )
461   {
462     if ( ( planes.mPlanes[ i ].mDistance + planes.mPlanes[ i ].mNormal.Dot( origin ) ) < -radius )
463     {
464       return false;
465     }
466   }
467   return true;
468 }
469
470 bool Camera::CheckAABBInFrustum( BufferIndex bufferIndex, const Vector3& origin, const Vector3& halfExtents )
471 {
472   const FrustumPlanes& planes = mFrustum[ bufferIndex ];
473   for ( uint32_t i = 0; i < 6; ++i )
474   {
475     if( planes.mPlanes[ i ].mNormal.Dot( origin + (halfExtents * planes.mSign[i]) ) > -(planes.mPlanes[ i ].mDistance) )
476     {
477       continue;
478     }
479
480     return false;
481   }
482   return true;
483 }
484
485 uint32_t Camera::UpdateProjection( BufferIndex updateBufferIndex )
486 {
487   uint32_t retval( mUpdateProjectionFlag );
488   // Early-exit if no update required
489   if ( 0u != mUpdateProjectionFlag )
490   {
491     if ( COPY_PREVIOUS_MATRIX == mUpdateProjectionFlag )
492     {
493       // The projection matrix was updated in the previous frame; copy it
494       mProjectionMatrix.CopyPrevious( updateBufferIndex );
495     }
496     else // UPDATE_COUNT == mUpdateProjectionFlag
497     {
498       switch( mProjectionMode )
499       {
500         case Dali::Camera::PERSPECTIVE_PROJECTION:
501         {
502           Matrix &projectionMatrix = mProjectionMatrix.Get( updateBufferIndex );
503           Perspective( projectionMatrix,
504                        mFieldOfView,
505                        mAspectRatio,
506                        mNearClippingPlane,
507                        mFarClippingPlane,
508                        mInvertYAxis );
509           break;
510         }
511         case Dali::Camera::ORTHOGRAPHIC_PROJECTION:
512         {
513           Matrix &projectionMatrix = mProjectionMatrix.Get( updateBufferIndex );
514           Orthographic( projectionMatrix,
515                         mLeftClippingPlane,   mRightClippingPlane,
516                         mBottomClippingPlane, mTopClippingPlane,
517                         mNearClippingPlane,   mFarClippingPlane,
518                         mInvertYAxis );
519           break;
520         }
521       }
522
523       mProjectionMatrix.SetDirty( updateBufferIndex );
524
525       Matrix &finalProjection = mFinalProjection[ updateBufferIndex ];
526       finalProjection.SetIdentity();
527
528       Quaternion rotationAngle;
529       switch( mProjectionRotation )
530       {
531         case 90:
532         {
533           rotationAngle = Quaternion( Dali::ANGLE_90, Vector3::ZAXIS );
534           break;
535         }
536         case 180:
537         {
538           rotationAngle = Quaternion( Dali::ANGLE_180, Vector3::ZAXIS );
539           break;
540         }
541         case 270:
542         {
543           rotationAngle = Quaternion( Dali::ANGLE_270, Vector3::ZAXIS );
544           break;
545         }
546         default:
547           rotationAngle = Quaternion( Dali::ANGLE_0, Vector3::ZAXIS );
548           break;
549       }
550
551       Matrix rotation;
552       rotation.SetIdentity();
553       rotation.SetTransformComponents( Vector3( 1.0f, 1.0f, 1.0f ), rotationAngle, Vector3( 0.0f, 0.0f, 0.0f ) );
554
555       Matrix::Multiply( finalProjection, mProjectionMatrix.Get( updateBufferIndex ), rotation );
556     }
557     --mUpdateProjectionFlag;
558   }
559   return retval;
560 }
561
562 } // namespace SceneGraph
563
564 } // namespace Internal
565
566 } // namespace Dali