Fix SVACE issue
[platform/core/uifw/dali-core.git] / dali / internal / update / render-tasks / scene-graph-camera.cpp
1 /*
2  * Copyright (c) 2017 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 unsigned int UPDATE_COUNT        = 2u;  // Update projection or view matrix this many frames after a change
33 const unsigned int 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 void LookAt(Matrix& result, const Vector3& eye, const Vector3& target, const Vector3& up)
49 {
50   Vector3 vZ = target - eye;
51   vZ.Normalize();
52
53   Vector3 vX = up.Cross(vZ);
54   vX.Normalize();
55
56   Vector3 vY = vZ.Cross(vX);
57   vY.Normalize();
58
59   result.SetInverseTransformComponents(vX, vY, vZ, eye);
60 }
61
62
63 void Frustum(Matrix& result, float left, float right, float bottom, float top, float near, float far, bool invertYAxis)
64 {
65   float deltaZ = far - near;
66   if ((near <= 0.0f) || (far <= 0.0f) || Equals(right, left) || Equals(bottom, top) || (deltaZ <= 0.0f))
67   {
68     DALI_LOG_ERROR("Invalid parameters passed into Frustum!\n");
69     DALI_ASSERT_DEBUG("Invalid parameters passed into Frustum!");
70     return;
71   }
72
73   float deltaX = right - left;
74   float deltaY = invertYAxis ? bottom - top : top - bottom;
75
76   result.SetIdentity();
77
78   float* m = result.AsFloat();
79   m[0] = -2.0f * near / deltaX;
80   m[1] = m[2] = m[3] = 0.0f;
81
82   m[5] = -2.0f * near / deltaY;
83   m[4] = m[6] = m[7] = 0.0f;
84
85   m[8] = (right + left) / deltaX;
86   m[9] = (top + bottom) / deltaY;
87   m[10] = (near + far) / deltaZ;
88   m[11] = 1.0f;
89
90   m[14] = -2.0f * near * far / deltaZ;
91   m[12] = m[13] = m[15] = 0.0f;
92 }
93
94 void Perspective(Matrix& result, float fovy, float aspect, float near, float far, bool invertYAxis, const Vector2& stereoBias )
95 {
96   float frustumH = tanf( fovy * 0.5f ) * near;
97   float frustumW = frustumH * aspect;
98   Vector2 bias = stereoBias * 0.5f;
99
100   Frustum(result, -(frustumW + bias.x), frustumW - bias.x, -(frustumH + bias.y), frustumH - bias.y, near, far, invertYAxis);
101 }
102
103 void Orthographic(Matrix& result, float left, float right, float bottom, float top, float near, float far, bool invertYAxis)
104 {
105   if ( Equals(right, left) || Equals(top, bottom) || Equals(far, near) )
106   {
107     DALI_LOG_ERROR( "Cannot create orthographic projection matrix with a zero dimension.\n" );
108     DALI_ASSERT_DEBUG( "Cannot create orthographic projection matrix with a zero dimension." );
109     return;
110   }
111
112   float deltaX = right - left;
113   float deltaY = invertYAxis ? bottom - top : top - bottom;
114   float deltaZ = far - near;
115
116   float *m = result.AsFloat();
117   m[0] = -2.0f / deltaX;
118   m[1] = 0.0f;
119   m[2] = 0.0f;
120   m[3] = 0.0f;
121
122   m[4] = 0.0f;
123   m[5] = -2.0f / deltaY;
124   m[6] = 0.0f;
125   m[7] = 0.0f;
126
127   m[8] = 0.0f;
128   m[9] = 0.0f;
129   m[10] = 2.0f / deltaZ;
130   m[11] = 0.0f;
131   m[12] = -(right + left) / deltaX;
132   m[13] = -(top + bottom) / deltaY;
133   m[14] = -(near + far)   / deltaZ;
134   m[15] = 1.0f;
135 }
136
137 } // unnamed namespace
138
139 const Dali::Camera::Type Camera::DEFAULT_TYPE( Dali::Camera::FREE_LOOK );
140 const Dali::Camera::ProjectionMode Camera::DEFAULT_MODE( Dali::Camera::PERSPECTIVE_PROJECTION );
141 const bool  Camera::DEFAULT_INVERT_Y_AXIS( false );
142 const float Camera::DEFAULT_FIELD_OF_VIEW( 45.0f*(M_PI/180.0f) );
143 const float Camera::DEFAULT_ASPECT_RATIO( 4.0f/3.0f );
144 const float Camera::DEFAULT_LEFT_CLIPPING_PLANE(-240.0f);
145 const float Camera::DEFAULT_RIGHT_CLIPPING_PLANE(240.0f);
146 const float Camera::DEFAULT_TOP_CLIPPING_PLANE(-400.0f);
147 const float Camera::DEFAULT_BOTTOM_CLIPPING_PLANE(400.0f);
148 const float Camera::DEFAULT_NEAR_CLIPPING_PLANE( 800.0f ); // default height of the screen
149 const float Camera::DEFAULT_FAR_CLIPPING_PLANE( DEFAULT_NEAR_CLIPPING_PLANE + 2.f * DEFAULT_NEAR_CLIPPING_PLANE );
150 const Vector2 Camera::DEFAULT_STEREO_BIAS( 0.0f, 0.0f );
151 const Vector3 Camera::DEFAULT_TARGET_POSITION( 0.0f, 0.0f, 0.0f );
152
153
154 Camera::Camera()
155 : mUpdateViewFlag( UPDATE_COUNT ),
156   mUpdateProjectionFlag( UPDATE_COUNT ),
157   mNode( NULL ),
158   mType( DEFAULT_TYPE ),
159   mProjectionMode( DEFAULT_MODE ),
160   mInvertYAxis( DEFAULT_INVERT_Y_AXIS ),
161   mFieldOfView( DEFAULT_FIELD_OF_VIEW ),
162   mAspectRatio( DEFAULT_ASPECT_RATIO ),
163   mLeftClippingPlane( DEFAULT_LEFT_CLIPPING_PLANE ),
164   mRightClippingPlane( DEFAULT_RIGHT_CLIPPING_PLANE ),
165   mTopClippingPlane( DEFAULT_TOP_CLIPPING_PLANE ),
166   mBottomClippingPlane( DEFAULT_BOTTOM_CLIPPING_PLANE ),
167   mNearClippingPlane( DEFAULT_NEAR_CLIPPING_PLANE ),
168   mFarClippingPlane( DEFAULT_FAR_CLIPPING_PLANE ),
169   mStereoBias( DEFAULT_STEREO_BIAS ),
170   mTargetPosition( DEFAULT_TARGET_POSITION ),
171   mViewMatrix(),
172   mProjectionMatrix(),
173   mInverseViewProjection( Matrix::IDENTITY )
174 {
175 }
176
177 Camera* Camera::New()
178 {
179   return new Camera();
180 }
181
182 Camera::~Camera()
183 {
184 }
185
186 void Camera::SetNode( const Node* node )
187 {
188   mNode = node;
189 }
190
191 void Camera::SetType( Dali::Camera::Type type )
192 {
193   mType = type;
194 }
195
196 void Camera::SetProjectionMode( Dali::Camera::ProjectionMode mode )
197 {
198   mProjectionMode = mode;
199   mUpdateProjectionFlag = UPDATE_COUNT;
200 }
201
202 void Camera::SetInvertYAxis( bool invertYAxis )
203 {
204   mInvertYAxis = invertYAxis;
205   mUpdateProjectionFlag = UPDATE_COUNT;
206 }
207
208 void Camera::SetFieldOfView( float fieldOfView )
209 {
210   mFieldOfView = fieldOfView;
211   mUpdateProjectionFlag = UPDATE_COUNT;
212 }
213
214 void Camera::SetAspectRatio( float aspectRatio )
215 {
216   mAspectRatio = aspectRatio;
217   mUpdateProjectionFlag = UPDATE_COUNT;
218 }
219
220 void Camera::SetStereoBias( const Vector2& stereoBias )
221 {
222   mStereoBias = stereoBias;
223   mUpdateProjectionFlag = UPDATE_COUNT;
224 }
225
226 void Camera::SetLeftClippingPlane( float leftClippingPlane )
227 {
228   mLeftClippingPlane = leftClippingPlane;
229   mUpdateProjectionFlag = UPDATE_COUNT;
230 }
231
232 void Camera::SetRightClippingPlane( float rightClippingPlane )
233 {
234   mRightClippingPlane = rightClippingPlane;
235   mUpdateProjectionFlag = UPDATE_COUNT;
236 }
237
238 void Camera::SetTopClippingPlane( float topClippingPlane )
239 {
240   mTopClippingPlane = topClippingPlane;
241   mUpdateProjectionFlag = UPDATE_COUNT;
242 }
243
244 void Camera::SetBottomClippingPlane( float bottomClippingPlane )
245 {
246   mBottomClippingPlane = bottomClippingPlane;
247   mUpdateProjectionFlag = UPDATE_COUNT;
248 }
249
250 void Camera::SetNearClippingPlane( float nearClippingPlane )
251 {
252   mNearClippingPlane = nearClippingPlane;
253   mUpdateProjectionFlag = UPDATE_COUNT;
254 }
255
256 void Camera::SetFarClippingPlane( float farClippingPlane )
257 {
258   mFarClippingPlane = farClippingPlane;
259   mUpdateProjectionFlag = UPDATE_COUNT;
260 }
261
262 void Camera::SetTargetPosition( const Vector3& targetPosition )
263 {
264   mTargetPosition = targetPosition;
265   mUpdateViewFlag = UPDATE_COUNT;
266 }
267
268 const Matrix& Camera::GetProjectionMatrix( BufferIndex bufferIndex ) const
269 {
270   return mProjectionMatrix[ bufferIndex ];
271 }
272
273 const Matrix& Camera::GetViewMatrix( BufferIndex bufferIndex ) const
274 {
275   return mViewMatrix[ bufferIndex ];
276 }
277
278 const Matrix& Camera::GetInverseViewProjectionMatrix( BufferIndex bufferIndex ) const
279 {
280   return mInverseViewProjection[ bufferIndex ];
281 }
282
283 const PropertyInputImpl* Camera::GetProjectionMatrix() const
284 {
285   return &mProjectionMatrix;
286 }
287
288 const PropertyInputImpl* Camera::GetViewMatrix() const
289 {
290   return &mViewMatrix;
291 }
292
293 void Camera::Update( BufferIndex updateBufferIndex )
294 {
295   // if owning node has changes in world position we need to update camera for next 2 frames
296   if( mNode->IsLocalMatrixDirty() )
297   {
298     mUpdateViewFlag = UPDATE_COUNT;
299   }
300   if( mNode->GetDirtyFlags() & VisibleFlag )
301   {
302     // If the visibility changes, the projection matrix needs to be re-calculated.
303     // It may happen the first time an actor is rendered it's rendered only once and becomes invisible,
304     // in the following update the node will be skipped leaving the projection matrix (double buffered)
305     // with the Identity.
306     mUpdateProjectionFlag = UPDATE_COUNT;
307   }
308
309   // if either matrix changed, we need to recalculate the inverse matrix for hit testing to work
310   unsigned int viewUpdateCount = UpdateViewMatrix( updateBufferIndex );
311   unsigned int projectionUpdateCount = UpdateProjection( updateBufferIndex );
312
313   // if model or view matrix changed we need to either recalculate the inverse VP or copy previous
314   if( viewUpdateCount > COPY_PREVIOUS_MATRIX || projectionUpdateCount > COPY_PREVIOUS_MATRIX )
315   {
316     // either has actually changed so recalculate
317     Matrix::Multiply( mInverseViewProjection[ updateBufferIndex ], mViewMatrix[ updateBufferIndex ], mProjectionMatrix[ updateBufferIndex ] );
318     UpdateFrustum( updateBufferIndex );
319
320     // ignore the error, if the view projection is incorrect (non inversible) then you will have tough times anyways
321     static_cast< void >( mInverseViewProjection[ updateBufferIndex ].Invert() );
322   }
323   else if( viewUpdateCount == COPY_PREVIOUS_MATRIX || projectionUpdateCount == COPY_PREVIOUS_MATRIX )
324   {
325     // neither has actually changed, but we might copied previous frames value so need to
326     // copy the previous inverse and frustum as well
327     mInverseViewProjection[updateBufferIndex] = mInverseViewProjection[updateBufferIndex ? 0 : 1];
328     mFrustum[ updateBufferIndex ] = mFrustum[ updateBufferIndex ? 0 : 1 ];
329   }
330 }
331
332 bool Camera::ViewMatrixUpdated()
333 {
334   return 0u != mUpdateViewFlag;
335 }
336
337 unsigned int Camera::UpdateViewMatrix( BufferIndex updateBufferIndex )
338 {
339   unsigned int retval( mUpdateViewFlag );
340   if( 0u != mUpdateViewFlag )
341   {
342     if( COPY_PREVIOUS_MATRIX == mUpdateViewFlag )
343     {
344       // The projection matrix was updated in the previous frame; copy it
345       mViewMatrix.CopyPrevious( updateBufferIndex );
346     }
347     else // UPDATE_COUNT == mUpdateViewFlag
348     {
349       switch( mType )
350       {
351         // camera orientation taken from node - i.e. look in abitrary, unconstrained direction
352         case Dali::Camera::FREE_LOOK:
353         {
354           Matrix& viewMatrix = mViewMatrix.Get( updateBufferIndex );
355           viewMatrix = mNode->GetWorldMatrix( updateBufferIndex );
356           viewMatrix.Invert();
357           mViewMatrix.SetDirty( updateBufferIndex );
358           break;
359         }
360           // camera orientation constrained to look at a target
361         case Dali::Camera::LOOK_AT_TARGET:
362         {
363           const Matrix& owningNodeMatrix( mNode->GetWorldMatrix( updateBufferIndex ) );
364           Vector3 position, scale;
365           Quaternion orientation;
366           owningNodeMatrix.GetTransformComponents( position, orientation, scale );
367           Matrix& viewMatrix = mViewMatrix.Get( updateBufferIndex );
368           LookAt( viewMatrix, position, mTargetPosition, orientation.Rotate( Vector3::YAXIS ) );
369           mViewMatrix.SetDirty( updateBufferIndex );
370           break;
371         }
372       }
373     }
374     --mUpdateViewFlag;
375   }
376   return retval;
377 }
378
379 void Camera::UpdateFrustum( BufferIndex updateBufferIndex, bool normalize )
380 {
381
382   // Extract the clip matrix planes
383   Matrix clipMatrix;
384   Matrix::Multiply( clipMatrix, mViewMatrix[ updateBufferIndex ], mProjectionMatrix[ updateBufferIndex ] );
385
386   const float* cm = clipMatrix.AsFloat();
387   FrustumPlanes& planes = mFrustum[ updateBufferIndex ];
388
389   // Left
390   planes.mPlanes[ 0 ].mNormal.x = cm[ 3 ]  + cm[ 0 ]; // column 4 + column 1
391   planes.mPlanes[ 0 ].mNormal.y = cm[ 7 ]  + cm[ 4 ];
392   planes.mPlanes[ 0 ].mNormal.z = cm[ 11 ] + cm[ 8 ];
393   planes.mPlanes[ 0 ].mDistance = cm[ 15 ] + cm[ 12 ];
394
395   // Right
396   planes.mPlanes[ 1 ].mNormal.x = cm[ 3 ]  - cm[ 0 ]; // column 4 - column 1
397   planes.mPlanes[ 1 ].mNormal.y = cm[ 7 ]  - cm[ 4 ];
398   planes.mPlanes[ 1 ].mNormal.z = cm[ 11 ] - cm[ 8 ];
399   planes.mPlanes[ 1 ].mDistance = cm[ 15 ] - cm[ 12 ];
400
401   // Bottom
402   planes.mPlanes[ 2 ].mNormal.x = cm[ 3 ]  + cm[ 1 ]; // column 4 + column 2
403   planes.mPlanes[ 2 ].mNormal.y = cm[ 7 ]  + cm[ 5 ];
404   planes.mPlanes[ 2 ].mNormal.z = cm[ 11 ] + cm[ 9 ];
405   planes.mPlanes[ 2 ].mDistance = cm[ 15 ] + cm[ 13 ];
406
407   // Top
408   planes.mPlanes[ 3 ].mNormal.x = cm[ 3 ]  - cm[ 1 ]; // column 4 - column 2
409   planes.mPlanes[ 3 ].mNormal.y = cm[ 7 ]  - cm[ 5 ];
410   planes.mPlanes[ 3 ].mNormal.z = cm[ 11 ] - cm[ 9 ];
411   planes.mPlanes[ 3 ].mDistance = cm[ 15 ] - cm[ 13 ];
412
413   // Near
414   planes.mPlanes[ 4 ].mNormal.x = cm[ 3 ]  + cm[ 2 ]; // column 4 + column 3
415   planes.mPlanes[ 4 ].mNormal.y = cm[ 7 ]  + cm[ 6 ];
416   planes.mPlanes[ 4 ].mNormal.z = cm[ 11 ] + cm[ 10 ];
417   planes.mPlanes[ 4 ].mDistance = cm[ 15 ] + cm[ 14 ];
418
419   // Far
420   planes.mPlanes[ 5 ].mNormal.x = cm[ 3 ]  - cm[ 2 ]; // column 4 - column 3
421   planes.mPlanes[ 5 ].mNormal.y = cm[ 7 ]  - cm[ 6 ];
422   planes.mPlanes[ 5 ].mNormal.z = cm[ 11 ] - cm[ 10 ];
423   planes.mPlanes[ 5 ].mDistance = cm[ 15 ] - cm[ 14 ];
424
425   if ( normalize )
426   {
427     for ( unsigned int i = 0; i < 6; ++i )
428     {
429       // Normalize planes to ensure correct bounding distance checking
430       Plane& plane = planes.mPlanes[ i ];
431       float l = 1.0f / plane.mNormal.Length();
432       plane.mNormal *= l;
433       plane.mDistance *= l;
434
435       planes.mSign[i] = Vector3( Sign(plane.mNormal.x), Sign(plane.mNormal.y), Sign(plane.mNormal.z) );
436     }
437   }
438   else
439   {
440     for ( unsigned int i = 0; i < 6; ++i )
441     {
442       planes.mSign[i] = Vector3( Sign(planes.mPlanes[ i ].mNormal.x), Sign(planes.mPlanes[ i ].mNormal.y), Sign(planes.mPlanes[ i ].mNormal.z) );
443     }
444   }
445   mFrustum[ updateBufferIndex ? 0 : 1 ] = planes;
446 }
447
448 bool Camera::CheckSphereInFrustum( BufferIndex bufferIndex, const Vector3& origin, float radius )
449 {
450   const FrustumPlanes& planes = mFrustum[ bufferIndex ];
451   for ( uint32_t i = 0; i < 6; ++i )
452   {
453     if ( ( planes.mPlanes[ i ].mDistance + planes.mPlanes[ i ].mNormal.Dot( origin ) ) < -radius )
454     {
455       return false;
456     }
457   }
458   return true;
459 }
460
461 bool Camera::CheckAABBInFrustum( BufferIndex bufferIndex, const Vector3& origin, const Vector3& halfExtents )
462 {
463   const FrustumPlanes& planes = mFrustum[ bufferIndex ];
464   for ( uint32_t i = 0; i < 6; ++i )
465   {
466     if( planes.mPlanes[ i ].mNormal.Dot( origin + (halfExtents * planes.mSign[i]) ) > -(planes.mPlanes[ i ].mDistance) )
467     {
468       continue;
469     }
470
471     return false;
472   }
473   return true;
474 }
475
476 unsigned int Camera::UpdateProjection( BufferIndex updateBufferIndex )
477 {
478   unsigned int retval( mUpdateProjectionFlag );
479   // Early-exit if no update required
480   if ( 0u != mUpdateProjectionFlag )
481   {
482     if ( COPY_PREVIOUS_MATRIX == mUpdateProjectionFlag )
483     {
484       // The projection matrix was updated in the previous frame; copy it
485       mProjectionMatrix.CopyPrevious( updateBufferIndex );
486     }
487     else // UPDATE_COUNT == mUpdateProjectionFlag
488     {
489       switch( mProjectionMode )
490       {
491         case Dali::Camera::PERSPECTIVE_PROJECTION:
492         {
493           Matrix &projectionMatrix = mProjectionMatrix.Get( updateBufferIndex );
494           Perspective( projectionMatrix,
495                        mFieldOfView,
496                        mAspectRatio,
497                        mNearClippingPlane,
498                        mFarClippingPlane,
499                        mInvertYAxis,
500                        mStereoBias );
501           break;
502         }
503         case Dali::Camera::ORTHOGRAPHIC_PROJECTION:
504         {
505           Matrix &projectionMatrix = mProjectionMatrix.Get( updateBufferIndex );
506           Orthographic( projectionMatrix,
507                         mLeftClippingPlane,   mRightClippingPlane,
508                         mBottomClippingPlane, mTopClippingPlane,
509                         mNearClippingPlane,   mFarClippingPlane,
510                         mInvertYAxis );
511           break;
512         }
513       }
514
515       mProjectionMatrix.SetDirty( updateBufferIndex );
516     }
517     --mUpdateProjectionFlag;
518   }
519   return retval;
520 }
521
522 } // namespace SceneGraph
523
524 } // namespace Internal
525
526 } // namespace Dali