2 * Copyright (c) 2022 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/update/nodes/node.h>
27 #include <dali/public-api/common/dali-common.h>
28 #include <dali/public-api/math/math-utils.h>
30 namespace // unnamed namespace
32 const uint32_t UPDATE_COUNT = 2u; // Update projection or view matrix this many frames after a change
33 const uint32_t COPY_PREVIOUS_MATRIX = 1u; // Copy view or projection matrix from previous frame
35 //For reflection and clipping plane
36 const float REFLECTION_NORMALIZED_DEVICE_COORDINATE_PARAMETER_A = 2.0f;
37 const float REFLECTION_NORMALIZED_DEVICE_COORDINATE_PARAMETER_D = 1.0f;
51 return T(T(0) < value) - T(value < T(0));
54 void LookAt(Matrix& result, const Vector3& eye, const Vector3& target, const Vector3& up)
56 Vector3 vZ = target - eye;
59 Vector3 vX = up.Cross(vZ);
62 Vector3 vY = vZ.Cross(vX);
65 result.SetInverseTransformComponents(vX, vY, vZ, eye);
68 void Frustum(Matrix& result, float left, float right, float bottom, float top, float near, float far, bool invertYAxis)
70 float deltaZ = far - near;
71 if((near <= 0.0f) || (far <= 0.0f) || Equals(right, left) || Equals(bottom, top) || (deltaZ <= 0.0f))
73 DALI_LOG_ERROR("Invalid parameters passed into Frustum!\n");
74 DALI_ASSERT_DEBUG("Invalid parameters passed into Frustum!");
78 float deltaX = right - left;
79 float deltaY = invertYAxis ? bottom - top : top - bottom;
83 float* m = result.AsFloat();
84 m[0] = -2.0f * near / deltaX;
85 m[1] = m[2] = m[3] = 0.0f;
87 m[5] = -2.0f * near / deltaY;
88 m[4] = m[6] = m[7] = 0.0f;
90 m[8] = (right + left) / deltaX;
91 m[9] = (top + bottom) / deltaY;
92 m[10] = (near + far) / deltaZ;
95 m[14] = -2.0f * near * far / deltaZ;
96 m[12] = m[13] = m[15] = 0.0f;
99 void Perspective(Matrix& result, Dali::DevelCameraActor::ProjectionDirection fovDir, float fov, float aspect, float near, float far, bool invertYAxis)
103 if(fovDir == Dali::DevelCameraActor::ProjectionDirection::VERTICAL)
105 frustumH = tanf(fov * 0.5f) * near;
106 frustumW = frustumH * aspect;
110 frustumW = tanf(fov * 0.5f) * near;
111 frustumH = frustumW / aspect;
114 Frustum(result, -frustumW, frustumW, -frustumH, frustumH, near, far, invertYAxis);
117 void Orthographic(Matrix& result, float left, float right, float bottom, float top, float near, float far, bool invertYAxis)
119 if(Equals(right, left) || Equals(top, bottom) || Equals(far, near))
121 DALI_LOG_ERROR("Cannot create orthographic projection matrix with a zero dimension.\n");
122 DALI_ASSERT_DEBUG("Cannot create orthographic projection matrix with a zero dimension.");
126 float deltaX = right - left;
127 float deltaY = invertYAxis ? bottom - top : top - bottom;
128 float deltaZ = far - near;
130 float* m = result.AsFloat();
131 m[0] = -2.0f / deltaX;
137 m[5] = -2.0f / deltaY;
143 m[10] = 2.0f / deltaZ;
145 m[12] = -(right + left) / deltaX;
146 m[13] = -(top + bottom) / deltaY;
147 m[14] = -(near + far) / deltaZ;
151 } // unnamed namespace
153 const Dali::Camera::Type Camera::DEFAULT_TYPE(Dali::Camera::FREE_LOOK);
154 const Dali::Camera::ProjectionMode Camera::DEFAULT_MODE(Dali::Camera::PERSPECTIVE_PROJECTION);
155 const Dali::DevelCameraActor::ProjectionDirection Camera::DEFAULT_PROJECTION_DIRECTION(Dali::DevelCameraActor::VERTICAL);
156 const bool Camera::DEFAULT_INVERT_Y_AXIS(false);
157 const float Camera::DEFAULT_FIELD_OF_VIEW(45.0f * (Math::PI / 180.0f));
158 const float Camera::DEFAULT_ASPECT_RATIO(800.0f / 480.0f);
159 const float Camera::DEFAULT_LEFT_CLIPPING_PLANE(-240.0f);
160 const float Camera::DEFAULT_RIGHT_CLIPPING_PLANE(240.0f);
161 const float Camera::DEFAULT_TOP_CLIPPING_PLANE(-400.0f);
162 const float Camera::DEFAULT_BOTTOM_CLIPPING_PLANE(400.0f);
163 const float Camera::DEFAULT_NEAR_CLIPPING_PLANE(800.0f); // default height of the screen
164 const float Camera::DEFAULT_FAR_CLIPPING_PLANE(DEFAULT_NEAR_CLIPPING_PLANE + 2.f * DEFAULT_NEAR_CLIPPING_PLANE);
165 const Vector3 Camera::DEFAULT_TARGET_POSITION(0.0f, 0.0f, 0.0f);
168 : mUpdateViewFlag(UPDATE_COUNT),
169 mUpdateProjectionFlag(UPDATE_COUNT),
170 mProjectionRotation(0),
173 mProjectionMode(DEFAULT_MODE),
174 mProjectionDirection(DEFAULT_PROJECTION_DIRECTION),
175 mInvertYAxis(DEFAULT_INVERT_Y_AXIS),
176 mFieldOfView(DEFAULT_FIELD_OF_VIEW),
177 mAspectRatio(DEFAULT_ASPECT_RATIO),
178 mLeftClippingPlane(DEFAULT_LEFT_CLIPPING_PLANE),
179 mRightClippingPlane(DEFAULT_RIGHT_CLIPPING_PLANE),
180 mTopClippingPlane(DEFAULT_TOP_CLIPPING_PLANE),
181 mBottomClippingPlane(DEFAULT_BOTTOM_CLIPPING_PLANE),
182 mNearClippingPlane(DEFAULT_NEAR_CLIPPING_PLANE),
183 mFarClippingPlane(DEFAULT_FAR_CLIPPING_PLANE),
184 mTargetPosition(DEFAULT_TARGET_POSITION),
187 mInverseViewProjection(Matrix::IDENTITY),
188 mFinalProjection(Matrix::IDENTITY)
192 Camera* Camera::New()
197 Camera::~Camera() = default;
199 void Camera::SetNode(const Node* node)
204 const Node* Camera::GetNode() const
209 void Camera::SetType(Dali::Camera::Type type)
214 void Camera::SetProjectionMode(Dali::Camera::ProjectionMode mode)
216 mProjectionMode = mode;
217 mUpdateProjectionFlag = UPDATE_COUNT;
220 void Camera::SetProjectionDirection(Dali::DevelCameraActor::ProjectionDirection direction)
222 mProjectionDirection = direction;
223 mUpdateProjectionFlag = UPDATE_COUNT;
226 void Camera::SetInvertYAxis(bool invertYAxis)
228 mInvertYAxis = invertYAxis;
229 mUpdateProjectionFlag = UPDATE_COUNT;
232 void Camera::SetFieldOfView(float fieldOfView)
234 mFieldOfView = fieldOfView;
235 mUpdateProjectionFlag = UPDATE_COUNT;
238 void Camera::SetAspectRatio(float aspectRatio)
240 mAspectRatio = aspectRatio;
241 mUpdateProjectionFlag = UPDATE_COUNT;
244 void Camera::SetLeftClippingPlane(float leftClippingPlane)
246 mLeftClippingPlane = leftClippingPlane;
247 mUpdateProjectionFlag = UPDATE_COUNT;
250 void Camera::SetRightClippingPlane(float rightClippingPlane)
252 mRightClippingPlane = rightClippingPlane;
253 mUpdateProjectionFlag = UPDATE_COUNT;
256 void Camera::SetTopClippingPlane(float topClippingPlane)
258 mTopClippingPlane = topClippingPlane;
259 mUpdateProjectionFlag = UPDATE_COUNT;
262 void Camera::SetBottomClippingPlane(float bottomClippingPlane)
264 mBottomClippingPlane = bottomClippingPlane;
265 mUpdateProjectionFlag = UPDATE_COUNT;
268 void Camera::SetNearClippingPlane(float nearClippingPlane)
270 mNearClippingPlane = nearClippingPlane;
271 mUpdateProjectionFlag = UPDATE_COUNT;
274 void Camera::SetFarClippingPlane(float farClippingPlane)
276 mFarClippingPlane = farClippingPlane;
277 mUpdateProjectionFlag = UPDATE_COUNT;
280 void Camera::SetTargetPosition(const Vector3& targetPosition)
282 mTargetPosition = targetPosition;
283 mUpdateViewFlag = UPDATE_COUNT;
286 void VectorReflectedByPlane(Vector4& out, Vector4& in, Vector4& plane)
288 float d = float(2.0) * plane.Dot(in);
289 out.x = static_cast<float>(in.x - plane.x * d);
290 out.y = static_cast<float>(in.y - plane.y * d);
291 out.z = static_cast<float>(in.z - plane.z * d);
292 out.w = static_cast<float>(in.w - plane.w * d);
295 void Camera::AdjustNearPlaneForPerspective(Matrix& perspective, const Vector4& clipPlane)
298 float* v = perspective.AsFloat();
300 q.x = (Sign(clipPlane.x) + v[8]) / v[0];
301 q.y = (Sign(clipPlane.y) + v[9]) / v[5];
303 q.w = (1.0f + v[10]) / v[14];
305 // Calculate the scaled plane vector
306 Vector4 c = clipPlane * (REFLECTION_NORMALIZED_DEVICE_COORDINATE_PARAMETER_A / q.Dot(clipPlane));
308 // Replace the third row of the projection v
311 v[10] = c.z + REFLECTION_NORMALIZED_DEVICE_COORDINATE_PARAMETER_D;
315 void Camera::SetReflectByPlane(const Vector4& plane)
317 float* v = mReflectionMtx.AsFloat();
318 float _2ab = -2.0f * plane.x * plane.y;
319 float _2ac = -2.0f * plane.x * plane.z;
320 float _2bc = -2.0f * plane.y * plane.z;
322 v[0] = 1.0f - 2.0f * plane.x * plane.x;
328 v[5] = 1.0f - 2.0f * plane.y * plane.y;
334 v[10] = 1.0f - 2.0f * plane.z * plane.z;
337 v[12] = -2 * plane.x * plane.w;
338 v[13] = -2 * plane.y * plane.w;
339 v[14] = -2 * plane.z * plane.w;
342 mUseReflection = true;
343 mReflectionPlane = plane;
344 mUpdateViewFlag = UPDATE_COUNT;
347 void Camera::RotateProjection(int rotationAngle)
349 mProjectionRotation = rotationAngle;
350 mUpdateProjectionFlag = UPDATE_COUNT;
353 const Matrix& Camera::GetProjectionMatrix(BufferIndex bufferIndex) const
355 return mProjectionMatrix[bufferIndex];
358 const Matrix& Camera::GetViewMatrix(BufferIndex bufferIndex) const
360 return mViewMatrix[bufferIndex];
363 const Matrix& Camera::GetInverseViewProjectionMatrix(BufferIndex bufferIndex) const
365 return mInverseViewProjection[bufferIndex];
368 const Matrix& Camera::GetFinalProjectionMatrix(BufferIndex bufferIndex) const
370 return mFinalProjection[bufferIndex];
373 const PropertyInputImpl* Camera::GetProjectionMatrix() const
375 return &mProjectionMatrix;
378 const PropertyInputImpl* Camera::GetViewMatrix() const
383 void Camera::Update(BufferIndex updateBufferIndex)
385 // if owning node has changes in world position we need to update camera for next 2 frames
386 if(mNode->IsLocalMatrixDirty())
388 mUpdateViewFlag = UPDATE_COUNT;
390 if(mNode->GetDirtyFlags() & NodePropertyFlags::VISIBLE)
392 // If the visibility changes, the projection matrix needs to be re-calculated.
393 // It may happen the first time an actor is rendered it's rendered only once and becomes invisible,
394 // in the following update the node will be skipped leaving the projection matrix (double buffered)
395 // with the Identity.
396 mUpdateProjectionFlag = UPDATE_COUNT;
399 // if either matrix changed, we need to recalculate the inverse matrix for hit testing to work
400 uint32_t viewUpdateCount = UpdateViewMatrix(updateBufferIndex);
401 uint32_t projectionUpdateCount = UpdateProjection(updateBufferIndex);
403 // if model or view matrix changed we need to either recalculate the inverse VP or copy previous
404 if(viewUpdateCount > COPY_PREVIOUS_MATRIX || projectionUpdateCount > COPY_PREVIOUS_MATRIX)
406 // either has actually changed so recalculate
407 Matrix::Multiply(mInverseViewProjection[updateBufferIndex], mViewMatrix[updateBufferIndex], mProjectionMatrix[updateBufferIndex]);
408 UpdateFrustum(updateBufferIndex);
410 // ignore the error, if the view projection is incorrect (non inversible) then you will have tough times anyways
411 static_cast<void>(mInverseViewProjection[updateBufferIndex].Invert());
413 else if(viewUpdateCount == COPY_PREVIOUS_MATRIX || projectionUpdateCount == COPY_PREVIOUS_MATRIX)
415 // neither has actually changed, but we might copied previous frames value so need to
416 // copy the previous inverse and frustum as well
417 mInverseViewProjection[updateBufferIndex] = mInverseViewProjection[updateBufferIndex ? 0 : 1];
418 mFrustum[updateBufferIndex] = mFrustum[updateBufferIndex ? 0 : 1];
422 bool Camera::ViewMatrixUpdated()
424 return 0u != mUpdateViewFlag;
427 uint32_t Camera::UpdateViewMatrix(BufferIndex updateBufferIndex)
429 uint32_t retval(mUpdateViewFlag);
430 if(0u != mUpdateViewFlag)
432 if(COPY_PREVIOUS_MATRIX == mUpdateViewFlag)
434 // The projection matrix was updated in the previous frame; copy it
435 mViewMatrix.CopyPrevious(updateBufferIndex);
437 else // UPDATE_COUNT == mUpdateViewFlag
441 // camera orientation taken from node - i.e. look in abitrary, unconstrained direction
442 case Dali::Camera::FREE_LOOK:
444 Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
445 viewMatrix = mNode->GetWorldMatrix(updateBufferIndex);
449 const Matrix& owningNodeMatrix(mNode->GetWorldMatrix(updateBufferIndex));
450 Vector3 position{}, scale{};
451 Quaternion orientation{};
452 owningNodeMatrix.GetTransformComponents(position, orientation, scale);
453 mReflectionEye = position;
454 mUseReflectionClip = true;
456 Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
457 Matrix oldViewMatrix(viewMatrix);
458 Matrix::Multiply(viewMatrix, oldViewMatrix, mReflectionMtx);
462 mViewMatrix.SetDirty(updateBufferIndex);
466 // camera orientation constrained to look at a target
467 case Dali::Camera::LOOK_AT_TARGET:
469 const Matrix& owningNodeMatrix(mNode->GetWorldMatrix(updateBufferIndex));
470 Vector3 position, scale;
471 Quaternion orientation;
472 owningNodeMatrix.GetTransformComponents(position, orientation, scale);
473 Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
477 Vector3 up = orientation.Rotate(Vector3::YAXIS);
478 Vector4 position4 = Vector4(position);
479 Vector4 target4 = Vector4(mTargetPosition);
480 Vector4 up4 = Vector4(up);
484 Vector3 positionNew3;
485 Vector3 targetNewVector3;
489 VectorReflectedByPlane(positionNew, position4, mReflectionPlane);
490 VectorReflectedByPlane(targetNew, target4, mReflectionPlane);
491 VectorReflectedByPlane(upNew, up4, mReflectionPlane);
493 positionNew3 = Vector3(positionNew);
494 targetNewVector3 = Vector3(targetNew);
495 upNew3 = Vector3(upNew);
496 LookAt(viewMatrix, positionNew3, targetNewVector3, upNew3);
498 Matrix oldViewMatrix(viewMatrix);
500 tmp.SetIdentityAndScale(Vector3(-1.0, 1.0, 1.0));
501 Matrix::Multiply(viewMatrix, oldViewMatrix, tmp);
503 mReflectionEye = positionNew;
504 mUseReflectionClip = true;
508 LookAt(viewMatrix, position, mTargetPosition, orientation.Rotate(Vector3::YAXIS));
510 mViewMatrix.SetDirty(updateBufferIndex);
520 void Camera::UpdateFrustum(BufferIndex updateBufferIndex, bool normalize)
522 // Extract the clip matrix planes
524 Matrix::Multiply(clipMatrix, mViewMatrix[updateBufferIndex], mProjectionMatrix[updateBufferIndex]);
526 const float* cm = clipMatrix.AsFloat();
527 FrustumPlanes& planes = mFrustum[updateBufferIndex];
530 planes.mPlanes[0].mNormal.x = cm[3] + cm[0]; // column 4 + column 1
531 planes.mPlanes[0].mNormal.y = cm[7] + cm[4];
532 planes.mPlanes[0].mNormal.z = cm[11] + cm[8];
533 planes.mPlanes[0].mDistance = cm[15] + cm[12];
536 planes.mPlanes[1].mNormal.x = cm[3] - cm[0]; // column 4 - column 1
537 planes.mPlanes[1].mNormal.y = cm[7] - cm[4];
538 planes.mPlanes[1].mNormal.z = cm[11] - cm[8];
539 planes.mPlanes[1].mDistance = cm[15] - cm[12];
542 planes.mPlanes[2].mNormal.x = cm[3] + cm[1]; // column 4 + column 2
543 planes.mPlanes[2].mNormal.y = cm[7] + cm[5];
544 planes.mPlanes[2].mNormal.z = cm[11] + cm[9];
545 planes.mPlanes[2].mDistance = cm[15] + cm[13];
548 planes.mPlanes[3].mNormal.x = cm[3] - cm[1]; // column 4 - column 2
549 planes.mPlanes[3].mNormal.y = cm[7] - cm[5];
550 planes.mPlanes[3].mNormal.z = cm[11] - cm[9];
551 planes.mPlanes[3].mDistance = cm[15] - cm[13];
554 planes.mPlanes[4].mNormal.x = cm[3] + cm[2]; // column 4 + column 3
555 planes.mPlanes[4].mNormal.y = cm[7] + cm[6];
556 planes.mPlanes[4].mNormal.z = cm[11] + cm[10];
557 planes.mPlanes[4].mDistance = cm[15] + cm[14];
560 planes.mPlanes[5].mNormal.x = cm[3] - cm[2]; // column 4 - column 3
561 planes.mPlanes[5].mNormal.y = cm[7] - cm[6];
562 planes.mPlanes[5].mNormal.z = cm[11] - cm[10];
563 planes.mPlanes[5].mDistance = cm[15] - cm[14];
567 for(uint32_t i = 0; i < 6; ++i)
569 // Normalize planes to ensure correct bounding distance checking
570 Plane& plane = planes.mPlanes[i];
571 float l = 1.0f / plane.mNormal.Length();
573 plane.mDistance *= l;
575 planes.mSign[i] = Vector3(Sign(plane.mNormal.x), Sign(plane.mNormal.y), Sign(plane.mNormal.z));
580 for(uint32_t i = 0; i < 6; ++i)
582 planes.mSign[i] = Vector3(Sign(planes.mPlanes[i].mNormal.x), Sign(planes.mPlanes[i].mNormal.y), Sign(planes.mPlanes[i].mNormal.z));
585 mFrustum[updateBufferIndex ? 0 : 1] = planes;
588 bool Camera::CheckSphereInFrustum(BufferIndex bufferIndex, const Vector3& origin, float radius)
590 const FrustumPlanes& planes = mFrustum[bufferIndex];
591 for(uint32_t i = 0; i < 6; ++i)
593 if((planes.mPlanes[i].mDistance + planes.mPlanes[i].mNormal.Dot(origin)) < -radius)
601 bool Camera::CheckAABBInFrustum(BufferIndex bufferIndex, const Vector3& origin, const Vector3& halfExtents)
603 const FrustumPlanes& planes = mFrustum[bufferIndex];
604 for(uint32_t i = 0; i < 6; ++i)
606 if(planes.mPlanes[i].mNormal.Dot(origin + (halfExtents * planes.mSign[i])) > -(planes.mPlanes[i].mDistance))
616 uint32_t Camera::UpdateProjection(BufferIndex updateBufferIndex)
618 uint32_t retval(mUpdateProjectionFlag);
619 // Early-exit if no update required
620 if(0u != mUpdateProjectionFlag)
622 Matrix& finalProjection = mFinalProjection[updateBufferIndex];
623 finalProjection.SetIdentity();
625 if(COPY_PREVIOUS_MATRIX == mUpdateProjectionFlag)
627 // The projection matrix was updated in the previous frame; copy it
628 mProjectionMatrix.CopyPrevious(updateBufferIndex);
630 finalProjection = mFinalProjection[updateBufferIndex ? 0 : 1];
632 else // UPDATE_COUNT == mUpdateProjectionFlag
634 switch(mProjectionMode)
636 case Dali::Camera::PERSPECTIVE_PROJECTION:
638 Matrix& projectionMatrix = mProjectionMatrix.Get(updateBufferIndex);
639 Perspective(projectionMatrix,
640 mProjectionDirection,
647 //need to apply custom clipping plane
648 if(mUseReflectionClip)
650 Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
651 Matrix viewInv = viewMatrix;
655 Dali::Vector4 adjReflectPlane = mReflectionPlane;
656 float d = mReflectionPlane.Dot(mReflectionEye);
659 adjReflectPlane.w = -adjReflectPlane.w;
662 Vector4 customClipping = viewInv * adjReflectPlane;
663 AdjustNearPlaneForPerspective(projectionMatrix, customClipping);
668 float* vZ = matZ.AsFloat();
670 Matrix::Multiply(projectionMatrix, projectionMatrix, matZ);
674 case Dali::Camera::ORTHOGRAPHIC_PROJECTION:
676 Matrix& projectionMatrix = mProjectionMatrix.Get(updateBufferIndex);
677 Orthographic(projectionMatrix,
680 mBottomClippingPlane,
689 mProjectionMatrix.SetDirty(updateBufferIndex);
691 Quaternion rotationAngle;
692 switch(mProjectionRotation)
696 rotationAngle = Quaternion(Dali::ANGLE_90, Vector3::ZAXIS);
701 rotationAngle = Quaternion(Dali::ANGLE_180, Vector3::ZAXIS);
706 rotationAngle = Quaternion(Dali::ANGLE_270, Vector3::ZAXIS);
710 rotationAngle = Quaternion(Dali::ANGLE_0, Vector3::ZAXIS);
715 rotation.SetIdentity();
716 rotation.SetTransformComponents(Vector3(1.0f, 1.0f, 1.0f), rotationAngle, Vector3(0.0f, 0.0f, 0.0f));
718 Matrix::Multiply(finalProjection, mProjectionMatrix.Get(updateBufferIndex), rotation);
720 --mUpdateProjectionFlag;
725 } // namespace SceneGraph
727 } // namespace Internal