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