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