/*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// INTERNAL INCLUDES
#include <dali/integration-api/debug.h>
+#include <dali/internal/common/matrix-utils.h>
+#include <dali/internal/common/memory-pool-object-allocator.h>
#include <dali/internal/update/nodes/node.h>
#include <dali/public-api/common/dali-common.h>
#include <dali/public-api/math/math-utils.h>
{
namespace
{
+//Memory pool used to allocate new camera. Memory used by this pool will be released when shutting down DALi
+MemoryPoolObjectAllocator<Camera>& GetCameraMemoryPool()
+{
+ static MemoryPoolObjectAllocator<Camera> gCameraMemoryPool;
+ return gCameraMemoryPool;
+}
+
template<typename T>
T Sign(T value)
{
Frustum(result, -frustumW, frustumW, -frustumH, frustumH, near, far, invertYAxis);
}
-void Orthographic(Matrix& result, float left, float right, float bottom, float top, float near, float far, bool invertYAxis)
+void Orthographic(Matrix& result, Dali::DevelCameraActor::ProjectionDirection orthographicDir, float orthographicSize, float aspect, float near, float far, bool invertYAxis)
{
- if(Equals(right, left) || Equals(top, bottom) || Equals(far, near))
+ if(EqualsZero(orthographicSize) || EqualsZero(aspect) || Equals(far, near))
{
DALI_LOG_ERROR("Cannot create orthographic projection matrix with a zero dimension.\n");
DALI_ASSERT_DEBUG("Cannot create orthographic projection matrix with a zero dimension.");
return;
}
- float deltaX = right - left;
- float deltaY = invertYAxis ? bottom - top : top - bottom;
+ float halfDeltaX;
+ float halfDeltaY;
+ if(orthographicDir == Dali::DevelCameraActor::ProjectionDirection::VERTICAL)
+ {
+ halfDeltaY = orthographicSize;
+ halfDeltaX = halfDeltaY * aspect;
+ }
+ else
+ {
+ halfDeltaX = orthographicSize;
+ halfDeltaY = halfDeltaX / aspect;
+ }
+
float deltaZ = far - near;
float* m = result.AsFloat();
- m[0] = -2.0f / deltaX;
- m[1] = 0.0f;
- m[2] = 0.0f;
- m[3] = 0.0f;
+
+ m[0] = -1.0f / halfDeltaX;
+ m[1] = 0.0f;
+ m[2] = 0.0f;
+ m[3] = 0.0f;
m[4] = 0.0f;
- m[5] = -2.0f / deltaY;
+ m[5] = (invertYAxis ? 1.0f : -1.0f) / halfDeltaY;
m[6] = 0.0f;
m[7] = 0.0f;
m[9] = 0.0f;
m[10] = 2.0f / deltaZ;
m[11] = 0.0f;
- m[12] = -(right + left) / deltaX;
- m[13] = -(top + bottom) / deltaY;
+
+ m[12] = 0.0f;
+ m[13] = 0.0f;
m[14] = -(near + far) / deltaZ;
m[15] = 1.0f;
}
const Dali::DevelCameraActor::ProjectionDirection Camera::DEFAULT_PROJECTION_DIRECTION(Dali::DevelCameraActor::VERTICAL);
const bool Camera::DEFAULT_INVERT_Y_AXIS(false);
const float Camera::DEFAULT_FIELD_OF_VIEW(45.0f * (Math::PI / 180.0f));
-const float Camera::DEFAULT_ASPECT_RATIO(800.0f / 480.0f);
-const float Camera::DEFAULT_LEFT_CLIPPING_PLANE(-240.0f);
-const float Camera::DEFAULT_RIGHT_CLIPPING_PLANE(240.0f);
-const float Camera::DEFAULT_TOP_CLIPPING_PLANE(-400.0f);
-const float Camera::DEFAULT_BOTTOM_CLIPPING_PLANE(400.0f);
-const float Camera::DEFAULT_NEAR_CLIPPING_PLANE(800.0f); // default height of the screen
+const float Camera::DEFAULT_ORTHOGRAPHIC_SIZE(400.0f); // half of default height of the screen
+const float Camera::DEFAULT_ASPECT_RATIO(480.0f / 800.0f); // default width / default height of the screen
+const float Camera::DEFAULT_NEAR_CLIPPING_PLANE(800.0f); // default height of the screen
const float Camera::DEFAULT_FAR_CLIPPING_PLANE(DEFAULT_NEAR_CLIPPING_PLANE + 2.f * DEFAULT_NEAR_CLIPPING_PLANE);
const Vector3 Camera::DEFAULT_TARGET_POSITION(0.0f, 0.0f, 0.0f);
Camera::Camera()
-: mUpdateViewFlag(UPDATE_COUNT),
+: Node(),
+ mUpdateViewFlag(UPDATE_COUNT),
mUpdateProjectionFlag(UPDATE_COUNT),
mProjectionRotation(0),
- mNode(nullptr),
mType(DEFAULT_TYPE),
mProjectionMode(DEFAULT_MODE),
mProjectionDirection(DEFAULT_PROJECTION_DIRECTION),
mInvertYAxis(DEFAULT_INVERT_Y_AXIS),
mFieldOfView(DEFAULT_FIELD_OF_VIEW),
+ mOrthographicSize(DEFAULT_ORTHOGRAPHIC_SIZE),
mAspectRatio(DEFAULT_ASPECT_RATIO),
- mLeftClippingPlane(DEFAULT_LEFT_CLIPPING_PLANE),
- mRightClippingPlane(DEFAULT_RIGHT_CLIPPING_PLANE),
- mTopClippingPlane(DEFAULT_TOP_CLIPPING_PLANE),
- mBottomClippingPlane(DEFAULT_BOTTOM_CLIPPING_PLANE),
mNearClippingPlane(DEFAULT_NEAR_CLIPPING_PLANE),
mFarClippingPlane(DEFAULT_FAR_CLIPPING_PLANE),
mTargetPosition(DEFAULT_TARGET_POSITION),
mInverseViewProjection(Matrix::IDENTITY),
mFinalProjection(Matrix::IDENTITY)
{
+ // set a flag the node to say this is a camera
+ mIsCamera = true;
}
Camera* Camera::New()
{
- return new Camera();
+ return new(GetCameraMemoryPool().AllocateRawThreadSafe()) Camera();
}
Camera::~Camera() = default;
-void Camera::SetNode(const Node* node)
-{
- mNode = node;
-}
-
-const Node* Camera::GetNode() const
+void Camera::operator delete(void* ptr)
{
- return mNode;
+ GetCameraMemoryPool().FreeThreadSafe(static_cast<Camera*>(ptr));
}
void Camera::SetType(Dali::Camera::Type type)
mUpdateProjectionFlag = UPDATE_COUNT;
}
-void Camera::SetFieldOfView(float fieldOfView)
-{
- mFieldOfView = fieldOfView;
- mUpdateProjectionFlag = UPDATE_COUNT;
-}
-
-void Camera::SetAspectRatio(float aspectRatio)
-{
- mAspectRatio = aspectRatio;
- mUpdateProjectionFlag = UPDATE_COUNT;
-}
-
-void Camera::SetLeftClippingPlane(float leftClippingPlane)
+void Camera::BakeFieldOfView(BufferIndex updateBufferIndex, float fieldOfView)
{
- mLeftClippingPlane = leftClippingPlane;
+ mFieldOfView.Bake(updateBufferIndex, fieldOfView);
mUpdateProjectionFlag = UPDATE_COUNT;
}
-void Camera::SetRightClippingPlane(float rightClippingPlane)
+void Camera::BakeOrthographicSize(BufferIndex updateBufferIndex, float orthographicSize)
{
- mRightClippingPlane = rightClippingPlane;
+ mOrthographicSize.Bake(updateBufferIndex, orthographicSize);
mUpdateProjectionFlag = UPDATE_COUNT;
}
-void Camera::SetTopClippingPlane(float topClippingPlane)
+void Camera::BakeAspectRatio(BufferIndex updateBufferIndex, float aspectRatio)
{
- mTopClippingPlane = topClippingPlane;
- mUpdateProjectionFlag = UPDATE_COUNT;
-}
-
-void Camera::SetBottomClippingPlane(float bottomClippingPlane)
-{
- mBottomClippingPlane = bottomClippingPlane;
+ mAspectRatio.Bake(updateBufferIndex, aspectRatio);
mUpdateProjectionFlag = UPDATE_COUNT;
}
return mFinalProjection[bufferIndex];
}
+const PropertyBase* Camera::GetFieldOfView() const
+{
+ return &mFieldOfView;
+}
+
+const PropertyBase* Camera::GetOrthographicSize() const
+{
+ return &mOrthographicSize;
+}
+
+const PropertyBase* Camera::GetAspectRatio() const
+{
+ return &mAspectRatio;
+}
+
const PropertyInputImpl* Camera::GetProjectionMatrix() const
{
return &mProjectionMatrix;
void Camera::Update(BufferIndex updateBufferIndex)
{
- // if owning node has changes in world position we need to update camera for next 2 frames
- if(mNode->IsLocalMatrixDirty())
+ // if this has changes in world position we need to update camera for next 2 frames
+ if(IsLocalMatrixDirty())
{
mUpdateViewFlag = UPDATE_COUNT;
}
- if(mNode->GetDirtyFlags() & NodePropertyFlags::VISIBLE)
+ if(GetDirtyFlags() & NodePropertyFlags::VISIBLE)
{
// If the visibility changes, the projection matrix needs to be re-calculated.
// It may happen the first time an actor is rendered it's rendered only once and becomes invisible,
mUpdateProjectionFlag = UPDATE_COUNT;
}
+ // If projection matrix relative properties are animated now, flag change.
+ if(IsProjectionMatrixAnimated())
+ {
+ mUpdateProjectionFlag = UPDATE_COUNT;
+ }
+
// if either matrix changed, we need to recalculate the inverse matrix for hit testing to work
uint32_t viewUpdateCount = UpdateViewMatrix(updateBufferIndex);
uint32_t projectionUpdateCount = UpdateProjection(updateBufferIndex);
if(viewUpdateCount > COPY_PREVIOUS_MATRIX || projectionUpdateCount > COPY_PREVIOUS_MATRIX)
{
// either has actually changed so recalculate
- Matrix::Multiply(mInverseViewProjection[updateBufferIndex], mViewMatrix[updateBufferIndex], mProjectionMatrix[updateBufferIndex]);
+ MatrixUtils::Multiply(mInverseViewProjection[updateBufferIndex], mViewMatrix[updateBufferIndex], mProjectionMatrix[updateBufferIndex]);
UpdateFrustum(updateBufferIndex);
// ignore the error, if the view projection is incorrect (non inversible) then you will have tough times anyways
}
}
-bool Camera::ViewMatrixUpdated()
+bool Camera::ViewMatrixUpdated() const
{
return 0u != mUpdateViewFlag;
}
+bool Camera::IsProjectionMatrixAnimated() const
+{
+ return !mFieldOfView.IsClean() ||
+ !mOrthographicSize.IsClean() ||
+ !mAspectRatio.IsClean();
+}
+
uint32_t Camera::UpdateViewMatrix(BufferIndex updateBufferIndex)
{
uint32_t retval(mUpdateViewFlag);
case Dali::Camera::FREE_LOOK:
{
Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
- viewMatrix = mNode->GetWorldMatrix(updateBufferIndex);
+ viewMatrix = GetWorldMatrix(updateBufferIndex);
if(mUseReflection)
{
- const Matrix& owningNodeMatrix(mNode->GetWorldMatrix(updateBufferIndex));
+ const Matrix& owningNodeMatrix(GetWorldMatrix(updateBufferIndex));
Vector3 position{}, scale{};
Quaternion orientation{};
owningNodeMatrix.GetTransformComponents(position, orientation, scale);
Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
Matrix oldViewMatrix(viewMatrix);
- Matrix::Multiply(viewMatrix, oldViewMatrix, mReflectionMtx);
+ MatrixUtils::Multiply(viewMatrix, oldViewMatrix, mReflectionMtx);
}
viewMatrix.Invert();
// camera orientation constrained to look at a target
case Dali::Camera::LOOK_AT_TARGET:
{
- const Matrix& owningNodeMatrix(mNode->GetWorldMatrix(updateBufferIndex));
+ const Matrix& owningNodeMatrix(GetWorldMatrix(updateBufferIndex));
Vector3 position, scale;
Quaternion orientation;
owningNodeMatrix.GetTransformComponents(position, orientation, scale);
Matrix oldViewMatrix(viewMatrix);
Matrix tmp;
tmp.SetIdentityAndScale(Vector3(-1.0, 1.0, 1.0));
- Matrix::Multiply(viewMatrix, oldViewMatrix, tmp);
+ MatrixUtils::Multiply(viewMatrix, oldViewMatrix, tmp);
mReflectionEye = positionNew;
mUseReflectionClip = true;
{
// Extract the clip matrix planes
Matrix clipMatrix;
- Matrix::Multiply(clipMatrix, mViewMatrix[updateBufferIndex], mProjectionMatrix[updateBufferIndex]);
+ MatrixUtils::Multiply(clipMatrix, mViewMatrix[updateBufferIndex], mProjectionMatrix[updateBufferIndex]);
const float* cm = clipMatrix.AsFloat();
FrustumPlanes& planes = mFrustum[updateBufferIndex];
mFrustum[updateBufferIndex ? 0 : 1] = planes;
}
-bool Camera::CheckSphereInFrustum(BufferIndex bufferIndex, const Vector3& origin, float radius)
+bool Camera::CheckSphereInFrustum(BufferIndex bufferIndex, const Vector3& origin, float radius) const
{
const FrustumPlanes& planes = mFrustum[bufferIndex];
for(uint32_t i = 0; i < 6; ++i)
return true;
}
-bool Camera::CheckAABBInFrustum(BufferIndex bufferIndex, const Vector3& origin, const Vector3& halfExtents)
+bool Camera::CheckAABBInFrustum(BufferIndex bufferIndex, const Vector3& origin, const Vector3& halfExtents) const
{
const FrustumPlanes& planes = mFrustum[bufferIndex];
for(uint32_t i = 0; i < 6; ++i)
}
return true;
}
+Dali::Rect<int32_t> Camera::GetOrthographicClippingBox(BufferIndex bufferIndex) const
+{
+ const float orthographicSize = mOrthographicSize[bufferIndex];
+ const float aspect = mAspectRatio[bufferIndex];
+
+ const float halfWidth = mProjectionDirection == DevelCameraActor::ProjectionDirection::VERTICAL ? orthographicSize * aspect : orthographicSize;
+ const float halfHeight = mProjectionDirection == DevelCameraActor::ProjectionDirection::VERTICAL ? orthographicSize : orthographicSize / aspect;
+
+ return Dali::Rect<int32_t>(-halfWidth, -halfHeight, halfWidth * 2.0f, halfHeight * 2.0f);
+}
uint32_t Camera::UpdateProjection(BufferIndex updateBufferIndex)
{
Matrix& projectionMatrix = mProjectionMatrix.Get(updateBufferIndex);
Perspective(projectionMatrix,
mProjectionDirection,
- mFieldOfView,
- mAspectRatio,
+ mFieldOfView[updateBufferIndex],
+ mAspectRatio[updateBufferIndex],
mNearClippingPlane,
mFarClippingPlane,
mInvertYAxis);
matZ.SetIdentity();
float* vZ = matZ.AsFloat();
vZ[10] = -vZ[10];
- Matrix::Multiply(projectionMatrix, projectionMatrix, matZ);
+ MatrixUtils::Multiply(projectionMatrix, projectionMatrix, matZ);
}
break;
}
{
Matrix& projectionMatrix = mProjectionMatrix.Get(updateBufferIndex);
Orthographic(projectionMatrix,
- mLeftClippingPlane,
- mRightClippingPlane,
- mBottomClippingPlane,
- mTopClippingPlane,
+ mProjectionDirection,
+ mOrthographicSize[updateBufferIndex],
+ mAspectRatio[updateBufferIndex],
mNearClippingPlane,
mFarClippingPlane,
mInvertYAxis);
rotation.SetIdentity();
rotation.SetTransformComponents(Vector3(1.0f, 1.0f, 1.0f), rotationAngle, Vector3(0.0f, 0.0f, 0.0f));
- Matrix::Multiply(finalProjection, mProjectionMatrix.Get(updateBufferIndex), rotation);
+ MatrixUtils::Multiply(finalProjection, mProjectionMatrix.Get(updateBufferIndex), rotation);
}
--mUpdateProjectionFlag;
}