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