Deprecate Plane Distance setter + Implement OrthographicSize + Animatable AspectRatio
[platform/core/uifw/dali-core.git] / dali / internal / update / render-tasks / scene-graph-camera.cpp
1 /*
2  * Copyright (c) 2022 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/internal/common/matrix-utils.h>
27 #include <dali/internal/common/memory-pool-object-allocator.h>
28 #include <dali/internal/update/nodes/node.h>
29 #include <dali/public-api/common/dali-common.h>
30 #include <dali/public-api/math/math-utils.h>
31
32 namespace // unnamed namespace
33 {
34 const uint32_t UPDATE_COUNT         = 2u; // Update projection or view matrix this many frames after a change
35 const uint32_t COPY_PREVIOUS_MATRIX = 1u; // Copy view or projection matrix from previous frame
36
37 //For reflection and clipping plane
38 const float REFLECTION_NORMALIZED_DEVICE_COORDINATE_PARAMETER_A = 2.0f;
39 const float REFLECTION_NORMALIZED_DEVICE_COORDINATE_PARAMETER_D = 1.0f;
40 } // namespace
41
42 namespace Dali
43 {
44 namespace Internal
45 {
46 namespace SceneGraph
47 {
48 namespace
49 {
50 //Memory pool used to allocate new camera. Memory used by this pool will be released when shutting down DALi
51 MemoryPoolObjectAllocator<Camera> gCameraMemoryPool;
52
53 template<typename T>
54 T Sign(T value)
55 {
56   return T(T(0) < value) - T(value < T(0));
57 }
58
59 void LookAt(Matrix& result, const Vector3& eye, const Vector3& target, const Vector3& up)
60 {
61   Vector3 vZ = target - eye;
62   vZ.Normalize();
63
64   Vector3 vX = up.Cross(vZ);
65   vX.Normalize();
66
67   Vector3 vY = vZ.Cross(vX);
68   vY.Normalize();
69
70   result.SetInverseTransformComponents(vX, vY, vZ, eye);
71 }
72
73 void Frustum(Matrix& result, float left, float right, float bottom, float top, float near, float far, bool invertYAxis)
74 {
75   float deltaZ = far - near;
76   if((near <= 0.0f) || (far <= 0.0f) || Equals(right, left) || Equals(bottom, top) || (deltaZ <= 0.0f))
77   {
78     DALI_LOG_ERROR("Invalid parameters passed into Frustum!\n");
79     DALI_ASSERT_DEBUG("Invalid parameters passed into Frustum!");
80     return;
81   }
82
83   float deltaX = right - left;
84   float deltaY = invertYAxis ? bottom - top : top - bottom;
85
86   result.SetIdentity();
87
88   float* m = result.AsFloat();
89   m[0]     = -2.0f * near / deltaX;
90   m[1] = m[2] = m[3] = 0.0f;
91
92   m[5] = -2.0f * near / deltaY;
93   m[4] = m[6] = m[7] = 0.0f;
94
95   m[8]  = (right + left) / deltaX;
96   m[9]  = (top + bottom) / deltaY;
97   m[10] = (near + far) / deltaZ;
98   m[11] = 1.0f;
99
100   m[14] = -2.0f * near * far / deltaZ;
101   m[12] = m[13] = m[15] = 0.0f;
102 }
103
104 void Perspective(Matrix& result, Dali::DevelCameraActor::ProjectionDirection fovDir, float fov, float aspect, float near, float far, bool invertYAxis)
105 {
106   float frustumH;
107   float frustumW;
108   if(fovDir == Dali::DevelCameraActor::ProjectionDirection::VERTICAL)
109   {
110     frustumH = tanf(fov * 0.5f) * near;
111     frustumW = frustumH * aspect;
112   }
113   else
114   {
115     frustumW = tanf(fov * 0.5f) * near;
116     frustumH = frustumW / aspect;
117   }
118
119   Frustum(result, -frustumW, frustumW, -frustumH, frustumH, near, far, invertYAxis);
120 }
121
122 void Orthographic(Matrix& result, Dali::DevelCameraActor::ProjectionDirection orthographicDir, float orthographicSize, float aspect, float near, float far, bool invertYAxis)
123 {
124   if(EqualsZero(orthographicSize) || EqualsZero(aspect) || Equals(far, near))
125   {
126     DALI_LOG_ERROR("Cannot create orthographic projection matrix with a zero dimension.\n");
127     DALI_ASSERT_DEBUG("Cannot create orthographic projection matrix with a zero dimension.");
128     return;
129   }
130
131   float halfDeltaX;
132   float halfDeltaY;
133   if(orthographicDir == Dali::DevelCameraActor::ProjectionDirection::VERTICAL)
134   {
135     halfDeltaY = orthographicSize;
136     halfDeltaX = halfDeltaY * aspect;
137   }
138   else
139   {
140     halfDeltaX = orthographicSize;
141     halfDeltaY = halfDeltaX / aspect;
142   }
143
144   float deltaZ = far - near;
145
146   float* m = result.AsFloat();
147
148   m[0] = -1.0f / halfDeltaX;
149   m[1] = 0.0f;
150   m[2] = 0.0f;
151   m[3] = 0.0f;
152
153   m[4] = 0.0f;
154   m[5] = (invertYAxis ? 1.0f : -1.0f) / halfDeltaY;
155   m[6] = 0.0f;
156   m[7] = 0.0f;
157
158   m[8]  = 0.0f;
159   m[9]  = 0.0f;
160   m[10] = 2.0f / deltaZ;
161   m[11] = 0.0f;
162
163   m[12] = 0.0f;
164   m[13] = 0.0f;
165   m[14] = -(near + far) / deltaZ;
166   m[15] = 1.0f;
167 }
168
169 } // unnamed namespace
170
171 const Dali::Camera::Type                          Camera::DEFAULT_TYPE(Dali::Camera::FREE_LOOK);
172 const Dali::Camera::ProjectionMode                Camera::DEFAULT_MODE(Dali::Camera::PERSPECTIVE_PROJECTION);
173 const Dali::DevelCameraActor::ProjectionDirection Camera::DEFAULT_PROJECTION_DIRECTION(Dali::DevelCameraActor::VERTICAL);
174 const bool                                        Camera::DEFAULT_INVERT_Y_AXIS(false);
175 const float                                       Camera::DEFAULT_FIELD_OF_VIEW(45.0f * (Math::PI / 180.0f));
176 const float                                       Camera::DEFAULT_ORTHOGRAPHIC_SIZE(400.0f);     // half of default height of the screen
177 const float                                       Camera::DEFAULT_ASPECT_RATIO(480.0f / 800.0f); // default width / default height of the screen
178 const float                                       Camera::DEFAULT_NEAR_CLIPPING_PLANE(800.0f);   // default height of the screen
179 const float                                       Camera::DEFAULT_FAR_CLIPPING_PLANE(DEFAULT_NEAR_CLIPPING_PLANE + 2.f * DEFAULT_NEAR_CLIPPING_PLANE);
180 const Vector3                                     Camera::DEFAULT_TARGET_POSITION(0.0f, 0.0f, 0.0f);
181
182 Camera::Camera()
183 : Node(),
184   mUpdateViewFlag(UPDATE_COUNT),
185   mUpdateProjectionFlag(UPDATE_COUNT),
186   mProjectionRotation(0),
187   mType(DEFAULT_TYPE),
188   mProjectionMode(DEFAULT_MODE),
189   mProjectionDirection(DEFAULT_PROJECTION_DIRECTION),
190   mInvertYAxis(DEFAULT_INVERT_Y_AXIS),
191   mFieldOfView(DEFAULT_FIELD_OF_VIEW),
192   mOrthographicSize(DEFAULT_ORTHOGRAPHIC_SIZE),
193   mAspectRatio(DEFAULT_ASPECT_RATIO),
194   mNearClippingPlane(DEFAULT_NEAR_CLIPPING_PLANE),
195   mFarClippingPlane(DEFAULT_FAR_CLIPPING_PLANE),
196   mTargetPosition(DEFAULT_TARGET_POSITION),
197   mViewMatrix(),
198   mProjectionMatrix(),
199   mInverseViewProjection(Matrix::IDENTITY),
200   mFinalProjection(Matrix::IDENTITY)
201 {
202   // set a flag the node to say this is a camera
203   mIsCamera = true;
204 }
205
206 Camera* Camera::New()
207 {
208   return new(gCameraMemoryPool.AllocateRawThreadSafe()) Camera();
209 }
210
211 Camera::~Camera() = default;
212
213 void Camera::operator delete(void* ptr)
214 {
215   gCameraMemoryPool.FreeThreadSafe(static_cast<Camera*>(ptr));
216 }
217
218 void Camera::SetType(Dali::Camera::Type type)
219 {
220   mType = type;
221 }
222
223 void Camera::SetProjectionMode(Dali::Camera::ProjectionMode mode)
224 {
225   mProjectionMode       = mode;
226   mUpdateProjectionFlag = UPDATE_COUNT;
227 }
228
229 void Camera::SetProjectionDirection(Dali::DevelCameraActor::ProjectionDirection direction)
230 {
231   mProjectionDirection  = direction;
232   mUpdateProjectionFlag = UPDATE_COUNT;
233 }
234
235 void Camera::SetInvertYAxis(bool invertYAxis)
236 {
237   mInvertYAxis          = invertYAxis;
238   mUpdateProjectionFlag = UPDATE_COUNT;
239 }
240
241 void Camera::BakeFieldOfView(BufferIndex updateBufferIndex, float fieldOfView)
242 {
243   mFieldOfView.Bake(updateBufferIndex, fieldOfView);
244   mUpdateProjectionFlag = UPDATE_COUNT;
245 }
246
247 void Camera::BakeOrthographicSize(BufferIndex updateBufferIndex, float orthographicSize)
248 {
249   mOrthographicSize.Bake(updateBufferIndex, orthographicSize);
250   mUpdateProjectionFlag = UPDATE_COUNT;
251 }
252
253 void Camera::BakeAspectRatio(BufferIndex updateBufferIndex, float aspectRatio)
254 {
255   mAspectRatio.Bake(updateBufferIndex, aspectRatio);
256   mUpdateProjectionFlag = UPDATE_COUNT;
257 }
258
259 void Camera::SetNearClippingPlane(float nearClippingPlane)
260 {
261   mNearClippingPlane    = nearClippingPlane;
262   mUpdateProjectionFlag = UPDATE_COUNT;
263 }
264
265 void Camera::SetFarClippingPlane(float farClippingPlane)
266 {
267   mFarClippingPlane     = farClippingPlane;
268   mUpdateProjectionFlag = UPDATE_COUNT;
269 }
270
271 void Camera::SetTargetPosition(const Vector3& targetPosition)
272 {
273   mTargetPosition = targetPosition;
274   mUpdateViewFlag = UPDATE_COUNT;
275 }
276
277 void VectorReflectedByPlane(Vector4& out, Vector4& in, Vector4& plane)
278 {
279   float d = float(2.0) * plane.Dot(in);
280   out.x   = static_cast<float>(in.x - plane.x * d);
281   out.y   = static_cast<float>(in.y - plane.y * d);
282   out.z   = static_cast<float>(in.z - plane.z * d);
283   out.w   = static_cast<float>(in.w - plane.w * d);
284 }
285
286 void Camera::AdjustNearPlaneForPerspective(Matrix& perspective, const Vector4& clipPlane)
287 {
288   Vector4 q;
289   float*  v = perspective.AsFloat();
290
291   q.x = (Sign(clipPlane.x) + v[8]) / v[0];
292   q.y = (Sign(clipPlane.y) + v[9]) / v[5];
293   q.z = -1.0f;
294   q.w = (1.0f + v[10]) / v[14];
295
296   // Calculate the scaled plane vector
297   Vector4 c = clipPlane * (REFLECTION_NORMALIZED_DEVICE_COORDINATE_PARAMETER_A / q.Dot(clipPlane));
298
299   // Replace the third row of the projection v
300   v[2]  = c.x;
301   v[6]  = c.y;
302   v[10] = c.z + REFLECTION_NORMALIZED_DEVICE_COORDINATE_PARAMETER_D;
303   v[14] = c.w;
304 }
305
306 void Camera::SetReflectByPlane(const Vector4& plane)
307 {
308   float* v    = mReflectionMtx.AsFloat();
309   float  _2ab = -2.0f * plane.x * plane.y;
310   float  _2ac = -2.0f * plane.x * plane.z;
311   float  _2bc = -2.0f * plane.y * plane.z;
312
313   v[0] = 1.0f - 2.0f * plane.x * plane.x;
314   v[1] = _2ab;
315   v[2] = _2ac;
316   v[3] = 0.0f;
317
318   v[4] = _2ab;
319   v[5] = 1.0f - 2.0f * plane.y * plane.y;
320   v[6] = _2bc;
321   v[7] = 0.0f;
322
323   v[8]  = _2ac;
324   v[9]  = _2bc;
325   v[10] = 1.0f - 2.0f * plane.z * plane.z;
326   v[11] = 0.0f;
327
328   v[12] = -2 * plane.x * plane.w;
329   v[13] = -2 * plane.y * plane.w;
330   v[14] = -2 * plane.z * plane.w;
331   v[15] = 1.0f;
332
333   mUseReflection   = true;
334   mReflectionPlane = plane;
335   mUpdateViewFlag  = UPDATE_COUNT;
336 }
337
338 void Camera::RotateProjection(int rotationAngle)
339 {
340   mProjectionRotation   = rotationAngle;
341   mUpdateProjectionFlag = UPDATE_COUNT;
342 }
343
344 const Matrix& Camera::GetProjectionMatrix(BufferIndex bufferIndex) const
345 {
346   return mProjectionMatrix[bufferIndex];
347 }
348
349 const Matrix& Camera::GetViewMatrix(BufferIndex bufferIndex) const
350 {
351   return mViewMatrix[bufferIndex];
352 }
353
354 const Matrix& Camera::GetInverseViewProjectionMatrix(BufferIndex bufferIndex) const
355 {
356   return mInverseViewProjection[bufferIndex];
357 }
358
359 const Matrix& Camera::GetFinalProjectionMatrix(BufferIndex bufferIndex) const
360 {
361   return mFinalProjection[bufferIndex];
362 }
363
364 const PropertyBase* Camera::GetFieldOfView() const
365 {
366   return &mFieldOfView;
367 }
368
369 const PropertyBase* Camera::GetOrthographicSize() const
370 {
371   return &mOrthographicSize;
372 }
373
374 const PropertyBase* Camera::GetAspectRatio() const
375 {
376   return &mAspectRatio;
377 }
378
379 const PropertyInputImpl* Camera::GetProjectionMatrix() const
380 {
381   return &mProjectionMatrix;
382 }
383
384 const PropertyInputImpl* Camera::GetViewMatrix() const
385 {
386   return &mViewMatrix;
387 }
388
389 void Camera::Update(BufferIndex updateBufferIndex)
390 {
391   // if this has changes in world position we need to update camera for next 2 frames
392   if(IsLocalMatrixDirty())
393   {
394     mUpdateViewFlag = UPDATE_COUNT;
395   }
396   if(GetDirtyFlags() & NodePropertyFlags::VISIBLE)
397   {
398     // If the visibility changes, the projection matrix needs to be re-calculated.
399     // It may happen the first time an actor is rendered it's rendered only once and becomes invisible,
400     // in the following update the node will be skipped leaving the projection matrix (double buffered)
401     // with the Identity.
402     mUpdateProjectionFlag = UPDATE_COUNT;
403   }
404
405   // If projection matrix relative properties are animated now, flag change.
406   if(IsProjectionMatrixAnimated())
407   {
408     mUpdateProjectionFlag = UPDATE_COUNT;
409   }
410
411   // if either matrix changed, we need to recalculate the inverse matrix for hit testing to work
412   uint32_t viewUpdateCount       = UpdateViewMatrix(updateBufferIndex);
413   uint32_t projectionUpdateCount = UpdateProjection(updateBufferIndex);
414
415   // if model or view matrix changed we need to either recalculate the inverse VP or copy previous
416   if(viewUpdateCount > COPY_PREVIOUS_MATRIX || projectionUpdateCount > COPY_PREVIOUS_MATRIX)
417   {
418     // either has actually changed so recalculate
419     MatrixUtils::Multiply(mInverseViewProjection[updateBufferIndex], mViewMatrix[updateBufferIndex], mProjectionMatrix[updateBufferIndex]);
420     UpdateFrustum(updateBufferIndex);
421
422     // ignore the error, if the view projection is incorrect (non inversible) then you will have tough times anyways
423     static_cast<void>(mInverseViewProjection[updateBufferIndex].Invert());
424   }
425   else if(viewUpdateCount == COPY_PREVIOUS_MATRIX || projectionUpdateCount == COPY_PREVIOUS_MATRIX)
426   {
427     // neither has actually changed, but we might copied previous frames value so need to
428     // copy the previous inverse and frustum as well
429     mInverseViewProjection[updateBufferIndex] = mInverseViewProjection[updateBufferIndex ? 0 : 1];
430     mFrustum[updateBufferIndex]               = mFrustum[updateBufferIndex ? 0 : 1];
431   }
432 }
433
434 bool Camera::ViewMatrixUpdated() const
435 {
436   return 0u != mUpdateViewFlag;
437 }
438
439 bool Camera::IsProjectionMatrixAnimated() const
440 {
441   return !mFieldOfView.IsClean() ||
442          !mOrthographicSize.IsClean() ||
443          !mAspectRatio.IsClean();
444 }
445
446 uint32_t Camera::UpdateViewMatrix(BufferIndex updateBufferIndex)
447 {
448   uint32_t retval(mUpdateViewFlag);
449   if(0u != mUpdateViewFlag)
450   {
451     if(COPY_PREVIOUS_MATRIX == mUpdateViewFlag)
452     {
453       // The projection matrix was updated in the previous frame; copy it
454       mViewMatrix.CopyPrevious(updateBufferIndex);
455     }
456     else // UPDATE_COUNT == mUpdateViewFlag
457     {
458       switch(mType)
459       {
460         // camera orientation taken from node - i.e. look in abitrary, unconstrained direction
461         case Dali::Camera::FREE_LOOK:
462         {
463           Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
464           viewMatrix         = GetWorldMatrix(updateBufferIndex);
465
466           if(mUseReflection)
467           {
468             const Matrix& owningNodeMatrix(GetWorldMatrix(updateBufferIndex));
469             Vector3       position{}, scale{};
470             Quaternion    orientation{};
471             owningNodeMatrix.GetTransformComponents(position, orientation, scale);
472             mReflectionEye     = position;
473             mUseReflectionClip = true;
474
475             Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
476             Matrix  oldViewMatrix(viewMatrix);
477             MatrixUtils::Multiply(viewMatrix, oldViewMatrix, mReflectionMtx);
478           }
479
480           viewMatrix.Invert();
481           mViewMatrix.SetDirty(updateBufferIndex);
482           break;
483         }
484
485         // camera orientation constrained to look at a target
486         case Dali::Camera::LOOK_AT_TARGET:
487         {
488           const Matrix& owningNodeMatrix(GetWorldMatrix(updateBufferIndex));
489           Vector3       position, scale;
490           Quaternion    orientation;
491           owningNodeMatrix.GetTransformComponents(position, orientation, scale);
492           Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
493
494           if(mUseReflection)
495           {
496             Vector3 up        = orientation.Rotate(Vector3::YAXIS);
497             Vector4 position4 = Vector4(position);
498             Vector4 target4   = Vector4(mTargetPosition);
499             Vector4 up4       = Vector4(up);
500             Vector4 positionNew;
501             Vector4 targetNew;
502             Vector4 upNew;
503             Vector3 positionNew3;
504             Vector3 targetNewVector3;
505             Vector3 upNew3;
506
507             // eye
508             VectorReflectedByPlane(positionNew, position4, mReflectionPlane);
509             VectorReflectedByPlane(targetNew, target4, mReflectionPlane);
510             VectorReflectedByPlane(upNew, up4, mReflectionPlane);
511
512             positionNew3     = Vector3(positionNew);
513             targetNewVector3 = Vector3(targetNew);
514             upNew3           = Vector3(upNew);
515             LookAt(viewMatrix, positionNew3, targetNewVector3, upNew3);
516
517             Matrix oldViewMatrix(viewMatrix);
518             Matrix tmp;
519             tmp.SetIdentityAndScale(Vector3(-1.0, 1.0, 1.0));
520             MatrixUtils::Multiply(viewMatrix, oldViewMatrix, tmp);
521
522             mReflectionEye     = positionNew;
523             mUseReflectionClip = true;
524           }
525           else
526           {
527             LookAt(viewMatrix, position, mTargetPosition, orientation.Rotate(Vector3::YAXIS));
528           }
529           mViewMatrix.SetDirty(updateBufferIndex);
530           break;
531         }
532       }
533     }
534     --mUpdateViewFlag;
535   }
536   return retval;
537 }
538
539 void Camera::UpdateFrustum(BufferIndex updateBufferIndex, bool normalize)
540 {
541   // Extract the clip matrix planes
542   Matrix clipMatrix;
543   MatrixUtils::Multiply(clipMatrix, mViewMatrix[updateBufferIndex], mProjectionMatrix[updateBufferIndex]);
544
545   const float*   cm     = clipMatrix.AsFloat();
546   FrustumPlanes& planes = mFrustum[updateBufferIndex];
547
548   // Left
549   planes.mPlanes[0].mNormal.x = cm[3] + cm[0]; // column 4 + column 1
550   planes.mPlanes[0].mNormal.y = cm[7] + cm[4];
551   planes.mPlanes[0].mNormal.z = cm[11] + cm[8];
552   planes.mPlanes[0].mDistance = cm[15] + cm[12];
553
554   // Right
555   planes.mPlanes[1].mNormal.x = cm[3] - cm[0]; // column 4 - column 1
556   planes.mPlanes[1].mNormal.y = cm[7] - cm[4];
557   planes.mPlanes[1].mNormal.z = cm[11] - cm[8];
558   planes.mPlanes[1].mDistance = cm[15] - cm[12];
559
560   // Bottom
561   planes.mPlanes[2].mNormal.x = cm[3] + cm[1]; // column 4 + column 2
562   planes.mPlanes[2].mNormal.y = cm[7] + cm[5];
563   planes.mPlanes[2].mNormal.z = cm[11] + cm[9];
564   planes.mPlanes[2].mDistance = cm[15] + cm[13];
565
566   // Top
567   planes.mPlanes[3].mNormal.x = cm[3] - cm[1]; // column 4 - column 2
568   planes.mPlanes[3].mNormal.y = cm[7] - cm[5];
569   planes.mPlanes[3].mNormal.z = cm[11] - cm[9];
570   planes.mPlanes[3].mDistance = cm[15] - cm[13];
571
572   // Near
573   planes.mPlanes[4].mNormal.x = cm[3] + cm[2]; // column 4 + column 3
574   planes.mPlanes[4].mNormal.y = cm[7] + cm[6];
575   planes.mPlanes[4].mNormal.z = cm[11] + cm[10];
576   planes.mPlanes[4].mDistance = cm[15] + cm[14];
577
578   // Far
579   planes.mPlanes[5].mNormal.x = cm[3] - cm[2]; // column 4 - column 3
580   planes.mPlanes[5].mNormal.y = cm[7] - cm[6];
581   planes.mPlanes[5].mNormal.z = cm[11] - cm[10];
582   planes.mPlanes[5].mDistance = cm[15] - cm[14];
583
584   if(normalize)
585   {
586     for(uint32_t i = 0; i < 6; ++i)
587     {
588       // Normalize planes to ensure correct bounding distance checking
589       Plane& plane = planes.mPlanes[i];
590       float  l     = 1.0f / plane.mNormal.Length();
591       plane.mNormal *= l;
592       plane.mDistance *= l;
593
594       planes.mSign[i] = Vector3(Sign(plane.mNormal.x), Sign(plane.mNormal.y), Sign(plane.mNormal.z));
595     }
596   }
597   else
598   {
599     for(uint32_t i = 0; i < 6; ++i)
600     {
601       planes.mSign[i] = Vector3(Sign(planes.mPlanes[i].mNormal.x), Sign(planes.mPlanes[i].mNormal.y), Sign(planes.mPlanes[i].mNormal.z));
602     }
603   }
604   mFrustum[updateBufferIndex ? 0 : 1] = planes;
605 }
606
607 bool Camera::CheckSphereInFrustum(BufferIndex bufferIndex, const Vector3& origin, float radius) const
608 {
609   const FrustumPlanes& planes = mFrustum[bufferIndex];
610   for(uint32_t i = 0; i < 6; ++i)
611   {
612     if((planes.mPlanes[i].mDistance + planes.mPlanes[i].mNormal.Dot(origin)) < -radius)
613     {
614       return false;
615     }
616   }
617   return true;
618 }
619
620 bool Camera::CheckAABBInFrustum(BufferIndex bufferIndex, const Vector3& origin, const Vector3& halfExtents) const
621 {
622   const FrustumPlanes& planes = mFrustum[bufferIndex];
623   for(uint32_t i = 0; i < 6; ++i)
624   {
625     if(planes.mPlanes[i].mNormal.Dot(origin + (halfExtents * planes.mSign[i])) > -(planes.mPlanes[i].mDistance))
626     {
627       continue;
628     }
629
630     return false;
631   }
632   return true;
633 }
634 Dali::Rect<int32_t> Camera::GetOrthographicClippingBox(BufferIndex bufferIndex) const
635 {
636   const float orthographicSize = mOrthographicSize[bufferIndex];
637   const float aspect           = mAspectRatio[bufferIndex];
638
639   const float halfWidth  = mProjectionDirection == DevelCameraActor::ProjectionDirection::VERTICAL ? orthographicSize * aspect : orthographicSize;
640   const float halfHeight = mProjectionDirection == DevelCameraActor::ProjectionDirection::VERTICAL ? orthographicSize : orthographicSize / aspect;
641
642   return Dali::Rect<int32_t>(-halfWidth, -halfHeight, halfWidth * 2.0f, halfHeight * 2.0f);
643 }
644
645 uint32_t Camera::UpdateProjection(BufferIndex updateBufferIndex)
646 {
647   uint32_t retval(mUpdateProjectionFlag);
648   // Early-exit if no update required
649   if(0u != mUpdateProjectionFlag)
650   {
651     Matrix& finalProjection = mFinalProjection[updateBufferIndex];
652     finalProjection.SetIdentity();
653
654     if(COPY_PREVIOUS_MATRIX == mUpdateProjectionFlag)
655     {
656       // The projection matrix was updated in the previous frame; copy it
657       mProjectionMatrix.CopyPrevious(updateBufferIndex);
658
659       finalProjection = mFinalProjection[updateBufferIndex ? 0 : 1];
660     }
661     else // UPDATE_COUNT == mUpdateProjectionFlag
662     {
663       switch(mProjectionMode)
664       {
665         case Dali::Camera::PERSPECTIVE_PROJECTION:
666         {
667           Matrix& projectionMatrix = mProjectionMatrix.Get(updateBufferIndex);
668           Perspective(projectionMatrix,
669                       mProjectionDirection,
670                       mFieldOfView[updateBufferIndex],
671                       mAspectRatio[updateBufferIndex],
672                       mNearClippingPlane,
673                       mFarClippingPlane,
674                       mInvertYAxis);
675
676           //need to apply custom clipping plane
677           if(mUseReflectionClip)
678           {
679             Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
680             Matrix  viewInv    = viewMatrix;
681             viewInv.Invert();
682             viewInv.Transpose();
683
684             Dali::Vector4 adjReflectPlane = mReflectionPlane;
685             float         d               = mReflectionPlane.Dot(mReflectionEye);
686             if(d < 0)
687             {
688               adjReflectPlane.w = -adjReflectPlane.w;
689             }
690
691             Vector4 customClipping = viewInv * adjReflectPlane;
692             AdjustNearPlaneForPerspective(projectionMatrix, customClipping);
693
694             // Invert Z
695             Matrix matZ;
696             matZ.SetIdentity();
697             float* vZ = matZ.AsFloat();
698             vZ[10]    = -vZ[10];
699             MatrixUtils::Multiply(projectionMatrix, projectionMatrix, matZ);
700           }
701           break;
702         }
703         case Dali::Camera::ORTHOGRAPHIC_PROJECTION:
704         {
705           Matrix& projectionMatrix = mProjectionMatrix.Get(updateBufferIndex);
706           Orthographic(projectionMatrix,
707                        mProjectionDirection,
708                        mOrthographicSize[updateBufferIndex],
709                        mAspectRatio[updateBufferIndex],
710                        mNearClippingPlane,
711                        mFarClippingPlane,
712                        mInvertYAxis);
713           break;
714         }
715       }
716
717       mProjectionMatrix.SetDirty(updateBufferIndex);
718
719       Quaternion rotationAngle;
720       switch(mProjectionRotation)
721       {
722         case 90:
723         {
724           rotationAngle = Quaternion(Dali::ANGLE_90, Vector3::ZAXIS);
725           break;
726         }
727         case 180:
728         {
729           rotationAngle = Quaternion(Dali::ANGLE_180, Vector3::ZAXIS);
730           break;
731         }
732         case 270:
733         {
734           rotationAngle = Quaternion(Dali::ANGLE_270, Vector3::ZAXIS);
735           break;
736         }
737         default:
738           rotationAngle = Quaternion(Dali::ANGLE_0, Vector3::ZAXIS);
739           break;
740       }
741
742       Matrix rotation;
743       rotation.SetIdentity();
744       rotation.SetTransformComponents(Vector3(1.0f, 1.0f, 1.0f), rotationAngle, Vector3(0.0f, 0.0f, 0.0f));
745
746       MatrixUtils::Multiply(finalProjection, mProjectionMatrix.Get(updateBufferIndex), rotation);
747     }
748     --mUpdateProjectionFlag;
749   }
750   return retval;
751 }
752
753 } // namespace SceneGraph
754
755 } // namespace Internal
756
757 } // namespace Dali