[dali_2.3.32] Merge branch 'devel/master'
[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 #include <dali/internal/update/common/property-resetter.h>
33 #include <dali/internal/update/common/resetter-manager.h> ///< For AddInitializeResetter
34
35 namespace // unnamed namespace
36 {
37 const uint32_t UPDATE_COUNT         = 2u; // Update projection or view matrix this many frames after a change
38 const uint32_t COPY_PREVIOUS_MATRIX = 1u; // Copy view or projection matrix from previous frame
39
40 //For reflection and clipping plane
41 const float REFLECTION_NORMALIZED_DEVICE_COORDINATE_PARAMETER_A = 2.0f;
42 const float REFLECTION_NORMALIZED_DEVICE_COORDINATE_PARAMETER_D = 1.0f;
43 } // namespace
44
45 namespace Dali
46 {
47 namespace Internal
48 {
49 namespace SceneGraph
50 {
51 namespace
52 {
53 //Memory pool used to allocate new camera. Memory used by this pool will be released when shutting down DALi
54 MemoryPoolObjectAllocator<Camera>& GetCameraMemoryPool()
55 {
56   static MemoryPoolObjectAllocator<Camera> gCameraMemoryPool;
57   return gCameraMemoryPool;
58 }
59
60 template<typename T>
61 T Sign(T value)
62 {
63   return T(T(0) < value) - T(value < T(0));
64 }
65
66 void LookAt(Matrix& result, const Vector3& eye, const Vector3& target, const Vector3& up)
67 {
68   Vector3 vZ = target - eye;
69   vZ.Normalize();
70
71   Vector3 vX = up.Cross(vZ);
72   vX.Normalize();
73
74   Vector3 vY = vZ.Cross(vX);
75   vY.Normalize();
76
77   result.SetInverseTransformComponents(vX, vY, vZ, eye);
78 }
79
80 void Frustum(Matrix& result, float left, float right, float bottom, float top, float near, float far, bool invertYAxis)
81 {
82   float deltaZ = far - near;
83   if((near <= 0.0f) || (far <= 0.0f) || Equals(right, left) || Equals(bottom, top) || (deltaZ <= 0.0f))
84   {
85     DALI_LOG_ERROR("Invalid parameters passed into Frustum!\n");
86     DALI_ASSERT_DEBUG("Invalid parameters passed into Frustum!");
87     return;
88   }
89
90   float deltaX = right - left;
91   float deltaY = invertYAxis ? bottom - top : top - bottom;
92
93   result.SetIdentity();
94
95   float* m = result.AsFloat();
96   m[0]     = -2.0f * near / deltaX;
97   m[1] = m[2] = m[3] = 0.0f;
98
99   m[5] = -2.0f * near / deltaY;
100   m[4] = m[6] = m[7] = 0.0f;
101
102   m[8]  = (right + left) / deltaX;
103   m[9]  = (top + bottom) / deltaY;
104   m[10] = (near + far) / deltaZ;
105   m[11] = 1.0f;
106
107   m[14] = -2.0f * near * far / deltaZ;
108   m[12] = m[13] = m[15] = 0.0f;
109 }
110
111 void Perspective(Matrix& result, Dali::DevelCameraActor::ProjectionDirection fovDir, float fov, float aspect, float near, float far, bool invertYAxis)
112 {
113   float frustumH;
114   float frustumW;
115   if(fovDir == Dali::DevelCameraActor::ProjectionDirection::VERTICAL)
116   {
117     frustumH = tanf(fov * 0.5f) * near;
118     frustumW = frustumH * aspect;
119   }
120   else
121   {
122     frustumW = tanf(fov * 0.5f) * near;
123     frustumH = frustumW / aspect;
124   }
125
126   Frustum(result, -frustumW, frustumW, -frustumH, frustumH, near, far, invertYAxis);
127 }
128
129 void Orthographic(Matrix& result, Dali::DevelCameraActor::ProjectionDirection orthographicDir, float orthographicSize, float aspect, float near, float far, bool invertYAxis)
130 {
131   if(EqualsZero(orthographicSize) || EqualsZero(aspect) || Equals(far, near))
132   {
133     DALI_LOG_ERROR("Cannot create orthographic projection matrix with a zero dimension.\n");
134     DALI_ASSERT_DEBUG("Cannot create orthographic projection matrix with a zero dimension.");
135     return;
136   }
137
138   float halfDeltaX;
139   float halfDeltaY;
140   if(orthographicDir == Dali::DevelCameraActor::ProjectionDirection::VERTICAL)
141   {
142     halfDeltaY = orthographicSize;
143     halfDeltaX = halfDeltaY * aspect;
144   }
145   else
146   {
147     halfDeltaX = orthographicSize;
148     halfDeltaY = halfDeltaX / aspect;
149   }
150
151   float deltaZ = far - near;
152
153   float* m = result.AsFloat();
154
155   m[0] = -1.0f / halfDeltaX;
156   m[1] = 0.0f;
157   m[2] = 0.0f;
158   m[3] = 0.0f;
159
160   m[4] = 0.0f;
161   m[5] = (invertYAxis ? 1.0f : -1.0f) / halfDeltaY;
162   m[6] = 0.0f;
163   m[7] = 0.0f;
164
165   m[8]  = 0.0f;
166   m[9]  = 0.0f;
167   m[10] = 2.0f / deltaZ;
168   m[11] = 0.0f;
169
170   m[12] = 0.0f;
171   m[13] = 0.0f;
172   m[14] = -(near + far) / deltaZ;
173   m[15] = 1.0f;
174 }
175
176 /**
177  * Adjust near plane for reflection
178  * @param[in] perspective Perspective matrix
179  * @param[in] clipPlane Clipping plane
180  * @param[in] far Far plane distance of original projection matrix
181  */
182 void AdjustNearPlaneForPerspective(Matrix& perspective, const Vector4& clipPlane, float far)
183 {
184   // Make third row of perspective projection matrix as clipPlane.
185   // If me let third row vector as v = (v[2], v[6], v[10], v[14]),
186   // z_n = v * (x, y, z, 1) / z
187
188   // For example, standard case : -1 for near, 1 for far.
189   // v.z * n + v.w = -n
190   // v.z * f + v.w = f
191   // This formular makes v.z = (f + n) / (f - n), v.w = -2fn / (f - n)
192
193   // Now, we should make like this : -1 for clipPlane, 1 for farPlane.
194   // Let we think some point p : c.x * p.x + c.y * p.y + c.z * p.z + c.w = 0.
195   // v.x * p.x + v.y * p.y + v.z * p.z + v.w = -p.z;
196
197   // Since point p doesn't have any special rule, we can think that
198   // (v.x, v.y, v.z + 1, v.w) = scale * (c.x, c.y, c.z, c.w).
199   // -->
200   // v.z = scale * c.z - 1.0,
201   // v.w = scale * c.w.
202
203   // Now we have to determine scale value.
204
205   // Reference of Far plane fomular : https://ubm-twvideo01.s3.amazonaws.com/o1/vault/gdc07/slides/S3730i1.pdf page 38
206   // Let we pick 'one of any edge' point Q which position on original projection frustum's far plane and...
207   // c.x * Q.x + c.y * Q.y + c.z * Q.z + c.w is maximum.
208
209   // To make Q far far away, Below fomular should be applied. (We can assume that Q.z is bigger than 0)
210   // || (v[0], 0, v[8], 0) * Q / Q.z || = 1 --> || v[0] * Q.x + v[8] * Q.z || = Q.z
211   // || (0, v[5], v[9], 0) * Q / Q.z || = 1 --> || v[5] * Q.y + v[9] * Q.z || = Q.z
212
213   // And the far plane case
214   // v * Q = Q.z
215   // --> (c * scale + (0, 0, -1, 0)) * Q = Q.z
216   // --> c * scale * Q = 2.0 * Q.z
217   // --> scale = 2.0 * Q.z / (c * Q)
218
219   float* v = perspective.AsFloat();
220
221   float maximalCDotQ = Math::MACHINE_EPSILON_0; // We should find CDotQ is positive.
222
223   float inverseV0 = 1.0f / v[0];
224   float inverseV5 = 1.0f / v[5];
225
226   // There are 4 case of solution. Choose one of them and check whether clipPlane * Q is maxium.
227   for(int testCase = 0; testCase != 4; ++testCase)
228   {
229     Vector4 Q(0.0f, 0.0f, far, 1.0f);
230
231     // Check for Q.x
232     // v[0] * Q.x = (+-1.0f - v[8]) * Q.z
233     Q.x = (((testCase & 1) ? 1.0f : -1.0f) - v[8]) * Q.z * inverseV0;
234     // v[5] * Q.y = (+-1.0f - v[9]) * Q.z
235     Q.y = (((testCase & 2) ? 1.0f : -1.0f) - v[9]) * Q.z * inverseV5;
236
237     maximalCDotQ = std::max(maximalCDotQ, clipPlane.Dot(Q));
238   }
239
240   float scale = 2.0f * far / maximalCDotQ;
241
242   Vector4 scaledPlaneVector = clipPlane * scale;
243
244   v[2]  = scaledPlaneVector.x;
245   v[6]  = scaledPlaneVector.y;
246   v[10] = scaledPlaneVector.z - 1.0f;
247   v[14] = scaledPlaneVector.w;
248 }
249
250 /**
251  * Adjust near plane for reflection
252  * @param[in] orthographic Orthographic matrix
253  * @param[in] clipPlane Clipping plane
254  * @param[in] far Far plane distance of original projection matrix
255  */
256 void AdjustNearPlaneForOrthographic(Matrix& orthographic, const Vector4& clipPlane, float far)
257 {
258   // Make third row of orthographic projection matrix as clipPlane.
259   // If me let third row vector as v = (v[2], v[6], v[10], v[14]),
260   // z_n = v * (x, y, z, 1)
261
262   // For example, standard case : -1 for near, 1 for far.
263   // v.z * n + v.w = -1
264   // v.z * f + v.w = 1
265   // This formular makes v.z = 2 / (f - n), v.w = -(f + n) / (f - n)
266
267   // Now, we should make like this : -1 for clipPlane, 1 for farPlane.
268   // Let we think some point p : c.x * p.x + c.y * p.y + c.z * p.z + c.w = 0.
269   // v.x * p.x + v.y * p.y + v.z * p.z + v.w = -1;
270
271   // Since point p doesn't have any special rule, we can think that
272   // (v.x, v.y, v.z, v.w + 1) = scale * (c.x, c.y, c.z, c.w).
273   // -->
274   // v.z = scale * c.z,
275   // v.w = scale * c.w - 1.0.
276
277   // Now we have to determine scale value.
278
279   // Reference of Far plane fomular : https://ubm-twvideo01.s3.amazonaws.com/o1/vault/gdc07/slides/S3730i1.pdf page 38
280   // Let we pick 'one of any edge' point Q which position on original projection frustum's far plane and...
281   // c.x * Q.x + c.y * Q.y + c.z * Q.z + c.w is maximum.
282
283   // To make Q far far away, Below fomular should be applied. (We can assume that Q.z is bigger than 0)
284   // || (v[0], 0, 0, v[12]) * Q || = 1 --> || v[0] * Q.x + v[12] || = 1
285   // || (0, v[5], 0, v[13]) * Q || = 1 --> || v[5] * Q.y + v[13] || = 1
286
287   // And the far plane case
288   // v * Q = 1
289   // --> (c * scale + (0, 0, 0, 1)) * Q = 1
290   // --> c * scale * Q = 2.0
291   // --> scale = 2.0 / (c * Q)
292
293   float* v = orthographic.AsFloat();
294
295   float maximalCDotQ = Math::MACHINE_EPSILON_0; // We should find CDotQ is positive.
296
297   float inverseV0 = 1.0f / v[0];
298   float inverseV5 = 1.0f / v[5];
299
300   // There are 4 case of solution. Choose one of them and check whether clipPlane * Q is maxium.
301   for(int testCase = 0; testCase != 4; ++testCase)
302   {
303     Vector4 Q(0.0f, 0.0f, far, 1.0f);
304
305     // Check for Q.x
306     // v[0] * Q.x = (+-1.0f - v[12])
307     Q.x = (((testCase & 1) ? 1.0f : -1.0f) - v[12]) * inverseV0;
308     // v[5] * Q.y = (+-1.0f - v[13])
309     Q.y = (((testCase & 2) ? 1.0f : -1.0f) - v[13]) * inverseV5;
310
311     maximalCDotQ = std::max(maximalCDotQ, clipPlane.Dot(Q));
312   }
313
314   float scale = 2.0f / maximalCDotQ;
315
316   Vector4 scaledPlaneVector = clipPlane * scale;
317
318   v[2]  = scaledPlaneVector.x;
319   v[6]  = scaledPlaneVector.y;
320   v[10] = scaledPlaneVector.z;
321   v[14] = scaledPlaneVector.w - 1.0f;
322 }
323
324 } // unnamed namespace
325
326 const Dali::Camera::Type                          Camera::DEFAULT_TYPE(Dali::Camera::FREE_LOOK);
327 const Dali::Camera::ProjectionMode                Camera::DEFAULT_MODE(Dali::Camera::PERSPECTIVE_PROJECTION);
328 const Dali::DevelCameraActor::ProjectionDirection Camera::DEFAULT_PROJECTION_DIRECTION(Dali::DevelCameraActor::VERTICAL);
329 const bool                                        Camera::DEFAULT_INVERT_Y_AXIS(false);
330 const float                                       Camera::DEFAULT_FIELD_OF_VIEW(45.0f * (Math::PI / 180.0f));
331 const float                                       Camera::DEFAULT_ORTHOGRAPHIC_SIZE(400.0f);     // half of default height of the screen
332 const float                                       Camera::DEFAULT_ASPECT_RATIO(480.0f / 800.0f); // default width / default height of the screen
333 const float                                       Camera::DEFAULT_NEAR_CLIPPING_PLANE(800.0f);   // default height of the screen
334 const float                                       Camera::DEFAULT_FAR_CLIPPING_PLANE(DEFAULT_NEAR_CLIPPING_PLANE + 2.f * DEFAULT_NEAR_CLIPPING_PLANE);
335 const Vector3                                     Camera::DEFAULT_TARGET_POSITION(0.0f, 0.0f, 0.0f);
336
337 Camera::Camera()
338 : Node(),
339   mUpdateViewFlag(UPDATE_COUNT),
340   mUpdateProjectionFlag(UPDATE_COUNT),
341   mProjectionRotation(0),
342   mType(DEFAULT_TYPE),
343   mProjectionMode(DEFAULT_MODE),
344   mProjectionDirection(DEFAULT_PROJECTION_DIRECTION),
345   mInvertYAxis(DEFAULT_INVERT_Y_AXIS),
346   mFieldOfView(DEFAULT_FIELD_OF_VIEW),
347   mOrthographicSize(DEFAULT_ORTHOGRAPHIC_SIZE),
348   mAspectRatio(DEFAULT_ASPECT_RATIO),
349   mNearClippingPlane(DEFAULT_NEAR_CLIPPING_PLANE),
350   mFarClippingPlane(DEFAULT_FAR_CLIPPING_PLANE),
351   mTargetPosition(DEFAULT_TARGET_POSITION),
352   mViewMatrix(),
353   mProjectionMatrix(),
354   mInverseViewProjection(Matrix::IDENTITY),
355   mFinalProjection(Matrix::IDENTITY)
356 {
357   // set a flag the node to say this is a camera
358   mIsCamera = true;
359 }
360
361 Camera* Camera::New()
362 {
363   return new(GetCameraMemoryPool().AllocateRawThreadSafe()) Camera();
364 }
365
366 Camera::~Camera() = default;
367
368 void Camera::operator delete(void* ptr)
369 {
370   GetCameraMemoryPool().FreeThreadSafe(static_cast<Camera*>(ptr));
371 }
372
373 void Camera::SetType(Dali::Camera::Type type)
374 {
375   mType = type;
376 }
377
378 void Camera::SetProjectionMode(Dali::Camera::ProjectionMode mode)
379 {
380   mProjectionMode.Bake(0, mode);
381   mUpdateProjectionFlag = UPDATE_COUNT;
382 }
383
384 void Camera::SetProjectionDirection(Dali::DevelCameraActor::ProjectionDirection direction)
385 {
386   mProjectionDirection.Bake(0, direction);
387   mUpdateProjectionFlag = UPDATE_COUNT;
388 }
389
390 void Camera::SetInvertYAxis(bool invertYAxis)
391 {
392   mInvertYAxis.Bake(0, invertYAxis);
393   mUpdateProjectionFlag = UPDATE_COUNT;
394 }
395
396 void Camera::BakeFieldOfView(BufferIndex updateBufferIndex, float fieldOfView)
397 {
398   mFieldOfView.Bake(updateBufferIndex, fieldOfView);
399   mUpdateProjectionFlag = UPDATE_COUNT;
400 }
401
402 void Camera::BakeOrthographicSize(BufferIndex updateBufferIndex, float orthographicSize)
403 {
404   mOrthographicSize.Bake(updateBufferIndex, orthographicSize);
405   mUpdateProjectionFlag = UPDATE_COUNT;
406 }
407
408 void Camera::BakeAspectRatio(BufferIndex updateBufferIndex, float aspectRatio)
409 {
410   mAspectRatio.Bake(updateBufferIndex, aspectRatio);
411   mUpdateProjectionFlag = UPDATE_COUNT;
412 }
413
414 void Camera::BakeNearClippingPlane(BufferIndex updateBufferIndex, float nearClippingPlane)
415 {
416   mNearClippingPlane.Bake(updateBufferIndex, nearClippingPlane);
417   mUpdateProjectionFlag = UPDATE_COUNT;
418 }
419
420 void Camera::BakeFarClippingPlane(BufferIndex updateBufferIndex, float farClippingPlane)
421 {
422   mFarClippingPlane.Bake(updateBufferIndex, farClippingPlane);
423   mUpdateProjectionFlag = UPDATE_COUNT;
424 }
425
426 void Camera::SetTargetPosition(const Vector3& targetPosition)
427 {
428   mTargetPosition = targetPosition;
429   mUpdateViewFlag = UPDATE_COUNT;
430 }
431
432 void VectorReflectedByPlane(Vector4& out, Vector4& in, Vector4& plane)
433 {
434   float d = float(2.0) * plane.Dot(in);
435   out.x   = static_cast<float>(in.x - plane.x * d);
436   out.y   = static_cast<float>(in.y - plane.y * d);
437   out.z   = static_cast<float>(in.z - plane.z * d);
438   out.w   = static_cast<float>(in.w - plane.w * d);
439 }
440
441 void Camera::SetReflectByPlane(const Vector4& plane)
442 {
443   // Note :  we assume that plane.xyz is normal vector.
444
445   float* v    = mReflectionMtx.AsFloat();
446   float  _2ab = -2.0f * plane.x * plane.y;
447   float  _2ac = -2.0f * plane.x * plane.z;
448   float  _2bc = -2.0f * plane.y * plane.z;
449
450   v[0] = 1.0f - 2.0f * plane.x * plane.x;
451   v[1] = _2ab;
452   v[2] = _2ac;
453   v[3] = 0.0f;
454
455   v[4] = _2ab;
456   v[5] = 1.0f - 2.0f * plane.y * plane.y;
457   v[6] = _2bc;
458   v[7] = 0.0f;
459
460   v[8]  = _2ac;
461   v[9]  = _2bc;
462   v[10] = 1.0f - 2.0f * plane.z * plane.z;
463   v[11] = 0.0f;
464
465   v[12] = -2 * plane.x * plane.w;
466   v[13] = -2 * plane.y * plane.w;
467   v[14] = -2 * plane.z * plane.w;
468   v[15] = 1.0f;
469
470   mUseReflection   = true;
471   mReflectionPlane = plane;
472   mUpdateViewFlag  = UPDATE_COUNT;
473 }
474
475 void Camera::RotateProjection(int rotationAngle)
476 {
477   mProjectionRotation   = rotationAngle;
478   mUpdateProjectionFlag = UPDATE_COUNT;
479 }
480
481 const Matrix& Camera::GetProjectionMatrix(BufferIndex bufferIndex) const
482 {
483   return mProjectionMatrix[bufferIndex];
484 }
485
486 const Matrix& Camera::GetViewMatrix(BufferIndex bufferIndex) const
487 {
488   return mViewMatrix[bufferIndex];
489 }
490
491 const Matrix& Camera::GetInverseViewProjectionMatrix(BufferIndex bufferIndex) const
492 {
493   return mInverseViewProjection[bufferIndex];
494 }
495
496 const Matrix& Camera::GetFinalProjectionMatrix(BufferIndex bufferIndex) const
497 {
498   return mFinalProjection[bufferIndex];
499 }
500
501 const PropertyBase* Camera::GetProjectionMode() const
502 {
503   return &mProjectionMode;
504 }
505
506 const PropertyBase* Camera::GetFieldOfView() const
507 {
508   return &mFieldOfView;
509 }
510
511 const PropertyBase* Camera::GetAspectRatio() const
512 {
513   return &mAspectRatio;
514 }
515
516 const PropertyBase* Camera::GetNearPlaneDistance() const
517 {
518   return &mNearClippingPlane;
519 }
520
521 const PropertyBase* Camera::GetFarPlaneDistance() const
522 {
523   return &mFarClippingPlane;
524 }
525
526 const PropertyInputImpl* Camera::GetProjectionMatrix() const
527 {
528   return &mProjectionMatrix;
529 }
530
531 const PropertyInputImpl* Camera::GetViewMatrix() const
532 {
533   return &mViewMatrix;
534 }
535
536 const PropertyBase* Camera::GetInvertYAxis() const
537 {
538   return &mInvertYAxis;
539 }
540
541 const PropertyBase* Camera::GetOrthographicSize() const
542 {
543   return &mOrthographicSize;
544 }
545
546 const PropertyBase* Camera::GetProjectionDirection() const
547 {
548   return &mProjectionDirection;
549 }
550
551 void Camera::Update(BufferIndex updateBufferIndex)
552 {
553   // if this has changes in world position we need to update camera for next 2 frames
554   if(IsLocalMatrixDirty())
555   {
556     mUpdateViewFlag = UPDATE_COUNT;
557   }
558   if(GetDirtyFlags() & NodePropertyFlags::VISIBLE)
559   {
560     // If the visibility changes, the projection matrix needs to be re-calculated.
561     // It may happen the first time an actor is rendered it's rendered only once and becomes invisible,
562     // in the following update the node will be skipped leaving the projection matrix (double buffered)
563     // with the Identity.
564     mUpdateProjectionFlag = UPDATE_COUNT;
565   }
566
567   // If projection matrix relative properties are animated now, flag change.
568   if(IsProjectionMatrixAnimated())
569   {
570     mUpdateProjectionFlag = UPDATE_COUNT;
571   }
572
573   // if either matrix changed, we need to recalculate the inverse matrix for hit testing to work
574   uint32_t viewUpdateCount       = UpdateViewMatrix(updateBufferIndex);
575   uint32_t projectionUpdateCount = UpdateProjection(updateBufferIndex);
576
577   // if model or view matrix changed we need to either recalculate the inverse VP or copy previous
578   if(viewUpdateCount > COPY_PREVIOUS_MATRIX || projectionUpdateCount > COPY_PREVIOUS_MATRIX)
579   {
580     // either has actually changed so recalculate
581     MatrixUtils::MultiplyProjectionMatrix(mInverseViewProjection[updateBufferIndex], mViewMatrix[updateBufferIndex], mProjectionMatrix[updateBufferIndex]);
582     UpdateFrustum(updateBufferIndex);
583
584     // ignore the error, if the view projection is incorrect (non inversible) then you will have tough times anyways
585     static_cast<void>(mInverseViewProjection[updateBufferIndex].Invert());
586   }
587   else if(viewUpdateCount == COPY_PREVIOUS_MATRIX || projectionUpdateCount == COPY_PREVIOUS_MATRIX)
588   {
589     // neither has actually changed, but we might copied previous frames value so need to
590     // copy the previous inverse and frustum as well
591     mInverseViewProjection[updateBufferIndex] = mInverseViewProjection[updateBufferIndex ? 0 : 1];
592     mFrustum[updateBufferIndex]               = mFrustum[updateBufferIndex ? 0 : 1];
593   }
594 }
595
596 bool Camera::ViewMatrixUpdated() const
597 {
598   return 0u != mUpdateViewFlag;
599 }
600
601 bool Camera::IsProjectionMatrixAnimated() const
602 {
603   return !mFieldOfView.IsClean() ||
604          !mOrthographicSize.IsClean() ||
605          !mAspectRatio.IsClean();
606 }
607
608 void Camera::AddInitializeResetter(ResetterManager& manager) const
609 {
610   // Call base class initialize resetter
611   Node::AddInitializeResetter(manager);
612
613   OwnerPointer<SceneGraph::PropertyResetterBase> resetterFieldOvView      = SceneGraph::BakerResetter::New(*this, mFieldOfView, SceneGraph::BakerResetter::Lifetime::BAKE);
614   OwnerPointer<SceneGraph::PropertyResetterBase> resetterOrthographicSize = SceneGraph::BakerResetter::New(*this, mOrthographicSize, SceneGraph::BakerResetter::Lifetime::BAKE);
615   OwnerPointer<SceneGraph::PropertyResetterBase> resetterAspectRatio      = SceneGraph::BakerResetter::New(*this, mAspectRatio, SceneGraph::BakerResetter::Lifetime::BAKE);
616   manager.AddPropertyResetter(resetterFieldOvView);
617   manager.AddPropertyResetter(resetterOrthographicSize);
618   manager.AddPropertyResetter(resetterAspectRatio);
619 }
620
621 uint32_t Camera::UpdateViewMatrix(BufferIndex updateBufferIndex)
622 {
623   uint32_t retval(mUpdateViewFlag);
624   if(0u != mUpdateViewFlag)
625   {
626     if(COPY_PREVIOUS_MATRIX == mUpdateViewFlag)
627     {
628       // The projection matrix was updated in the previous frame; copy it
629       mViewMatrix.CopyPrevious(updateBufferIndex);
630     }
631     else // UPDATE_COUNT == mUpdateViewFlag
632     {
633       switch(mType)
634       {
635         // camera orientation taken from node - i.e. look in abitrary, unconstrained direction
636         case Dali::Camera::FREE_LOOK:
637         {
638           Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
639           viewMatrix         = GetWorldMatrix(updateBufferIndex);
640
641           if(mUseReflection)
642           {
643             const Matrix& owningNodeMatrix(GetWorldMatrix(updateBufferIndex));
644             Vector3       position{}, scale{};
645             Quaternion    orientation{};
646             owningNodeMatrix.GetTransformComponents(position, orientation, scale);
647             mReflectionEye     = position;
648             mUseReflectionClip = true;
649
650             Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
651             Matrix  oldViewMatrix(viewMatrix);
652             MatrixUtils::MultiplyTransformMatrix(viewMatrix, oldViewMatrix, mReflectionMtx);
653           }
654
655           viewMatrix.Invert();
656           mViewMatrix.SetDirty(updateBufferIndex);
657           break;
658         }
659
660         // camera orientation constrained to look at a target
661         case Dali::Camera::LOOK_AT_TARGET:
662         {
663           const Matrix& owningNodeMatrix(GetWorldMatrix(updateBufferIndex));
664           Vector3       position, scale;
665           Quaternion    orientation;
666           owningNodeMatrix.GetTransformComponents(position, orientation, scale);
667           Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
668
669           if(mUseReflection)
670           {
671             Vector3 up        = orientation.Rotate(Vector3::YAXIS);
672             Vector4 position4 = Vector4(position);
673             Vector4 target4   = Vector4(mTargetPosition);
674             Vector4 up4       = Vector4(up);
675             Vector4 positionNew;
676             Vector4 targetNew;
677             Vector4 upNew;
678             Vector3 positionNew3;
679             Vector3 targetNewVector3;
680             Vector3 upNew3;
681
682             // eye
683             VectorReflectedByPlane(positionNew, position4, mReflectionPlane);
684             VectorReflectedByPlane(targetNew, target4, mReflectionPlane);
685             VectorReflectedByPlane(upNew, up4, mReflectionPlane);
686
687             positionNew3     = Vector3(positionNew);
688             targetNewVector3 = Vector3(targetNew);
689             upNew3           = Vector3(upNew);
690             LookAt(viewMatrix, positionNew3, targetNewVector3, upNew3);
691
692             // Invert X
693             float* vZ = viewMatrix.AsFloat();
694             vZ[0]     = -vZ[0];
695             vZ[4]     = -vZ[4];
696             vZ[8]     = -vZ[8];
697             vZ[12]    = -vZ[12];
698
699             mReflectionEye     = positionNew;
700             mUseReflectionClip = true;
701           }
702           else
703           {
704             LookAt(viewMatrix, position, mTargetPosition, orientation.Rotate(Vector3::YAXIS));
705           }
706           mViewMatrix.SetDirty(updateBufferIndex);
707           break;
708         }
709       }
710     }
711     --mUpdateViewFlag;
712   }
713   return retval;
714 }
715
716 void Camera::UpdateFrustum(BufferIndex updateBufferIndex, bool normalize)
717 {
718   // Extract the clip matrix planes
719   Matrix clipMatrix(false); // Don't initialize.
720   MatrixUtils::MultiplyProjectionMatrix(clipMatrix, mViewMatrix[updateBufferIndex], mProjectionMatrix[updateBufferIndex]);
721
722   const float*   cm     = clipMatrix.AsFloat();
723   FrustumPlanes& planes = mFrustum[updateBufferIndex];
724
725   // Left
726   planes.mPlanes[0].mNormal.x = cm[3] + cm[0]; // column 4 + column 1
727   planes.mPlanes[0].mNormal.y = cm[7] + cm[4];
728   planes.mPlanes[0].mNormal.z = cm[11] + cm[8];
729   planes.mPlanes[0].mDistance = cm[15] + cm[12];
730
731   // Right
732   planes.mPlanes[1].mNormal.x = cm[3] - cm[0]; // column 4 - column 1
733   planes.mPlanes[1].mNormal.y = cm[7] - cm[4];
734   planes.mPlanes[1].mNormal.z = cm[11] - cm[8];
735   planes.mPlanes[1].mDistance = cm[15] - cm[12];
736
737   // Bottom
738   planes.mPlanes[2].mNormal.x = cm[3] + cm[1]; // column 4 + column 2
739   planes.mPlanes[2].mNormal.y = cm[7] + cm[5];
740   planes.mPlanes[2].mNormal.z = cm[11] + cm[9];
741   planes.mPlanes[2].mDistance = cm[15] + cm[13];
742
743   // Top
744   planes.mPlanes[3].mNormal.x = cm[3] - cm[1]; // column 4 - column 2
745   planes.mPlanes[3].mNormal.y = cm[7] - cm[5];
746   planes.mPlanes[3].mNormal.z = cm[11] - cm[9];
747   planes.mPlanes[3].mDistance = cm[15] - cm[13];
748
749   // Near
750   planes.mPlanes[4].mNormal.x = cm[3] + cm[2]; // column 4 + column 3
751   planes.mPlanes[4].mNormal.y = cm[7] + cm[6];
752   planes.mPlanes[4].mNormal.z = cm[11] + cm[10];
753   planes.mPlanes[4].mDistance = cm[15] + cm[14];
754
755   // Far
756   planes.mPlanes[5].mNormal.x = cm[3] - cm[2]; // column 4 - column 3
757   planes.mPlanes[5].mNormal.y = cm[7] - cm[6];
758   planes.mPlanes[5].mNormal.z = cm[11] - cm[10];
759   planes.mPlanes[5].mDistance = cm[15] - cm[14];
760
761   if(normalize)
762   {
763     for(uint32_t i = 0; i < 6; ++i)
764     {
765       // Normalize planes to ensure correct bounding distance checking
766       Plane& plane = planes.mPlanes[i];
767       float  l     = 1.0f / plane.mNormal.Length();
768       plane.mNormal *= l;
769       plane.mDistance *= l;
770
771       planes.mSign[i] = Vector3(Sign(plane.mNormal.x), Sign(plane.mNormal.y), Sign(plane.mNormal.z));
772     }
773   }
774   else
775   {
776     for(uint32_t i = 0; i < 6; ++i)
777     {
778       planes.mSign[i] = Vector3(Sign(planes.mPlanes[i].mNormal.x), Sign(planes.mPlanes[i].mNormal.y), Sign(planes.mPlanes[i].mNormal.z));
779     }
780   }
781   mFrustum[updateBufferIndex ? 0 : 1] = planes;
782 }
783
784 bool Camera::CheckSphereInFrustum(BufferIndex bufferIndex, const Vector3& origin, float radius) const
785 {
786   const FrustumPlanes& planes = mFrustum[bufferIndex];
787   for(uint32_t i = 0; i < 6; ++i)
788   {
789     if((planes.mPlanes[i].mDistance + planes.mPlanes[i].mNormal.Dot(origin)) < -radius)
790     {
791       return false;
792     }
793   }
794   return true;
795 }
796
797 bool Camera::CheckAABBInFrustum(BufferIndex bufferIndex, const Vector3& origin, const Vector3& halfExtents) const
798 {
799   const FrustumPlanes& planes = mFrustum[bufferIndex];
800   for(uint32_t i = 0; i < 6; ++i)
801   {
802     if(planes.mPlanes[i].mNormal.Dot(origin + (halfExtents * planes.mSign[i])) > -(planes.mPlanes[i].mDistance))
803     {
804       continue;
805     }
806
807     return false;
808   }
809   return true;
810 }
811 Dali::Rect<int32_t> Camera::GetOrthographicClippingBox(BufferIndex bufferIndex) const
812 {
813   const float orthographicSize = mOrthographicSize[bufferIndex];
814   const float aspect           = mAspectRatio[bufferIndex];
815
816   const float halfWidth  = mProjectionDirection[0] == DevelCameraActor::ProjectionDirection::VERTICAL ? orthographicSize * aspect : orthographicSize;
817   const float halfHeight = mProjectionDirection[0] == DevelCameraActor::ProjectionDirection::VERTICAL ? orthographicSize : orthographicSize / aspect;
818
819   return Dali::Rect<int32_t>(-halfWidth, -halfHeight, halfWidth * 2.0f, halfHeight * 2.0f);
820 }
821
822 uint32_t Camera::UpdateProjection(BufferIndex updateBufferIndex)
823 {
824   uint32_t retval(mUpdateProjectionFlag);
825   // Early-exit if no update required
826   if(0u != mUpdateProjectionFlag)
827   {
828     Matrix& finalProjection = mFinalProjection[updateBufferIndex];
829     finalProjection.SetIdentity();
830
831     if(COPY_PREVIOUS_MATRIX == mUpdateProjectionFlag)
832     {
833       // The projection matrix was updated in the previous frame; copy it
834       mProjectionMatrix.CopyPrevious(updateBufferIndex);
835
836       finalProjection = mFinalProjection[updateBufferIndex ? 0 : 1];
837     }
838     else // UPDATE_COUNT == mUpdateProjectionFlag
839     {
840       switch(mProjectionMode[0])
841       {
842         case Dali::Camera::PERSPECTIVE_PROJECTION:
843         {
844           Matrix& projectionMatrix = mProjectionMatrix.Get(updateBufferIndex);
845           Perspective(projectionMatrix,
846                       static_cast<Dali::DevelCameraActor::ProjectionDirection>(mProjectionDirection[0]),
847                       mFieldOfView[updateBufferIndex],
848                       mAspectRatio[updateBufferIndex],
849                       mNearClippingPlane[updateBufferIndex],
850                       mFarClippingPlane[updateBufferIndex],
851                       mInvertYAxis[0]);
852
853           //need to apply custom clipping plane
854           if(mUseReflectionClip)
855           {
856             Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
857             Matrix  viewInv    = viewMatrix;
858             viewInv.Invert();
859             viewInv.Transpose();
860
861             Dali::Vector4 adjReflectPlane = mReflectionPlane;
862             float         d               = mReflectionPlane.Dot(mReflectionEye);
863             if(d < 0)
864             {
865               // Original eyesight was behind of mReflectionPlane. Reverse the plane.
866               adjReflectPlane = -adjReflectPlane;
867             }
868
869             Vector4 customClipping = viewInv * adjReflectPlane;
870             AdjustNearPlaneForPerspective(projectionMatrix, customClipping, mFarClippingPlane[updateBufferIndex]);
871           }
872           break;
873         }
874         case Dali::Camera::ORTHOGRAPHIC_PROJECTION:
875         {
876           Matrix& projectionMatrix = mProjectionMatrix.Get(updateBufferIndex);
877           Orthographic(projectionMatrix,
878                       static_cast<Dali::DevelCameraActor::ProjectionDirection>(mProjectionDirection[0]),
879                        mOrthographicSize[updateBufferIndex],
880                        mAspectRatio[updateBufferIndex],
881                        mNearClippingPlane[updateBufferIndex],
882                        mFarClippingPlane[updateBufferIndex],
883                        mInvertYAxis[0]);
884
885           //need to apply custom clipping plane
886           if(mUseReflectionClip)
887           {
888             Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
889             Matrix  viewInv    = viewMatrix;
890             viewInv.Invert();
891             viewInv.Transpose();
892
893             Dali::Vector4 adjReflectPlane = mReflectionPlane;
894             float         d               = mReflectionPlane.Dot(mReflectionEye);
895             if(d < 0)
896             {
897               // Original eyesight was behind of mReflectionPlane. Reverse the plane.
898               adjReflectPlane = -adjReflectPlane;
899             }
900
901             Vector4 customClipping = viewInv * adjReflectPlane;
902             AdjustNearPlaneForOrthographic(projectionMatrix, customClipping, mFarClippingPlane[updateBufferIndex]);
903           }
904           break;
905         }
906       }
907
908       mProjectionMatrix.SetDirty(updateBufferIndex);
909
910       Quaternion rotationAngle;
911       switch(mProjectionRotation)
912       {
913         case 90:
914         {
915           rotationAngle = Quaternion(Dali::ANGLE_90, Vector3::ZAXIS);
916           break;
917         }
918         case 180:
919         {
920           rotationAngle = Quaternion(Dali::ANGLE_180, Vector3::ZAXIS);
921           break;
922         }
923         case 270:
924         {
925           rotationAngle = Quaternion(Dali::ANGLE_270, Vector3::ZAXIS);
926           break;
927         }
928         default:
929           rotationAngle = Quaternion(Dali::ANGLE_0, Vector3::ZAXIS);
930           break;
931       }
932
933       // TODO : Can't we make finalProjection without matrix multiply?
934       MatrixUtils::Multiply(finalProjection, mProjectionMatrix.Get(updateBufferIndex), rotationAngle);
935     }
936     --mUpdateProjectionFlag;
937   }
938   return retval;
939 }
940
941 } // namespace SceneGraph
942
943 } // namespace Internal
944
945 } // namespace Dali