e62a7d856571e14ecde69fff5f2479ef5fc88eb0
[platform/core/uifw/dali-core.git] / dali / internal / update / node-attachments / scene-graph-camera-attachment.cpp
1 /*
2  * Copyright (c) 2014 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/node-attachments/scene-graph-camera-attachment.h>
20
21 // INTERNAL HEADERS
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>
27
28 namespace // unnamed namespace
29 {
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
32 }
33
34 namespace Dali
35 {
36
37 namespace Internal
38 {
39
40 namespace SceneGraph
41 {
42
43 namespace
44 {
45
46 void LookAt(Matrix& result, const Vector3& eye, const Vector3& target, const Vector3& up)
47 {
48   Vector3 vZ = target - eye;
49   vZ.Normalize();
50
51   Vector3 vX = up.Cross(vZ);
52   vX.Normalize();
53
54   Vector3 vY = vZ.Cross(vX);
55   vY.Normalize();
56
57   result.SetInverseTransformComponents(vX, vY, vZ, eye);
58 }
59
60
61 void Frustum(Matrix& result, float left, float right, float bottom, float top, float near, float far, bool invertYAxis)
62 {
63   float deltaZ = far - near;
64   if ((near <= 0.0f) || (far <= 0.0f) || Equals(right, left) || Equals(bottom, top) || (deltaZ <= 0.0f))
65   {
66     DALI_LOG_ERROR("Invalid parameters passed into Frustum!");
67     DALI_ASSERT_DEBUG("Invalid parameters passed into Frustum!");
68     return;
69   }
70
71   float deltaX = right - left;
72   float deltaY = invertYAxis ? bottom - top : top - bottom;
73
74   result.SetIdentity();
75
76   float* m = result.AsFloat();
77   m[0] = -2.0f * near / deltaX;
78   m[1] = m[2] = m[3] = 0.0f;
79
80   m[5] = -2.0f * near / deltaY;
81   m[4] = m[6] = m[7] = 0.0f;
82
83   m[8] = (right + left) / deltaX;
84   m[9] = (top + bottom) / deltaY;
85   m[10] = (near + far) / deltaZ;
86   m[11] = 1.0f;
87
88   m[14] = -2.0f * near * far / deltaZ;
89   m[12] = m[13] = m[15] = 0.0f;
90 }
91
92 void Perspective(Matrix& result, float fovy, float aspect, float near, float far, bool invertYAxis, const Vector2& stereoBias )
93 {
94   float frustumH = tanf( fovy * 0.5f ) * near;
95   float frustumW = frustumH * aspect;
96   Vector2 bias = stereoBias * 0.5f;
97
98   Frustum(result, -(frustumW + bias.x), frustumW - bias.x, -(frustumH + bias.y), frustumH - bias.y, near, far, invertYAxis);
99 }
100
101 void Orthographic(Matrix& result, float left, float right, float bottom, float top, float near, float far, bool invertYAxis)
102 {
103   if ( Equals(right, left) || Equals(top, bottom) || Equals(far, near) )
104   {
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." );
107     return;
108   }
109
110   float deltaX = right - left;
111   float deltaY = invertYAxis ? bottom - top : top - bottom;
112   float deltaZ = far - near;
113
114   float *m = result.AsFloat();
115   m[0] = -2.0f / deltaX;
116   m[1] = 0.0f;
117   m[2] = 0.0f;
118   m[3] = 0.0f;
119
120   m[4] = 0.0f;
121   m[5] = -2.0f / deltaY;
122   m[6] = 0.0f;
123   m[7] = 0.0f;
124
125   m[8] = 0.0f;
126   m[9] = 0.0f;
127   m[10] = 2.0f / deltaZ;
128   m[11] = 0.0f;
129   m[12] = -(right + left) / deltaX;
130   m[13] = -(top + bottom) / deltaY;
131   m[14] = -(near + far)   / deltaZ;
132   m[15] = 1.0f;
133 }
134
135 } // unnamed namespace
136
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 );
150
151
152 CameraAttachment::CameraAttachment()
153 : NodeAttachment(),
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 ),
169   mViewMatrix(),
170   mProjectionMatrix(),
171   mInverseViewProjection( Matrix::IDENTITY )
172 {
173 }
174
175 CameraAttachment* CameraAttachment::New()
176 {
177   return new CameraAttachment();
178 }
179
180 void CameraAttachment::Initialize( SceneController& sceneController, BufferIndex updateBufferIndex )
181 {
182   // do nothing
183 }
184
185 void CameraAttachment::OnDestroy()
186 {
187   // do nothing
188 }
189
190 void CameraAttachment::ConnectedToSceneGraph()
191 {
192   // do nothing
193 }
194
195 void CameraAttachment::DisconnectedFromSceneGraph()
196 {
197   // do nothing
198 }
199
200 CameraAttachment::~CameraAttachment()
201 {
202 }
203
204 RenderableAttachment* CameraAttachment::GetRenderable()
205 {
206   return NULL;
207 }
208
209 void CameraAttachment::SetType( Dali::Camera::Type type )
210 {
211   mType = type;
212 }
213
214 void CameraAttachment::SetProjectionMode( Dali::Camera::ProjectionMode mode )
215 {
216   mProjectionMode = mode;
217   mUpdateProjectionFlag = UPDATE_COUNT;
218 }
219
220 void CameraAttachment::SetInvertYAxis( bool invertYAxis )
221 {
222   mInvertYAxis = invertYAxis;
223   mUpdateProjectionFlag = UPDATE_COUNT;
224 }
225
226 void CameraAttachment::SetFieldOfView( float fieldOfView )
227 {
228   mFieldOfView = fieldOfView;
229   mUpdateProjectionFlag = UPDATE_COUNT;
230 }
231
232 void CameraAttachment::SetAspectRatio( float aspectRatio )
233 {
234   mAspectRatio = aspectRatio;
235   mUpdateProjectionFlag = UPDATE_COUNT;
236 }
237
238 void CameraAttachment::SetStereoBias( const Vector2& stereoBias )
239 {
240   mStereoBias = stereoBias;
241   mUpdateProjectionFlag = UPDATE_COUNT;
242 }
243
244 void CameraAttachment::SetLeftClippingPlane( float leftClippingPlane )
245 {
246   mLeftClippingPlane = leftClippingPlane;
247   mUpdateProjectionFlag = UPDATE_COUNT;
248 }
249
250 void CameraAttachment::SetRightClippingPlane( float rightClippingPlane )
251 {
252   mRightClippingPlane = rightClippingPlane;
253   mUpdateProjectionFlag = UPDATE_COUNT;
254 }
255
256 void CameraAttachment::SetTopClippingPlane( float topClippingPlane )
257 {
258   mTopClippingPlane = topClippingPlane;
259   mUpdateProjectionFlag = UPDATE_COUNT;
260 }
261
262 void CameraAttachment::SetBottomClippingPlane( float bottomClippingPlane )
263 {
264   mBottomClippingPlane = bottomClippingPlane;
265   mUpdateProjectionFlag = UPDATE_COUNT;
266 }
267
268 void CameraAttachment::SetNearClippingPlane( float nearClippingPlane )
269 {
270   mNearClippingPlane = nearClippingPlane;
271   mUpdateProjectionFlag = UPDATE_COUNT;
272 }
273
274 void CameraAttachment::SetFarClippingPlane( float farClippingPlane )
275 {
276   mFarClippingPlane = farClippingPlane;
277   mUpdateProjectionFlag = UPDATE_COUNT;
278 }
279
280 void CameraAttachment::SetTargetPosition( const Vector3& targetPosition )
281 {
282   mTargetPosition = targetPosition;
283   mUpdateViewFlag = UPDATE_COUNT;
284 }
285
286 const Matrix& CameraAttachment::GetProjectionMatrix( BufferIndex bufferIndex ) const
287 {
288   return mProjectionMatrix[ bufferIndex ];
289 }
290
291 const Matrix& CameraAttachment::GetViewMatrix( BufferIndex bufferIndex ) const
292 {
293   return mViewMatrix[ bufferIndex ];
294 }
295
296 const Matrix& CameraAttachment::GetInverseViewProjectionMatrix( BufferIndex bufferIndex ) const
297 {
298   return mInverseViewProjection[ bufferIndex ];
299 }
300
301 const PropertyInputImpl* CameraAttachment::GetProjectionMatrix() const
302 {
303   return &mProjectionMatrix;
304 }
305
306 const PropertyInputImpl* CameraAttachment::GetViewMatrix() const
307 {
308   return &mViewMatrix;
309 }
310
311 void CameraAttachment::Update( BufferIndex updateBufferIndex, const Node& owningNode, int nodeDirtyFlags )
312 {
313   // if owning node has changes in world position we need to update camera for next 2 frames
314   if( nodeDirtyFlags & TransformFlag )
315   {
316     mUpdateViewFlag = UPDATE_COUNT;
317   }
318   if( nodeDirtyFlags & VisibleFlag )
319   {
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;
325   }
326
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 );
330
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 )
333   {
334     // either has actually changed so recalculate
335     Matrix::Multiply( mInverseViewProjection[ updateBufferIndex ], mViewMatrix[ updateBufferIndex ], mProjectionMatrix[ updateBufferIndex ] );
336     UpdateFrustum( updateBufferIndex );
337
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() );
340   }
341   else if( viewUpdateCount == COPY_PREVIOUS_MATRIX || projectionUpdateCount == COPY_PREVIOUS_MATRIX )
342   {
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 ];
347   }
348 }
349
350 bool CameraAttachment::ViewMatrixUpdated()
351 {
352   return 0u != mUpdateViewFlag;
353 }
354
355 unsigned int CameraAttachment::UpdateViewMatrix( BufferIndex updateBufferIndex, const Node& owningNode )
356 {
357   unsigned int retval( mUpdateViewFlag );
358   if( 0u != mUpdateViewFlag )
359   {
360     if( COPY_PREVIOUS_MATRIX == mUpdateViewFlag )
361     {
362       // The projection matrix was updated in the previous frame; copy it
363       mViewMatrix.CopyPrevious( updateBufferIndex );
364     }
365     else // UPDATE_COUNT == mUpdateViewFlag
366     {
367       switch( mType )
368       {
369         // camera orientation taken from node - i.e. look in abitrary, unconstrained direction
370         case Dali::Camera::FREE_LOOK:
371           {
372           Matrix& viewMatrix = mViewMatrix.Get( updateBufferIndex );
373           viewMatrix.SetInverseTransformComponents( Vector3::ONE, owningNode.GetWorldOrientation( updateBufferIndex ),
374                                                     owningNode.GetWorldPosition( updateBufferIndex ) );
375           mViewMatrix.SetDirty( updateBufferIndex );
376           break;
377         }
378           // camera orientation constrained to look at a target
379         case Dali::Camera::LOOK_AT_TARGET:
380           {
381           Matrix& viewMatrix = mViewMatrix.Get( updateBufferIndex );
382           LookAt( viewMatrix, owningNode.GetWorldPosition( updateBufferIndex ), mTargetPosition,
383                   owningNode.GetWorldOrientation( updateBufferIndex ).Rotate( Vector3::YAXIS ) );
384           mViewMatrix.SetDirty( updateBufferIndex );
385           break;
386         }
387       }
388     }
389     --mUpdateViewFlag;
390   }
391   return retval;
392 }
393
394 void CameraAttachment::UpdateFrustum( BufferIndex updateBufferIndex, bool normalize )
395 {
396
397   // Extract the clip matrix planes
398   Matrix clipMatrix;
399   Matrix::Multiply( clipMatrix, mViewMatrix[ updateBufferIndex ], mProjectionMatrix[ updateBufferIndex ] );
400
401   const float* cm = clipMatrix.AsFloat();
402   FrustumPlanes& planes = mFrustum[ updateBufferIndex ];
403
404   // Left
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 ];
409
410   // Right
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 ];
415
416   // Bottom
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 ];
421
422   // Top
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 ];
427
428   // Near
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 ];
433
434   // Far
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 ];
439
440   if ( normalize )
441   {
442     for ( unsigned int i = 0; i < 6; ++i )
443     {
444       // Normalize planes to ensure correct bounding distance checking
445       Plane& plane = planes.mPlanes[ i ];
446       float l = 1.0f / plane.mNormal.Length();
447       plane.mNormal *= l;
448       plane.mDistance *= l;
449     }
450   }
451   mFrustum[ updateBufferIndex ? 0 : 1 ] = planes;
452 }
453
454 bool CameraAttachment::CheckSphereInFrustum( BufferIndex bufferIndex, const Vector3& origin, float radius )
455 {
456   const FrustumPlanes& planes = mFrustum[ bufferIndex ];
457   for ( uint32_t i = 0; i < 6; ++i )
458   {
459     if ( ( planes.mPlanes[ i ].mDistance + planes.mPlanes[ i ].mNormal.Dot( origin ) ) < -radius )
460     {
461       return false;
462     }
463   }
464   return true;
465 }
466
467 bool CameraAttachment::CheckAABBInFrustum( BufferIndex bufferIndex, const Vector3& origin, const Vector3& halfExtents )
468 {
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 );
478
479   for ( uint32_t i = 0; i < 6; ++i )
480   {
481     if ( planes.mPlanes[ i ].mDistance + planes.mPlanes[ i ].mNormal.Dot( tln ) >= 0 )
482     {
483       continue;
484     }
485
486     if ( planes.mPlanes[ i ].mDistance + planes.mPlanes[ i ].mNormal.Dot( trn ) >= 0 )
487     {
488       continue;
489     }
490
491     if ( planes.mPlanes[ i ].mDistance + planes.mPlanes[ i ].mNormal.Dot( bln ) >= 0 )
492     {
493       continue;
494     }
495
496     if ( planes.mPlanes[ i ].mDistance + planes.mPlanes[ i ].mNormal.Dot( brn ) >= 0 )
497     {
498       continue;
499     }
500
501     if ( planes.mPlanes[ i ].mDistance + planes.mPlanes[ i ].mNormal.Dot( tlf ) >= 0 )
502     {
503       continue;
504     }
505
506      if ( planes.mPlanes[ i ].mDistance + planes.mPlanes[ i ].mNormal.Dot( trf ) >= 0 )
507     {
508       continue;
509     }
510
511      if ( planes.mPlanes[ i ].mDistance + planes.mPlanes[ i ].mNormal.Dot( blf ) >= 0 )
512     {
513       continue;
514     }
515
516     if ( planes.mPlanes[ i ].mDistance + planes.mPlanes[ i ].mNormal.Dot( brf ) >= 0 )
517     {
518       continue;
519     }
520     return false;
521   }
522   return true;
523 }
524
525 unsigned int CameraAttachment::UpdateProjection( BufferIndex updateBufferIndex )
526 {
527   unsigned int retval( mUpdateProjectionFlag );
528   // Early-exit if no update required
529   if ( 0u != mUpdateProjectionFlag )
530   {
531     if ( COPY_PREVIOUS_MATRIX == mUpdateProjectionFlag )
532     {
533       // The projection matrix was updated in the previous frame; copy it
534       mProjectionMatrix.CopyPrevious( updateBufferIndex );
535     }
536     else // UPDATE_COUNT == mUpdateProjectionFlag
537     {
538       switch( mProjectionMode )
539       {
540         case Dali::Camera::PERSPECTIVE_PROJECTION:
541         {
542           Matrix &projectionMatrix = mProjectionMatrix.Get(updateBufferIndex);
543           Perspective( projectionMatrix,
544                        mFieldOfView,
545                        mAspectRatio,
546                        mNearClippingPlane,
547                        mFarClippingPlane,
548                        mInvertYAxis,
549                        mStereoBias );
550           break;
551         }
552         case Dali::Camera::ORTHOGRAPHIC_PROJECTION:
553         {
554           Matrix &projectionMatrix = mProjectionMatrix.Get(updateBufferIndex);
555           Orthographic( projectionMatrix,
556                         mLeftClippingPlane,   mRightClippingPlane,
557                         mBottomClippingPlane, mTopClippingPlane,
558                         mNearClippingPlane,   mFarClippingPlane,
559                         mInvertYAxis );
560           break;
561         }
562       }
563
564       mProjectionMatrix.SetDirty(updateBufferIndex);
565     }
566     --mUpdateProjectionFlag;
567   }
568   return retval;
569 }
570
571 } // namespace SceneGraph
572
573 } // namespace Internal
574
575 } // namespace Dali