2 * Copyright (c) 2023 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali/internal/update/render-tasks/scene-graph-camera.h>
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>
32 namespace // unnamed namespace
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
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;
50 //Memory pool used to allocate new camera. Memory used by this pool will be released when shutting down DALi
51 MemoryPoolObjectAllocator<Camera>& GetCameraMemoryPool()
53 static MemoryPoolObjectAllocator<Camera> gCameraMemoryPool;
54 return gCameraMemoryPool;
60 return T(T(0) < value) - T(value < T(0));
63 void LookAt(Matrix& result, const Vector3& eye, const Vector3& target, const Vector3& up)
65 Vector3 vZ = target - eye;
68 Vector3 vX = up.Cross(vZ);
71 Vector3 vY = vZ.Cross(vX);
74 result.SetInverseTransformComponents(vX, vY, vZ, eye);
77 void Frustum(Matrix& result, float left, float right, float bottom, float top, float near, float far, bool invertYAxis)
79 float deltaZ = far - near;
80 if((near <= 0.0f) || (far <= 0.0f) || Equals(right, left) || Equals(bottom, top) || (deltaZ <= 0.0f))
82 DALI_LOG_ERROR("Invalid parameters passed into Frustum!\n");
83 DALI_ASSERT_DEBUG("Invalid parameters passed into Frustum!");
87 float deltaX = right - left;
88 float deltaY = invertYAxis ? bottom - top : top - bottom;
92 float* m = result.AsFloat();
93 m[0] = -2.0f * near / deltaX;
94 m[1] = m[2] = m[3] = 0.0f;
96 m[5] = -2.0f * near / deltaY;
97 m[4] = m[6] = m[7] = 0.0f;
99 m[8] = (right + left) / deltaX;
100 m[9] = (top + bottom) / deltaY;
101 m[10] = (near + far) / deltaZ;
104 m[14] = -2.0f * near * far / deltaZ;
105 m[12] = m[13] = m[15] = 0.0f;
108 void Perspective(Matrix& result, Dali::DevelCameraActor::ProjectionDirection fovDir, float fov, float aspect, float near, float far, bool invertYAxis)
112 if(fovDir == Dali::DevelCameraActor::ProjectionDirection::VERTICAL)
114 frustumH = tanf(fov * 0.5f) * near;
115 frustumW = frustumH * aspect;
119 frustumW = tanf(fov * 0.5f) * near;
120 frustumH = frustumW / aspect;
123 Frustum(result, -frustumW, frustumW, -frustumH, frustumH, near, far, invertYAxis);
126 void Orthographic(Matrix& result, Dali::DevelCameraActor::ProjectionDirection orthographicDir, float orthographicSize, float aspect, float near, float far, bool invertYAxis)
128 if(EqualsZero(orthographicSize) || EqualsZero(aspect) || Equals(far, near))
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.");
137 if(orthographicDir == Dali::DevelCameraActor::ProjectionDirection::VERTICAL)
139 halfDeltaY = orthographicSize;
140 halfDeltaX = halfDeltaY * aspect;
144 halfDeltaX = orthographicSize;
145 halfDeltaY = halfDeltaX / aspect;
148 float deltaZ = far - near;
150 float* m = result.AsFloat();
152 m[0] = -1.0f / halfDeltaX;
158 m[5] = (invertYAxis ? 1.0f : -1.0f) / halfDeltaY;
164 m[10] = 2.0f / deltaZ;
169 m[14] = -(near + far) / deltaZ;
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
179 void AdjustNearPlaneForPerspective(Matrix& perspective, const Vector4& clipPlane, float far)
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
185 // For example, standard case : -1 for near, 1 for far.
186 // v.z * n + v.w = -n
188 // This formular makes v.z = (f + n) / (f - n), v.w = -2fn / (f - n)
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;
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).
197 // v.z = scale * c.z - 1.0,
198 // v.w = scale * c.w.
200 // Now we have to determine scale value.
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.
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
210 // And the far plane case
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)
216 float* v = perspective.AsFloat();
218 float maximalCDotQ = Math::MACHINE_EPSILON_0; // We should find CDotQ is positive.
220 float inverseV0 = 1.0f / v[0];
221 float inverseV5 = 1.0f / v[5];
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)
226 Vector4 Q(0.0f, 0.0f, far, 1.0f);
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;
234 maximalCDotQ = std::max(maximalCDotQ, clipPlane.Dot(Q));
237 float scale = 2.0f * far / maximalCDotQ;
239 Vector4 scaledPlaneVector = clipPlane * scale;
241 v[2] = scaledPlaneVector.x;
242 v[6] = scaledPlaneVector.y;
243 v[10] = scaledPlaneVector.z - 1.0f;
244 v[14] = scaledPlaneVector.w;
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
253 void AdjustNearPlaneForOrthographic(Matrix& orthographic, const Vector4& clipPlane, float far)
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)
259 // For example, standard case : -1 for near, 1 for far.
260 // v.z * n + v.w = -1
262 // This formular makes v.z = 2 / (f - n), v.w = -(f + n) / (f - n)
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;
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).
271 // v.z = scale * c.z,
272 // v.w = scale * c.w - 1.0.
274 // Now we have to determine scale value.
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.
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
284 // And the far plane case
286 // --> (c * scale + (0, 0, 0, 1)) * Q = 1
287 // --> c * scale * Q = 2.0
288 // --> scale = 2.0 / (c * Q)
290 float* v = orthographic.AsFloat();
292 float maximalCDotQ = Math::MACHINE_EPSILON_0; // We should find CDotQ is positive.
294 float inverseV0 = 1.0f / v[0];
295 float inverseV5 = 1.0f / v[5];
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)
300 Vector4 Q(0.0f, 0.0f, far, 1.0f);
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;
308 maximalCDotQ = std::max(maximalCDotQ, clipPlane.Dot(Q));
311 float scale = 2.0f / maximalCDotQ;
313 Vector4 scaledPlaneVector = clipPlane * scale;
315 v[2] = scaledPlaneVector.x;
316 v[6] = scaledPlaneVector.y;
317 v[10] = scaledPlaneVector.z;
318 v[14] = scaledPlaneVector.w - 1.0f;
321 } // unnamed namespace
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);
336 mUpdateViewFlag(UPDATE_COUNT),
337 mUpdateProjectionFlag(UPDATE_COUNT),
338 mProjectionRotation(0),
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),
351 mInverseViewProjection(Matrix::IDENTITY),
352 mFinalProjection(Matrix::IDENTITY)
354 // set a flag the node to say this is a camera
358 Camera* Camera::New()
360 return new(GetCameraMemoryPool().AllocateRawThreadSafe()) Camera();
363 Camera::~Camera() = default;
365 void Camera::operator delete(void* ptr)
367 GetCameraMemoryPool().FreeThreadSafe(static_cast<Camera*>(ptr));
370 void Camera::SetType(Dali::Camera::Type type)
375 void Camera::SetProjectionMode(Dali::Camera::ProjectionMode mode)
377 mProjectionMode = mode;
378 mUpdateProjectionFlag = UPDATE_COUNT;
381 void Camera::SetProjectionDirection(Dali::DevelCameraActor::ProjectionDirection direction)
383 mProjectionDirection = direction;
384 mUpdateProjectionFlag = UPDATE_COUNT;
387 void Camera::SetInvertYAxis(bool invertYAxis)
389 mInvertYAxis = invertYAxis;
390 mUpdateProjectionFlag = UPDATE_COUNT;
393 void Camera::BakeFieldOfView(BufferIndex updateBufferIndex, float fieldOfView)
395 mFieldOfView.Bake(updateBufferIndex, fieldOfView);
396 mUpdateProjectionFlag = UPDATE_COUNT;
399 void Camera::BakeOrthographicSize(BufferIndex updateBufferIndex, float orthographicSize)
401 mOrthographicSize.Bake(updateBufferIndex, orthographicSize);
402 mUpdateProjectionFlag = UPDATE_COUNT;
405 void Camera::BakeAspectRatio(BufferIndex updateBufferIndex, float aspectRatio)
407 mAspectRatio.Bake(updateBufferIndex, aspectRatio);
408 mUpdateProjectionFlag = UPDATE_COUNT;
411 void Camera::SetNearClippingPlane(float nearClippingPlane)
413 mNearClippingPlane = nearClippingPlane;
414 mUpdateProjectionFlag = UPDATE_COUNT;
417 void Camera::SetFarClippingPlane(float farClippingPlane)
419 mFarClippingPlane = farClippingPlane;
420 mUpdateProjectionFlag = UPDATE_COUNT;
423 void Camera::SetTargetPosition(const Vector3& targetPosition)
425 mTargetPosition = targetPosition;
426 mUpdateViewFlag = UPDATE_COUNT;
429 void VectorReflectedByPlane(Vector4& out, Vector4& in, Vector4& plane)
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);
438 void Camera::SetReflectByPlane(const Vector4& plane)
440 // Note : we assume that plane.xyz is normal vector.
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;
447 v[0] = 1.0f - 2.0f * plane.x * plane.x;
453 v[5] = 1.0f - 2.0f * plane.y * plane.y;
459 v[10] = 1.0f - 2.0f * plane.z * plane.z;
462 v[12] = -2 * plane.x * plane.w;
463 v[13] = -2 * plane.y * plane.w;
464 v[14] = -2 * plane.z * plane.w;
467 mUseReflection = true;
468 mReflectionPlane = plane;
469 mUpdateViewFlag = UPDATE_COUNT;
472 void Camera::RotateProjection(int rotationAngle)
474 mProjectionRotation = rotationAngle;
475 mUpdateProjectionFlag = UPDATE_COUNT;
478 const Matrix& Camera::GetProjectionMatrix(BufferIndex bufferIndex) const
480 return mProjectionMatrix[bufferIndex];
483 const Matrix& Camera::GetViewMatrix(BufferIndex bufferIndex) const
485 return mViewMatrix[bufferIndex];
488 const Matrix& Camera::GetInverseViewProjectionMatrix(BufferIndex bufferIndex) const
490 return mInverseViewProjection[bufferIndex];
493 const Matrix& Camera::GetFinalProjectionMatrix(BufferIndex bufferIndex) const
495 return mFinalProjection[bufferIndex];
498 const PropertyBase* Camera::GetFieldOfView() const
500 return &mFieldOfView;
503 const PropertyBase* Camera::GetOrthographicSize() const
505 return &mOrthographicSize;
508 const PropertyBase* Camera::GetAspectRatio() const
510 return &mAspectRatio;
513 const PropertyInputImpl* Camera::GetProjectionMatrix() const
515 return &mProjectionMatrix;
518 const PropertyInputImpl* Camera::GetViewMatrix() const
523 void Camera::Update(BufferIndex updateBufferIndex)
525 // if this has changes in world position we need to update camera for next 2 frames
526 if(IsLocalMatrixDirty())
528 mUpdateViewFlag = UPDATE_COUNT;
530 if(GetDirtyFlags() & NodePropertyFlags::VISIBLE)
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;
539 // If projection matrix relative properties are animated now, flag change.
540 if(IsProjectionMatrixAnimated())
542 mUpdateProjectionFlag = UPDATE_COUNT;
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);
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)
552 // either has actually changed so recalculate
553 MatrixUtils::MultiplyProjectionMatrix(mInverseViewProjection[updateBufferIndex], mViewMatrix[updateBufferIndex], mProjectionMatrix[updateBufferIndex]);
554 UpdateFrustum(updateBufferIndex);
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());
559 else if(viewUpdateCount == COPY_PREVIOUS_MATRIX || projectionUpdateCount == COPY_PREVIOUS_MATRIX)
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];
568 bool Camera::ViewMatrixUpdated() const
570 return 0u != mUpdateViewFlag;
573 bool Camera::IsProjectionMatrixAnimated() const
575 return !mFieldOfView.IsClean() ||
576 !mOrthographicSize.IsClean() ||
577 !mAspectRatio.IsClean();
580 uint32_t Camera::UpdateViewMatrix(BufferIndex updateBufferIndex)
582 uint32_t retval(mUpdateViewFlag);
583 if(0u != mUpdateViewFlag)
585 if(COPY_PREVIOUS_MATRIX == mUpdateViewFlag)
587 // The projection matrix was updated in the previous frame; copy it
588 mViewMatrix.CopyPrevious(updateBufferIndex);
590 else // UPDATE_COUNT == mUpdateViewFlag
594 // camera orientation taken from node - i.e. look in abitrary, unconstrained direction
595 case Dali::Camera::FREE_LOOK:
597 Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
598 viewMatrix = GetWorldMatrix(updateBufferIndex);
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;
609 Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
610 Matrix oldViewMatrix(viewMatrix);
611 MatrixUtils::MultiplyTransformMatrix(viewMatrix, oldViewMatrix, mReflectionMtx);
615 mViewMatrix.SetDirty(updateBufferIndex);
619 // camera orientation constrained to look at a target
620 case Dali::Camera::LOOK_AT_TARGET:
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);
630 Vector3 up = orientation.Rotate(Vector3::YAXIS);
631 Vector4 position4 = Vector4(position);
632 Vector4 target4 = Vector4(mTargetPosition);
633 Vector4 up4 = Vector4(up);
637 Vector3 positionNew3;
638 Vector3 targetNewVector3;
642 VectorReflectedByPlane(positionNew, position4, mReflectionPlane);
643 VectorReflectedByPlane(targetNew, target4, mReflectionPlane);
644 VectorReflectedByPlane(upNew, up4, mReflectionPlane);
646 positionNew3 = Vector3(positionNew);
647 targetNewVector3 = Vector3(targetNew);
648 upNew3 = Vector3(upNew);
649 LookAt(viewMatrix, positionNew3, targetNewVector3, upNew3);
652 float* vZ = viewMatrix.AsFloat();
658 mReflectionEye = positionNew;
659 mUseReflectionClip = true;
663 LookAt(viewMatrix, position, mTargetPosition, orientation.Rotate(Vector3::YAXIS));
665 mViewMatrix.SetDirty(updateBufferIndex);
675 void Camera::UpdateFrustum(BufferIndex updateBufferIndex, bool normalize)
677 // Extract the clip matrix planes
678 Matrix clipMatrix(false); // Don't initialize.
679 MatrixUtils::MultiplyProjectionMatrix(clipMatrix, mViewMatrix[updateBufferIndex], mProjectionMatrix[updateBufferIndex]);
681 const float* cm = clipMatrix.AsFloat();
682 FrustumPlanes& planes = mFrustum[updateBufferIndex];
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];
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];
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];
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];
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];
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];
722 for(uint32_t i = 0; i < 6; ++i)
724 // Normalize planes to ensure correct bounding distance checking
725 Plane& plane = planes.mPlanes[i];
726 float l = 1.0f / plane.mNormal.Length();
728 plane.mDistance *= l;
730 planes.mSign[i] = Vector3(Sign(plane.mNormal.x), Sign(plane.mNormal.y), Sign(plane.mNormal.z));
735 for(uint32_t i = 0; i < 6; ++i)
737 planes.mSign[i] = Vector3(Sign(planes.mPlanes[i].mNormal.x), Sign(planes.mPlanes[i].mNormal.y), Sign(planes.mPlanes[i].mNormal.z));
740 mFrustum[updateBufferIndex ? 0 : 1] = planes;
743 bool Camera::CheckSphereInFrustum(BufferIndex bufferIndex, const Vector3& origin, float radius) const
745 const FrustumPlanes& planes = mFrustum[bufferIndex];
746 for(uint32_t i = 0; i < 6; ++i)
748 if((planes.mPlanes[i].mDistance + planes.mPlanes[i].mNormal.Dot(origin)) < -radius)
756 bool Camera::CheckAABBInFrustum(BufferIndex bufferIndex, const Vector3& origin, const Vector3& halfExtents) const
758 const FrustumPlanes& planes = mFrustum[bufferIndex];
759 for(uint32_t i = 0; i < 6; ++i)
761 if(planes.mPlanes[i].mNormal.Dot(origin + (halfExtents * planes.mSign[i])) > -(planes.mPlanes[i].mDistance))
770 Dali::Rect<int32_t> Camera::GetOrthographicClippingBox(BufferIndex bufferIndex) const
772 const float orthographicSize = mOrthographicSize[bufferIndex];
773 const float aspect = mAspectRatio[bufferIndex];
775 const float halfWidth = mProjectionDirection == DevelCameraActor::ProjectionDirection::VERTICAL ? orthographicSize * aspect : orthographicSize;
776 const float halfHeight = mProjectionDirection == DevelCameraActor::ProjectionDirection::VERTICAL ? orthographicSize : orthographicSize / aspect;
778 return Dali::Rect<int32_t>(-halfWidth, -halfHeight, halfWidth * 2.0f, halfHeight * 2.0f);
781 uint32_t Camera::UpdateProjection(BufferIndex updateBufferIndex)
783 uint32_t retval(mUpdateProjectionFlag);
784 // Early-exit if no update required
785 if(0u != mUpdateProjectionFlag)
787 Matrix& finalProjection = mFinalProjection[updateBufferIndex];
788 finalProjection.SetIdentity();
790 if(COPY_PREVIOUS_MATRIX == mUpdateProjectionFlag)
792 // The projection matrix was updated in the previous frame; copy it
793 mProjectionMatrix.CopyPrevious(updateBufferIndex);
795 finalProjection = mFinalProjection[updateBufferIndex ? 0 : 1];
797 else // UPDATE_COUNT == mUpdateProjectionFlag
799 switch(mProjectionMode)
801 case Dali::Camera::PERSPECTIVE_PROJECTION:
803 Matrix& projectionMatrix = mProjectionMatrix.Get(updateBufferIndex);
804 Perspective(projectionMatrix,
805 mProjectionDirection,
806 mFieldOfView[updateBufferIndex],
807 mAspectRatio[updateBufferIndex],
812 //need to apply custom clipping plane
813 if(mUseReflectionClip)
815 Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
816 Matrix viewInv = viewMatrix;
820 Dali::Vector4 adjReflectPlane = mReflectionPlane;
821 float d = mReflectionPlane.Dot(mReflectionEye);
824 // Original eyesight was behind of mReflectionPlane. Reverse the plane.
825 adjReflectPlane = -adjReflectPlane;
828 Vector4 customClipping = viewInv * adjReflectPlane;
829 AdjustNearPlaneForPerspective(projectionMatrix, customClipping, mFarClippingPlane);
833 case Dali::Camera::ORTHOGRAPHIC_PROJECTION:
835 Matrix& projectionMatrix = mProjectionMatrix.Get(updateBufferIndex);
836 Orthographic(projectionMatrix,
837 mProjectionDirection,
838 mOrthographicSize[updateBufferIndex],
839 mAspectRatio[updateBufferIndex],
844 //need to apply custom clipping plane
845 if(mUseReflectionClip)
847 Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
848 Matrix viewInv = viewMatrix;
852 Dali::Vector4 adjReflectPlane = mReflectionPlane;
853 float d = mReflectionPlane.Dot(mReflectionEye);
856 // Original eyesight was behind of mReflectionPlane. Reverse the plane.
857 adjReflectPlane = -adjReflectPlane;
860 Vector4 customClipping = viewInv * adjReflectPlane;
861 AdjustNearPlaneForOrthographic(projectionMatrix, customClipping, mFarClippingPlane);
867 mProjectionMatrix.SetDirty(updateBufferIndex);
869 Quaternion rotationAngle;
870 switch(mProjectionRotation)
874 rotationAngle = Quaternion(Dali::ANGLE_90, Vector3::ZAXIS);
879 rotationAngle = Quaternion(Dali::ANGLE_180, Vector3::ZAXIS);
884 rotationAngle = Quaternion(Dali::ANGLE_270, Vector3::ZAXIS);
888 rotationAngle = Quaternion(Dali::ANGLE_0, Vector3::ZAXIS);
892 // TODO : Can't we make finalProjection without matrix multiply?
893 MatrixUtils::Multiply(finalProjection, mProjectionMatrix.Get(updateBufferIndex), rotationAngle);
895 --mUpdateProjectionFlag;
900 } // namespace SceneGraph
902 } // namespace Internal