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 #include <dali/internal/update/common/property-resetter.h>
33 #include <dali/internal/update/common/resetter-manager.h> ///< For AddInitializeResetter
35 namespace // unnamed namespace
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
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;
53 //Memory pool used to allocate new camera. Memory used by this pool will be released when shutting down DALi
54 MemoryPoolObjectAllocator<Camera>& GetCameraMemoryPool()
56 static MemoryPoolObjectAllocator<Camera> gCameraMemoryPool;
57 return gCameraMemoryPool;
63 return T(T(0) < value) - T(value < T(0));
66 void LookAt(Matrix& result, const Vector3& eye, const Vector3& target, const Vector3& up)
68 Vector3 vZ = target - eye;
71 Vector3 vX = up.Cross(vZ);
74 Vector3 vY = vZ.Cross(vX);
77 result.SetInverseTransformComponents(vX, vY, vZ, eye);
80 void Frustum(Matrix& result, float left, float right, float bottom, float top, float near, float far, bool invertYAxis)
82 float deltaZ = far - near;
83 if((near <= 0.0f) || (far <= 0.0f) || Equals(right, left) || Equals(bottom, top) || (deltaZ <= 0.0f))
85 DALI_LOG_ERROR("Invalid parameters passed into Frustum!\n");
86 DALI_ASSERT_DEBUG("Invalid parameters passed into Frustum!");
90 float deltaX = right - left;
91 float deltaY = invertYAxis ? bottom - top : top - bottom;
95 float* m = result.AsFloat();
96 m[0] = -2.0f * near / deltaX;
97 m[1] = m[2] = m[3] = 0.0f;
99 m[5] = -2.0f * near / deltaY;
100 m[4] = m[6] = m[7] = 0.0f;
102 m[8] = (right + left) / deltaX;
103 m[9] = (top + bottom) / deltaY;
104 m[10] = (near + far) / deltaZ;
107 m[14] = -2.0f * near * far / deltaZ;
108 m[12] = m[13] = m[15] = 0.0f;
111 void Perspective(Matrix& result, Dali::DevelCameraActor::ProjectionDirection fovDir, float fov, float aspect, float near, float far, bool invertYAxis)
115 if(fovDir == Dali::DevelCameraActor::ProjectionDirection::VERTICAL)
117 frustumH = tanf(fov * 0.5f) * near;
118 frustumW = frustumH * aspect;
122 frustumW = tanf(fov * 0.5f) * near;
123 frustumH = frustumW / aspect;
126 Frustum(result, -frustumW, frustumW, -frustumH, frustumH, near, far, invertYAxis);
129 void Orthographic(Matrix& result, Dali::DevelCameraActor::ProjectionDirection orthographicDir, float orthographicSize, float aspect, float near, float far, bool invertYAxis)
131 if(EqualsZero(orthographicSize) || EqualsZero(aspect) || Equals(far, near))
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.");
140 if(orthographicDir == Dali::DevelCameraActor::ProjectionDirection::VERTICAL)
142 halfDeltaY = orthographicSize;
143 halfDeltaX = halfDeltaY * aspect;
147 halfDeltaX = orthographicSize;
148 halfDeltaY = halfDeltaX / aspect;
151 float deltaZ = far - near;
153 float* m = result.AsFloat();
155 m[0] = -1.0f / halfDeltaX;
161 m[5] = (invertYAxis ? 1.0f : -1.0f) / halfDeltaY;
167 m[10] = 2.0f / deltaZ;
172 m[14] = -(near + far) / deltaZ;
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
182 void AdjustNearPlaneForPerspective(Matrix& perspective, const Vector4& clipPlane, float far)
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
188 // For example, standard case : -1 for near, 1 for far.
189 // v.z * n + v.w = -n
191 // This formular makes v.z = (f + n) / (f - n), v.w = -2fn / (f - n)
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;
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).
200 // v.z = scale * c.z - 1.0,
201 // v.w = scale * c.w.
203 // Now we have to determine scale value.
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.
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
213 // And the far plane case
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)
219 float* v = perspective.AsFloat();
221 float maximalCDotQ = Math::MACHINE_EPSILON_0; // We should find CDotQ is positive.
223 float inverseV0 = 1.0f / v[0];
224 float inverseV5 = 1.0f / v[5];
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)
229 Vector4 Q(0.0f, 0.0f, far, 1.0f);
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;
237 maximalCDotQ = std::max(maximalCDotQ, clipPlane.Dot(Q));
240 float scale = 2.0f * far / maximalCDotQ;
242 Vector4 scaledPlaneVector = clipPlane * scale;
244 v[2] = scaledPlaneVector.x;
245 v[6] = scaledPlaneVector.y;
246 v[10] = scaledPlaneVector.z - 1.0f;
247 v[14] = scaledPlaneVector.w;
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
256 void AdjustNearPlaneForOrthographic(Matrix& orthographic, const Vector4& clipPlane, float far)
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)
262 // For example, standard case : -1 for near, 1 for far.
263 // v.z * n + v.w = -1
265 // This formular makes v.z = 2 / (f - n), v.w = -(f + n) / (f - n)
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;
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).
274 // v.z = scale * c.z,
275 // v.w = scale * c.w - 1.0.
277 // Now we have to determine scale value.
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.
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
287 // And the far plane case
289 // --> (c * scale + (0, 0, 0, 1)) * Q = 1
290 // --> c * scale * Q = 2.0
291 // --> scale = 2.0 / (c * Q)
293 float* v = orthographic.AsFloat();
295 float maximalCDotQ = Math::MACHINE_EPSILON_0; // We should find CDotQ is positive.
297 float inverseV0 = 1.0f / v[0];
298 float inverseV5 = 1.0f / v[5];
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)
303 Vector4 Q(0.0f, 0.0f, far, 1.0f);
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;
311 maximalCDotQ = std::max(maximalCDotQ, clipPlane.Dot(Q));
314 float scale = 2.0f / maximalCDotQ;
316 Vector4 scaledPlaneVector = clipPlane * scale;
318 v[2] = scaledPlaneVector.x;
319 v[6] = scaledPlaneVector.y;
320 v[10] = scaledPlaneVector.z;
321 v[14] = scaledPlaneVector.w - 1.0f;
324 } // unnamed namespace
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);
339 mUpdateViewFlag(UPDATE_COUNT),
340 mUpdateProjectionFlag(UPDATE_COUNT),
341 mProjectionRotation(0),
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),
354 mInverseViewProjection(Matrix::IDENTITY),
355 mFinalProjection(Matrix::IDENTITY)
357 // set a flag the node to say this is a camera
361 Camera* Camera::New()
363 return new(GetCameraMemoryPool().AllocateRawThreadSafe()) Camera();
366 Camera::~Camera() = default;
368 void Camera::operator delete(void* ptr)
370 GetCameraMemoryPool().FreeThreadSafe(static_cast<Camera*>(ptr));
373 void Camera::SetType(Dali::Camera::Type type)
378 void Camera::SetProjectionMode(Dali::Camera::ProjectionMode mode)
380 mProjectionMode.Bake(0, mode);
381 mUpdateProjectionFlag = UPDATE_COUNT;
384 void Camera::SetProjectionDirection(Dali::DevelCameraActor::ProjectionDirection direction)
386 mProjectionDirection.Bake(0, direction);
387 mUpdateProjectionFlag = UPDATE_COUNT;
390 void Camera::SetInvertYAxis(bool invertYAxis)
392 mInvertYAxis.Bake(0, invertYAxis);
393 mUpdateProjectionFlag = UPDATE_COUNT;
396 void Camera::BakeFieldOfView(BufferIndex updateBufferIndex, float fieldOfView)
398 mFieldOfView.Bake(updateBufferIndex, fieldOfView);
399 mUpdateProjectionFlag = UPDATE_COUNT;
402 void Camera::BakeOrthographicSize(BufferIndex updateBufferIndex, float orthographicSize)
404 mOrthographicSize.Bake(updateBufferIndex, orthographicSize);
405 mUpdateProjectionFlag = UPDATE_COUNT;
408 void Camera::BakeAspectRatio(BufferIndex updateBufferIndex, float aspectRatio)
410 mAspectRatio.Bake(updateBufferIndex, aspectRatio);
411 mUpdateProjectionFlag = UPDATE_COUNT;
414 void Camera::BakeNearClippingPlane(BufferIndex updateBufferIndex, float nearClippingPlane)
416 mNearClippingPlane.Bake(updateBufferIndex, nearClippingPlane);
417 mUpdateProjectionFlag = UPDATE_COUNT;
420 void Camera::BakeFarClippingPlane(BufferIndex updateBufferIndex, float farClippingPlane)
422 mFarClippingPlane.Bake(updateBufferIndex, farClippingPlane);
423 mUpdateProjectionFlag = UPDATE_COUNT;
426 void Camera::SetTargetPosition(const Vector3& targetPosition)
428 mTargetPosition = targetPosition;
429 mUpdateViewFlag = UPDATE_COUNT;
432 void VectorReflectedByPlane(Vector4& out, Vector4& in, Vector4& plane)
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);
441 void Camera::SetReflectByPlane(const Vector4& plane)
443 // Note : we assume that plane.xyz is normal vector.
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;
450 v[0] = 1.0f - 2.0f * plane.x * plane.x;
456 v[5] = 1.0f - 2.0f * plane.y * plane.y;
462 v[10] = 1.0f - 2.0f * plane.z * plane.z;
465 v[12] = -2 * plane.x * plane.w;
466 v[13] = -2 * plane.y * plane.w;
467 v[14] = -2 * plane.z * plane.w;
470 mUseReflection = true;
471 mReflectionPlane = plane;
472 mUpdateViewFlag = UPDATE_COUNT;
475 void Camera::RotateProjection(int rotationAngle)
477 mProjectionRotation = rotationAngle;
478 mUpdateProjectionFlag = UPDATE_COUNT;
481 const Matrix& Camera::GetProjectionMatrix(BufferIndex bufferIndex) const
483 return mProjectionMatrix[bufferIndex];
486 const Matrix& Camera::GetViewMatrix(BufferIndex bufferIndex) const
488 return mViewMatrix[bufferIndex];
491 const Matrix& Camera::GetInverseViewProjectionMatrix(BufferIndex bufferIndex) const
493 return mInverseViewProjection[bufferIndex];
496 const Matrix& Camera::GetFinalProjectionMatrix(BufferIndex bufferIndex) const
498 return mFinalProjection[bufferIndex];
501 const PropertyBase* Camera::GetProjectionMode() const
503 return &mProjectionMode;
506 const PropertyBase* Camera::GetFieldOfView() const
508 return &mFieldOfView;
511 const PropertyBase* Camera::GetAspectRatio() const
513 return &mAspectRatio;
516 const PropertyBase* Camera::GetNearPlaneDistance() const
518 return &mNearClippingPlane;
521 const PropertyBase* Camera::GetFarPlaneDistance() const
523 return &mFarClippingPlane;
526 const PropertyInputImpl* Camera::GetProjectionMatrix() const
528 return &mProjectionMatrix;
531 const PropertyInputImpl* Camera::GetViewMatrix() const
536 const PropertyBase* Camera::GetInvertYAxis() const
538 return &mInvertYAxis;
541 const PropertyBase* Camera::GetOrthographicSize() const
543 return &mOrthographicSize;
546 const PropertyBase* Camera::GetProjectionDirection() const
548 return &mProjectionDirection;
551 void Camera::Update(BufferIndex updateBufferIndex)
553 // if this has changes in world position we need to update camera for next 2 frames
554 if(IsLocalMatrixDirty())
556 mUpdateViewFlag = UPDATE_COUNT;
558 if(GetDirtyFlags() & NodePropertyFlags::VISIBLE)
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;
567 // If projection matrix relative properties are animated now, flag change.
568 if(IsProjectionMatrixAnimated())
570 mUpdateProjectionFlag = UPDATE_COUNT;
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);
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)
580 // either has actually changed so recalculate
581 MatrixUtils::MultiplyProjectionMatrix(mInverseViewProjection[updateBufferIndex], mViewMatrix[updateBufferIndex], mProjectionMatrix[updateBufferIndex]);
582 UpdateFrustum(updateBufferIndex);
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());
587 else if(viewUpdateCount == COPY_PREVIOUS_MATRIX || projectionUpdateCount == COPY_PREVIOUS_MATRIX)
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];
596 bool Camera::ViewMatrixUpdated() const
598 return 0u != mUpdateViewFlag;
601 bool Camera::IsProjectionMatrixAnimated() const
603 return !mFieldOfView.IsClean() ||
604 !mOrthographicSize.IsClean() ||
605 !mAspectRatio.IsClean();
608 void Camera::AddInitializeResetter(ResetterManager& manager) const
610 // Call base class initialize resetter
611 Node::AddInitializeResetter(manager);
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);
621 uint32_t Camera::UpdateViewMatrix(BufferIndex updateBufferIndex)
623 uint32_t retval(mUpdateViewFlag);
624 if(0u != mUpdateViewFlag)
626 if(COPY_PREVIOUS_MATRIX == mUpdateViewFlag)
628 // The projection matrix was updated in the previous frame; copy it
629 mViewMatrix.CopyPrevious(updateBufferIndex);
631 else // UPDATE_COUNT == mUpdateViewFlag
635 // camera orientation taken from node - i.e. look in abitrary, unconstrained direction
636 case Dali::Camera::FREE_LOOK:
638 Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
639 viewMatrix = GetWorldMatrix(updateBufferIndex);
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;
650 Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
651 Matrix oldViewMatrix(viewMatrix);
652 MatrixUtils::MultiplyTransformMatrix(viewMatrix, oldViewMatrix, mReflectionMtx);
656 mViewMatrix.SetDirty(updateBufferIndex);
660 // camera orientation constrained to look at a target
661 case Dali::Camera::LOOK_AT_TARGET:
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);
671 Vector3 up = orientation.Rotate(Vector3::YAXIS);
672 Vector4 position4 = Vector4(position);
673 Vector4 target4 = Vector4(mTargetPosition);
674 Vector4 up4 = Vector4(up);
678 Vector3 positionNew3;
679 Vector3 targetNewVector3;
683 VectorReflectedByPlane(positionNew, position4, mReflectionPlane);
684 VectorReflectedByPlane(targetNew, target4, mReflectionPlane);
685 VectorReflectedByPlane(upNew, up4, mReflectionPlane);
687 positionNew3 = Vector3(positionNew);
688 targetNewVector3 = Vector3(targetNew);
689 upNew3 = Vector3(upNew);
690 LookAt(viewMatrix, positionNew3, targetNewVector3, upNew3);
693 float* vZ = viewMatrix.AsFloat();
699 mReflectionEye = positionNew;
700 mUseReflectionClip = true;
704 LookAt(viewMatrix, position, mTargetPosition, orientation.Rotate(Vector3::YAXIS));
706 mViewMatrix.SetDirty(updateBufferIndex);
716 void Camera::UpdateFrustum(BufferIndex updateBufferIndex, bool normalize)
718 // Extract the clip matrix planes
719 Matrix clipMatrix(false); // Don't initialize.
720 MatrixUtils::MultiplyProjectionMatrix(clipMatrix, mViewMatrix[updateBufferIndex], mProjectionMatrix[updateBufferIndex]);
722 const float* cm = clipMatrix.AsFloat();
723 FrustumPlanes& planes = mFrustum[updateBufferIndex];
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];
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];
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];
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];
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];
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];
763 for(uint32_t i = 0; i < 6; ++i)
765 // Normalize planes to ensure correct bounding distance checking
766 Plane& plane = planes.mPlanes[i];
767 float l = 1.0f / plane.mNormal.Length();
769 plane.mDistance *= l;
771 planes.mSign[i] = Vector3(Sign(plane.mNormal.x), Sign(plane.mNormal.y), Sign(plane.mNormal.z));
776 for(uint32_t i = 0; i < 6; ++i)
778 planes.mSign[i] = Vector3(Sign(planes.mPlanes[i].mNormal.x), Sign(planes.mPlanes[i].mNormal.y), Sign(planes.mPlanes[i].mNormal.z));
781 mFrustum[updateBufferIndex ? 0 : 1] = planes;
784 bool Camera::CheckSphereInFrustum(BufferIndex bufferIndex, const Vector3& origin, float radius) const
786 const FrustumPlanes& planes = mFrustum[bufferIndex];
787 for(uint32_t i = 0; i < 6; ++i)
789 if((planes.mPlanes[i].mDistance + planes.mPlanes[i].mNormal.Dot(origin)) < -radius)
797 bool Camera::CheckAABBInFrustum(BufferIndex bufferIndex, const Vector3& origin, const Vector3& halfExtents) const
799 const FrustumPlanes& planes = mFrustum[bufferIndex];
800 for(uint32_t i = 0; i < 6; ++i)
802 if(planes.mPlanes[i].mNormal.Dot(origin + (halfExtents * planes.mSign[i])) > -(planes.mPlanes[i].mDistance))
811 Dali::Rect<int32_t> Camera::GetOrthographicClippingBox(BufferIndex bufferIndex) const
813 const float orthographicSize = mOrthographicSize[bufferIndex];
814 const float aspect = mAspectRatio[bufferIndex];
816 const float halfWidth = mProjectionDirection[0] == DevelCameraActor::ProjectionDirection::VERTICAL ? orthographicSize * aspect : orthographicSize;
817 const float halfHeight = mProjectionDirection[0] == DevelCameraActor::ProjectionDirection::VERTICAL ? orthographicSize : orthographicSize / aspect;
819 return Dali::Rect<int32_t>(-halfWidth, -halfHeight, halfWidth * 2.0f, halfHeight * 2.0f);
822 uint32_t Camera::UpdateProjection(BufferIndex updateBufferIndex)
824 uint32_t retval(mUpdateProjectionFlag);
825 // Early-exit if no update required
826 if(0u != mUpdateProjectionFlag)
828 Matrix& finalProjection = mFinalProjection[updateBufferIndex];
829 finalProjection.SetIdentity();
831 if(COPY_PREVIOUS_MATRIX == mUpdateProjectionFlag)
833 // The projection matrix was updated in the previous frame; copy it
834 mProjectionMatrix.CopyPrevious(updateBufferIndex);
836 finalProjection = mFinalProjection[updateBufferIndex ? 0 : 1];
838 else // UPDATE_COUNT == mUpdateProjectionFlag
840 switch(mProjectionMode[0])
842 case Dali::Camera::PERSPECTIVE_PROJECTION:
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],
853 //need to apply custom clipping plane
854 if(mUseReflectionClip)
856 Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
857 Matrix viewInv = viewMatrix;
861 Dali::Vector4 adjReflectPlane = mReflectionPlane;
862 float d = mReflectionPlane.Dot(mReflectionEye);
865 // Original eyesight was behind of mReflectionPlane. Reverse the plane.
866 adjReflectPlane = -adjReflectPlane;
869 Vector4 customClipping = viewInv * adjReflectPlane;
870 AdjustNearPlaneForPerspective(projectionMatrix, customClipping, mFarClippingPlane[updateBufferIndex]);
874 case Dali::Camera::ORTHOGRAPHIC_PROJECTION:
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],
885 //need to apply custom clipping plane
886 if(mUseReflectionClip)
888 Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
889 Matrix viewInv = viewMatrix;
893 Dali::Vector4 adjReflectPlane = mReflectionPlane;
894 float d = mReflectionPlane.Dot(mReflectionEye);
897 // Original eyesight was behind of mReflectionPlane. Reverse the plane.
898 adjReflectPlane = -adjReflectPlane;
901 Vector4 customClipping = viewInv * adjReflectPlane;
902 AdjustNearPlaneForOrthographic(projectionMatrix, customClipping, mFarClippingPlane[updateBufferIndex]);
908 mProjectionMatrix.SetDirty(updateBufferIndex);
910 Quaternion rotationAngle;
911 switch(mProjectionRotation)
915 rotationAngle = Quaternion(Dali::ANGLE_90, Vector3::ZAXIS);
920 rotationAngle = Quaternion(Dali::ANGLE_180, Vector3::ZAXIS);
925 rotationAngle = Quaternion(Dali::ANGLE_270, Vector3::ZAXIS);
929 rotationAngle = Quaternion(Dali::ANGLE_0, Vector3::ZAXIS);
933 // TODO : Can't we make finalProjection without matrix multiply?
934 MatrixUtils::Multiply(finalProjection, mProjectionMatrix.Get(updateBufferIndex), rotationAngle);
936 --mUpdateProjectionFlag;
941 } // namespace SceneGraph
943 } // namespace Internal