Deprecate Plane Distance setter + Implement OrthographicSize + Animatable AspectRatio
[platform/core/uifw/dali-core.git] / dali / internal / update / render-tasks / scene-graph-camera.cpp
index 158ed37..586d7cc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 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>
-#include <dali/internal/update/nodes/node.h>
 
 namespace // unnamed namespace
 {
-const unsigned int UPDATE_COUNT        = 2u;  // Update projection or view matrix this many frames after a change
-const unsigned int COPY_PREVIOUS_MATRIX = 1u; // Copy view or projection matrix from previous frame
-}
+const uint32_t UPDATE_COUNT         = 2u; // Update projection or view matrix this many frames after a change
+const uint32_t COPY_PREVIOUS_MATRIX = 1u; // Copy view or projection matrix from previous frame
+
+//For reflection and clipping plane
+const float REFLECTION_NORMALIZED_DEVICE_COORDINATE_PARAMETER_A = 2.0f;
+const float REFLECTION_NORMALIZED_DEVICE_COORDINATE_PARAMETER_D = 1.0f;
+} // namespace
 
 namespace Dali
 {
-
 namespace Internal
 {
-
 namespace SceneGraph
 {
-
 namespace
 {
+//Memory pool used to allocate new camera. Memory used by this pool will be released when shutting down DALi
+MemoryPoolObjectAllocator<Camera> gCameraMemoryPool;
+
+template<typename T>
+T Sign(T value)
+{
+  return T(T(0) < value) - T(value < T(0));
+}
 
 void LookAt(Matrix& result, const Vector3& eye, const Vector3& target, const Vector3& up)
 {
@@ -59,13 +70,12 @@ void LookAt(Matrix& result, const Vector3& eye, const Vector3& target, const Vec
   result.SetInverseTransformComponents(vX, vY, vZ, eye);
 }
 
-
 void Frustum(Matrix& result, float left, float right, float bottom, float top, float near, float far, bool invertYAxis)
 {
   float deltaZ = far - near;
-  if ((near <= 0.0f) || (far <= 0.0f) || Equals(right, left) || Equals(bottom, top) || (deltaZ <= 0.0f))
+  if((near <= 0.0f) || (far <= 0.0f) || Equals(right, left) || Equals(bottom, top) || (deltaZ <= 0.0f))
   {
-    DALI_LOG_ERROR("Invalid parameters passed into Frustum!");
+    DALI_LOG_ERROR("Invalid parameters passed into Frustum!\n");
     DALI_ASSERT_DEBUG("Invalid parameters passed into Frustum!");
     return;
   }
@@ -76,14 +86,14 @@ void Frustum(Matrix& result, float left, float right, float bottom, float top, f
   result.SetIdentity();
 
   float* m = result.AsFloat();
-  m[0] = -2.0f * near / deltaX;
+  m[0]     = -2.0f * near / deltaX;
   m[1] = m[2] = m[3] = 0.0f;
 
   m[5] = -2.0f * near / deltaY;
   m[4] = m[6] = m[7] = 0.0f;
 
-  m[8] = (right + left) / deltaX;
-  m[9] = (top + bottom) / deltaY;
+  m[8]  = (right + left) / deltaX;
+  m[9]  = (top + bottom) / deltaY;
   m[10] = (near + far) / deltaZ;
   m[11] = 1.0f;
 
@@ -91,187 +101,279 @@ void Frustum(Matrix& result, float left, float right, float bottom, float top, f
   m[12] = m[13] = m[15] = 0.0f;
 }
 
-void Perspective(Matrix& result, float fovy, float aspect, float near, float far, bool invertYAxis, const Vector2& stereoBias )
+void Perspective(Matrix& result, Dali::DevelCameraActor::ProjectionDirection fovDir, float fov, float aspect, float near, float far, bool invertYAxis)
 {
-  float frustumH = tanf( fovy * 0.5f ) * near;
-  float frustumW = frustumH * aspect;
-  Vector2 bias = stereoBias * 0.5f;
+  float frustumH;
+  float frustumW;
+  if(fovDir == Dali::DevelCameraActor::ProjectionDirection::VERTICAL)
+  {
+    frustumH = tanf(fov * 0.5f) * near;
+    frustumW = frustumH * aspect;
+  }
+  else
+  {
+    frustumW = tanf(fov * 0.5f) * near;
+    frustumH = frustumW / aspect;
+  }
 
-  Frustum(result, -(frustumW + bias.x), frustumW - bias.x, -(frustumH + bias.y), frustumH - bias.y, near, far, invertYAxis);
+  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." );
-    DALI_ASSERT_DEBUG( "Cannot create orthographic projection matrix with a zero dimension." );
+    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;
+  float* m = result.AsFloat();
+
+  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[8] = 0.0f;
-  m[9] = 0.0f;
+  m[8]  = 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[14] = -(near + far)   / deltaZ;
+
+  m[12] = 0.0f;
+  m[13] = 0.0f;
+  m[14] = -(near + far) / deltaZ;
   m[15] = 1.0f;
 }
 
 } // unnamed namespace
 
-const Dali::Camera::Type Camera::DEFAULT_TYPE( Dali::Camera::FREE_LOOK );
-const Dali::Camera::ProjectionMode Camera::DEFAULT_MODE( Dali::Camera::PERSPECTIVE_PROJECTION );
-const bool  Camera::DEFAULT_INVERT_Y_AXIS( false );
-const float Camera::DEFAULT_FIELD_OF_VIEW( 45.0f*(M_PI/180.0f) );
-const float Camera::DEFAULT_ASPECT_RATIO( 4.0f/3.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_FAR_CLIPPING_PLANE( DEFAULT_NEAR_CLIPPING_PLANE + 2.f * DEFAULT_NEAR_CLIPPING_PLANE );
-const Vector2 Camera::DEFAULT_STEREO_BIAS( 0.0f, 0.0f );
-const Vector3 Camera::DEFAULT_TARGET_POSITION( 0.0f, 0.0f, 0.0f );
-
+const Dali::Camera::Type                          Camera::DEFAULT_TYPE(Dali::Camera::FREE_LOOK);
+const Dali::Camera::ProjectionMode                Camera::DEFAULT_MODE(Dali::Camera::PERSPECTIVE_PROJECTION);
+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_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 ),
-  mUpdateProjectionFlag( UPDATE_COUNT ),
-  mType( DEFAULT_TYPE ),
-  mProjectionMode( DEFAULT_MODE ),
-  mInvertYAxis( DEFAULT_INVERT_Y_AXIS ),
-  mFieldOfView( DEFAULT_FIELD_OF_VIEW ),
-  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 ),
-  mStereoBias( DEFAULT_STEREO_BIAS ),
-  mTargetPosition( DEFAULT_TARGET_POSITION ),
+: Node(),
+  mUpdateViewFlag(UPDATE_COUNT),
+  mUpdateProjectionFlag(UPDATE_COUNT),
+  mProjectionRotation(0),
+  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),
+  mNearClippingPlane(DEFAULT_NEAR_CLIPPING_PLANE),
+  mFarClippingPlane(DEFAULT_FAR_CLIPPING_PLANE),
+  mTargetPosition(DEFAULT_TARGET_POSITION),
   mViewMatrix(),
   mProjectionMatrix(),
-  mInverseViewProjection( Matrix::IDENTITY )
+  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(gCameraMemoryPool.AllocateRawThreadSafe()) Camera();
 }
 
-Camera::~Camera()
+Camera::~Camera() = default;
+
+void Camera::operator delete(void* ptr)
 {
+  gCameraMemoryPool.FreeThreadSafe(static_cast<Camera*>(ptr));
 }
 
-void Camera::SetType( Dali::Camera::Type type )
+void Camera::SetType(Dali::Camera::Type type)
 {
   mType = type;
 }
 
-void Camera::SetProjectionMode( Dali::Camera::ProjectionMode mode )
+void Camera::SetProjectionMode(Dali::Camera::ProjectionMode mode)
 {
-  mProjectionMode = mode;
+  mProjectionMode       = mode;
   mUpdateProjectionFlag = UPDATE_COUNT;
 }
 
-void Camera::SetInvertYAxis( bool invertYAxis )
+void Camera::SetProjectionDirection(Dali::DevelCameraActor::ProjectionDirection direction)
 {
-  mInvertYAxis = invertYAxis;
+  mProjectionDirection  = direction;
   mUpdateProjectionFlag = UPDATE_COUNT;
 }
 
-void Camera::SetFieldOfView( float fieldOfView )
+void Camera::SetInvertYAxis(bool invertYAxis)
 {
-  mFieldOfView = fieldOfView;
+  mInvertYAxis          = invertYAxis;
   mUpdateProjectionFlag = UPDATE_COUNT;
 }
 
-void Camera::SetAspectRatio( float aspectRatio )
+void Camera::BakeFieldOfView(BufferIndex updateBufferIndex, float fieldOfView)
 {
-  mAspectRatio = aspectRatio;
+  mFieldOfView.Bake(updateBufferIndex, fieldOfView);
   mUpdateProjectionFlag = UPDATE_COUNT;
 }
 
-void Camera::SetStereoBias( const Vector2& stereoBias )
+void Camera::BakeOrthographicSize(BufferIndex updateBufferIndex, float orthographicSize)
 {
-  mStereoBias = stereoBias;
+  mOrthographicSize.Bake(updateBufferIndex, orthographicSize);
   mUpdateProjectionFlag = UPDATE_COUNT;
 }
 
-void Camera::SetLeftClippingPlane( float leftClippingPlane )
+void Camera::BakeAspectRatio(BufferIndex updateBufferIndex, float aspectRatio)
 {
-  mLeftClippingPlane = leftClippingPlane;
+  mAspectRatio.Bake(updateBufferIndex, aspectRatio);
   mUpdateProjectionFlag = UPDATE_COUNT;
 }
 
-void Camera::SetRightClippingPlane( float rightClippingPlane )
+void Camera::SetNearClippingPlane(float nearClippingPlane)
 {
-  mRightClippingPlane = rightClippingPlane;
+  mNearClippingPlane    = nearClippingPlane;
   mUpdateProjectionFlag = UPDATE_COUNT;
 }
 
-void Camera::SetTopClippingPlane( float topClippingPlane )
+void Camera::SetFarClippingPlane(float farClippingPlane)
 {
-  mTopClippingPlane = topClippingPlane;
+  mFarClippingPlane     = farClippingPlane;
   mUpdateProjectionFlag = UPDATE_COUNT;
 }
 
-void Camera::SetBottomClippingPlane( float bottomClippingPlane )
+void Camera::SetTargetPosition(const Vector3& targetPosition)
 {
-  mBottomClippingPlane = bottomClippingPlane;
-  mUpdateProjectionFlag = UPDATE_COUNT;
+  mTargetPosition = targetPosition;
+  mUpdateViewFlag = UPDATE_COUNT;
 }
 
-void Camera::SetNearClippingPlane( float nearClippingPlane )
+void VectorReflectedByPlane(Vector4& out, Vector4& in, Vector4& plane)
 {
-  mNearClippingPlane = nearClippingPlane;
-  mUpdateProjectionFlag = UPDATE_COUNT;
+  float d = float(2.0) * plane.Dot(in);
+  out.x   = static_cast<float>(in.x - plane.x * d);
+  out.y   = static_cast<float>(in.y - plane.y * d);
+  out.z   = static_cast<float>(in.z - plane.z * d);
+  out.w   = static_cast<float>(in.w - plane.w * d);
 }
 
-void Camera::SetFarClippingPlane( float farClippingPlane )
+void Camera::AdjustNearPlaneForPerspective(Matrix& perspective, const Vector4& clipPlane)
 {
-  mFarClippingPlane = farClippingPlane;
+  Vector4 q;
+  float*  v = perspective.AsFloat();
+
+  q.x = (Sign(clipPlane.x) + v[8]) / v[0];
+  q.y = (Sign(clipPlane.y) + v[9]) / v[5];
+  q.z = -1.0f;
+  q.w = (1.0f + v[10]) / v[14];
+
+  // Calculate the scaled plane vector
+  Vector4 c = clipPlane * (REFLECTION_NORMALIZED_DEVICE_COORDINATE_PARAMETER_A / q.Dot(clipPlane));
+
+  // Replace the third row of the projection v
+  v[2]  = c.x;
+  v[6]  = c.y;
+  v[10] = c.z + REFLECTION_NORMALIZED_DEVICE_COORDINATE_PARAMETER_D;
+  v[14] = c.w;
+}
+
+void Camera::SetReflectByPlane(const Vector4& plane)
+{
+  float* v    = mReflectionMtx.AsFloat();
+  float  _2ab = -2.0f * plane.x * plane.y;
+  float  _2ac = -2.0f * plane.x * plane.z;
+  float  _2bc = -2.0f * plane.y * plane.z;
+
+  v[0] = 1.0f - 2.0f * plane.x * plane.x;
+  v[1] = _2ab;
+  v[2] = _2ac;
+  v[3] = 0.0f;
+
+  v[4] = _2ab;
+  v[5] = 1.0f - 2.0f * plane.y * plane.y;
+  v[6] = _2bc;
+  v[7] = 0.0f;
+
+  v[8]  = _2ac;
+  v[9]  = _2bc;
+  v[10] = 1.0f - 2.0f * plane.z * plane.z;
+  v[11] = 0.0f;
+
+  v[12] = -2 * plane.x * plane.w;
+  v[13] = -2 * plane.y * plane.w;
+  v[14] = -2 * plane.z * plane.w;
+  v[15] = 1.0f;
+
+  mUseReflection   = true;
+  mReflectionPlane = plane;
+  mUpdateViewFlag  = UPDATE_COUNT;
+}
+
+void Camera::RotateProjection(int rotationAngle)
+{
+  mProjectionRotation   = rotationAngle;
   mUpdateProjectionFlag = UPDATE_COUNT;
 }
 
-void Camera::SetTargetPosition( const Vector3& targetPosition )
+const Matrix& Camera::GetProjectionMatrix(BufferIndex bufferIndex) const
 {
-  mTargetPosition = targetPosition;
-  mUpdateViewFlag = UPDATE_COUNT;
+  return mProjectionMatrix[bufferIndex];
+}
+
+const Matrix& Camera::GetViewMatrix(BufferIndex bufferIndex) const
+{
+  return mViewMatrix[bufferIndex];
+}
+
+const Matrix& Camera::GetInverseViewProjectionMatrix(BufferIndex bufferIndex) const
+{
+  return mInverseViewProjection[bufferIndex];
+}
+
+const Matrix& Camera::GetFinalProjectionMatrix(BufferIndex bufferIndex) const
+{
+  return mFinalProjection[bufferIndex];
 }
 
-const Matrix& Camera::GetProjectionMatrix( BufferIndex bufferIndex ) const
+const PropertyBase* Camera::GetFieldOfView() const
 {
-  return mProjectionMatrix[ bufferIndex ];
+  return &mFieldOfView;
 }
 
-const Matrix& Camera::GetViewMatrix( BufferIndex bufferIndex ) const
+const PropertyBase* Camera::GetOrthographicSize() const
 {
-  return mViewMatrix[ bufferIndex ];
+  return &mOrthographicSize;
 }
 
-const Matrix& Camera::GetInverseViewProjectionMatrix( BufferIndex bufferIndex ) const
+const PropertyBase* Camera::GetAspectRatio() const
 {
-  return mInverseViewProjection[ bufferIndex ];
+  return &mAspectRatio;
 }
 
 const PropertyInputImpl* Camera::GetProjectionMatrix() const
@@ -284,14 +386,14 @@ const PropertyInputImpl* Camera::GetViewMatrix() const
   return &mViewMatrix;
 }
 
-void Camera::Update( BufferIndex updateBufferIndex, const Node& owningNode )
+void Camera::Update(BufferIndex updateBufferIndex)
 {
-  // if owning node has changes in world position we need to update camera for next 2 frames
-  if( owningNode.IsLocalMatrixDirty() )
+  // if this has changes in world position we need to update camera for next 2 frames
+  if(IsLocalMatrixDirty())
   {
     mUpdateViewFlag = UPDATE_COUNT;
   }
-  if( owningNode.GetDirtyFlags() & VisibleFlag )
+  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,
@@ -300,67 +402,131 @@ void Camera::Update( BufferIndex updateBufferIndex, const Node& owningNode )
     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
-  unsigned int viewUpdateCount = UpdateViewMatrix( updateBufferIndex, owningNode );
-  unsigned int projectionUpdateCount = UpdateProjection( updateBufferIndex );
+  uint32_t viewUpdateCount       = UpdateViewMatrix(updateBufferIndex);
+  uint32_t projectionUpdateCount = UpdateProjection(updateBufferIndex);
 
   // if model or view matrix changed we need to either recalculate the inverse VP or copy previous
-  if( viewUpdateCount > COPY_PREVIOUS_MATRIX || projectionUpdateCount > COPY_PREVIOUS_MATRIX )
+  if(viewUpdateCount > COPY_PREVIOUS_MATRIX || projectionUpdateCount > COPY_PREVIOUS_MATRIX)
   {
     // either has actually changed so recalculate
-    Matrix::Multiply( mInverseViewProjection[ updateBufferIndex ], mViewMatrix[ updateBufferIndex ], mProjectionMatrix[ updateBufferIndex ] );
-    UpdateFrustum( 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
-    static_cast< void >( mInverseViewProjection[ updateBufferIndex ].Invert() );
+    static_cast<void>(mInverseViewProjection[updateBufferIndex].Invert());
   }
-  else if( viewUpdateCount == COPY_PREVIOUS_MATRIX || projectionUpdateCount == COPY_PREVIOUS_MATRIX )
+  else if(viewUpdateCount == COPY_PREVIOUS_MATRIX || projectionUpdateCount == COPY_PREVIOUS_MATRIX)
   {
     // neither has actually changed, but we might copied previous frames value so need to
     // copy the previous inverse and frustum as well
     mInverseViewProjection[updateBufferIndex] = mInverseViewProjection[updateBufferIndex ? 0 : 1];
-    mFrustum[ updateBufferIndex ] = mFrustum[ updateBufferIndex ? 0 : 1 ];
+    mFrustum[updateBufferIndex]               = mFrustum[updateBufferIndex ? 0 : 1];
   }
 }
 
-bool Camera::ViewMatrixUpdated()
+bool Camera::ViewMatrixUpdated() const
 {
   return 0u != mUpdateViewFlag;
 }
 
-unsigned int Camera::UpdateViewMatrix( BufferIndex updateBufferIndex, const Node& owningNode )
+bool Camera::IsProjectionMatrixAnimated() const
 {
-  unsigned int retval( mUpdateViewFlag );
-  if( 0u != mUpdateViewFlag )
+  return !mFieldOfView.IsClean() ||
+         !mOrthographicSize.IsClean() ||
+         !mAspectRatio.IsClean();
+}
+
+uint32_t Camera::UpdateViewMatrix(BufferIndex updateBufferIndex)
+{
+  uint32_t retval(mUpdateViewFlag);
+  if(0u != mUpdateViewFlag)
   {
-    if( COPY_PREVIOUS_MATRIX == mUpdateViewFlag )
+    if(COPY_PREVIOUS_MATRIX == mUpdateViewFlag)
     {
       // The projection matrix was updated in the previous frame; copy it
-      mViewMatrix.CopyPrevious( updateBufferIndex );
+      mViewMatrix.CopyPrevious(updateBufferIndex);
     }
     else // UPDATE_COUNT == mUpdateViewFlag
     {
-      switch( mType )
+      switch(mType)
       {
         // camera orientation taken from node - i.e. look in abitrary, unconstrained direction
         case Dali::Camera::FREE_LOOK:
         {
-          Matrix& viewMatrix = mViewMatrix.Get( updateBufferIndex );
-          viewMatrix = owningNode.GetWorldMatrix( updateBufferIndex );
+          Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
+          viewMatrix         = GetWorldMatrix(updateBufferIndex);
+
+          if(mUseReflection)
+          {
+            const Matrix& owningNodeMatrix(GetWorldMatrix(updateBufferIndex));
+            Vector3       position{}, scale{};
+            Quaternion    orientation{};
+            owningNodeMatrix.GetTransformComponents(position, orientation, scale);
+            mReflectionEye     = position;
+            mUseReflectionClip = true;
+
+            Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
+            Matrix  oldViewMatrix(viewMatrix);
+            MatrixUtils::Multiply(viewMatrix, oldViewMatrix, mReflectionMtx);
+          }
+
           viewMatrix.Invert();
-          mViewMatrix.SetDirty( updateBufferIndex );
+          mViewMatrix.SetDirty(updateBufferIndex);
           break;
         }
-          // camera orientation constrained to look at a target
+
+        // camera orientation constrained to look at a target
         case Dali::Camera::LOOK_AT_TARGET:
         {
-          const Matrix& owningNodeMatrix( owningNode.GetWorldMatrix( updateBufferIndex ) );
-          Vector3 position, scale;
-          Quaternion orientation;
-          owningNodeMatrix.GetTransformComponents( position, orientation, scale );
-          Matrix& viewMatrix = mViewMatrix.Get( updateBufferIndex );
-          LookAt( viewMatrix, position, mTargetPosition, orientation.Rotate( Vector3::YAXIS ) );
-          mViewMatrix.SetDirty( updateBufferIndex );
+          const Matrix& owningNodeMatrix(GetWorldMatrix(updateBufferIndex));
+          Vector3       position, scale;
+          Quaternion    orientation;
+          owningNodeMatrix.GetTransformComponents(position, orientation, scale);
+          Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
+
+          if(mUseReflection)
+          {
+            Vector3 up        = orientation.Rotate(Vector3::YAXIS);
+            Vector4 position4 = Vector4(position);
+            Vector4 target4   = Vector4(mTargetPosition);
+            Vector4 up4       = Vector4(up);
+            Vector4 positionNew;
+            Vector4 targetNew;
+            Vector4 upNew;
+            Vector3 positionNew3;
+            Vector3 targetNewVector3;
+            Vector3 upNew3;
+
+            // eye
+            VectorReflectedByPlane(positionNew, position4, mReflectionPlane);
+            VectorReflectedByPlane(targetNew, target4, mReflectionPlane);
+            VectorReflectedByPlane(upNew, up4, mReflectionPlane);
+
+            positionNew3     = Vector3(positionNew);
+            targetNewVector3 = Vector3(targetNew);
+            upNew3           = Vector3(upNew);
+            LookAt(viewMatrix, positionNew3, targetNewVector3, upNew3);
+
+            Matrix oldViewMatrix(viewMatrix);
+            Matrix tmp;
+            tmp.SetIdentityAndScale(Vector3(-1.0, 1.0, 1.0));
+            MatrixUtils::Multiply(viewMatrix, oldViewMatrix, tmp);
+
+            mReflectionEye     = positionNew;
+            mUseReflectionClip = true;
+          }
+          else
+          {
+            LookAt(viewMatrix, position, mTargetPosition, orientation.Rotate(Vector3::YAXIS));
+          }
+          mViewMatrix.SetDirty(updateBufferIndex);
           break;
         }
       }
@@ -370,81 +536,80 @@ unsigned int Camera::UpdateViewMatrix( BufferIndex updateBufferIndex, const Node
   return retval;
 }
 
-void Camera::UpdateFrustum( BufferIndex updateBufferIndex, bool normalize )
+void Camera::UpdateFrustum(BufferIndex updateBufferIndex, bool normalize)
 {
-
   // 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 ];
+  const float*   cm     = clipMatrix.AsFloat();
+  FrustumPlanes& planes = mFrustum[updateBufferIndex];
 
   // Left
-  planes.mPlanes[ 0 ].mNormal.x = cm[ 3 ]  + cm[ 0 ]; // column 4 + column 1
-  planes.mPlanes[ 0 ].mNormal.y = cm[ 7 ]  + cm[ 4 ];
-  planes.mPlanes[ 0 ].mNormal.z = cm[ 11 ] + cm[ 8 ];
-  planes.mPlanes[ 0 ].mDistance = cm[ 15 ] + cm[ 12 ];
+  planes.mPlanes[0].mNormal.x = cm[3] + cm[0]; // column 4 + column 1
+  planes.mPlanes[0].mNormal.y = cm[7] + cm[4];
+  planes.mPlanes[0].mNormal.z = cm[11] + cm[8];
+  planes.mPlanes[0].mDistance = cm[15] + cm[12];
 
   // Right
-  planes.mPlanes[ 1 ].mNormal.x = cm[ 3 ]  - cm[ 0 ]; // column 4 - column 1
-  planes.mPlanes[ 1 ].mNormal.y = cm[ 7 ]  - cm[ 4 ];
-  planes.mPlanes[ 1 ].mNormal.z = cm[ 11 ] - cm[ 8 ];
-  planes.mPlanes[ 1 ].mDistance = cm[ 15 ] - cm[ 12 ];
+  planes.mPlanes[1].mNormal.x = cm[3] - cm[0]; // column 4 - column 1
+  planes.mPlanes[1].mNormal.y = cm[7] - cm[4];
+  planes.mPlanes[1].mNormal.z = cm[11] - cm[8];
+  planes.mPlanes[1].mDistance = cm[15] - cm[12];
 
   // Bottom
-  planes.mPlanes[ 2 ].mNormal.x = cm[ 3 ]  + cm[ 1 ]; // column 4 + column 2
-  planes.mPlanes[ 2 ].mNormal.y = cm[ 7 ]  + cm[ 5 ];
-  planes.mPlanes[ 2 ].mNormal.z = cm[ 11 ] + cm[ 9 ];
-  planes.mPlanes[ 2 ].mDistance = cm[ 15 ] + cm[ 13 ];
+  planes.mPlanes[2].mNormal.x = cm[3] + cm[1]; // column 4 + column 2
+  planes.mPlanes[2].mNormal.y = cm[7] + cm[5];
+  planes.mPlanes[2].mNormal.z = cm[11] + cm[9];
+  planes.mPlanes[2].mDistance = cm[15] + cm[13];
 
   // Top
-  planes.mPlanes[ 3 ].mNormal.x = cm[ 3 ]  - cm[ 1 ]; // column 4 - column 2
-  planes.mPlanes[ 3 ].mNormal.y = cm[ 7 ]  - cm[ 5 ];
-  planes.mPlanes[ 3 ].mNormal.z = cm[ 11 ] - cm[ 9 ];
-  planes.mPlanes[ 3 ].mDistance = cm[ 15 ] - cm[ 13 ];
+  planes.mPlanes[3].mNormal.x = cm[3] - cm[1]; // column 4 - column 2
+  planes.mPlanes[3].mNormal.y = cm[7] - cm[5];
+  planes.mPlanes[3].mNormal.z = cm[11] - cm[9];
+  planes.mPlanes[3].mDistance = cm[15] - cm[13];
 
   // Near
-  planes.mPlanes[ 4 ].mNormal.x = cm[ 3 ]  + cm[ 2 ]; // column 4 + column 3
-  planes.mPlanes[ 4 ].mNormal.y = cm[ 7 ]  + cm[ 6 ];
-  planes.mPlanes[ 4 ].mNormal.z = cm[ 11 ] + cm[ 10 ];
-  planes.mPlanes[ 4 ].mDistance = cm[ 15 ] + cm[ 14 ];
+  planes.mPlanes[4].mNormal.x = cm[3] + cm[2]; // column 4 + column 3
+  planes.mPlanes[4].mNormal.y = cm[7] + cm[6];
+  planes.mPlanes[4].mNormal.z = cm[11] + cm[10];
+  planes.mPlanes[4].mDistance = cm[15] + cm[14];
 
   // Far
-  planes.mPlanes[ 5 ].mNormal.x = cm[ 3 ]  - cm[ 2 ]; // column 4 - column 3
-  planes.mPlanes[ 5 ].mNormal.y = cm[ 7 ]  - cm[ 6 ];
-  planes.mPlanes[ 5 ].mNormal.z = cm[ 11 ] - cm[ 10 ];
-  planes.mPlanes[ 5 ].mDistance = cm[ 15 ] - cm[ 14 ];
+  planes.mPlanes[5].mNormal.x = cm[3] - cm[2]; // column 4 - column 3
+  planes.mPlanes[5].mNormal.y = cm[7] - cm[6];
+  planes.mPlanes[5].mNormal.z = cm[11] - cm[10];
+  planes.mPlanes[5].mDistance = cm[15] - cm[14];
 
-  if ( normalize )
+  if(normalize)
   {
-    for ( unsigned int i = 0; i < 6; ++i )
+    for(uint32_t i = 0; i < 6; ++i)
     {
       // Normalize planes to ensure correct bounding distance checking
-      Plane& plane = planes.mPlanes[ i ];
-      float l = 1.0f / plane.mNormal.Length();
+      Plane& plane = planes.mPlanes[i];
+      float  l     = 1.0f / plane.mNormal.Length();
       plane.mNormal *= l;
       plane.mDistance *= l;
 
-      planes.mSign[i] = Vector3( Sign(plane.mNormal.x), Sign(plane.mNormal.y), Sign(plane.mNormal.z) );
+      planes.mSign[i] = Vector3(Sign(plane.mNormal.x), Sign(plane.mNormal.y), Sign(plane.mNormal.z));
     }
   }
   else
   {
-    for ( unsigned int i = 0; i < 6; ++i )
+    for(uint32_t i = 0; i < 6; ++i)
     {
-      planes.mSign[i] = Vector3( Sign(planes.mPlanes[ i ].mNormal.x), Sign(planes.mPlanes[ i ].mNormal.y), Sign(planes.mPlanes[ i ].mNormal.z) );
+      planes.mSign[i] = Vector3(Sign(planes.mPlanes[i].mNormal.x), Sign(planes.mPlanes[i].mNormal.y), Sign(planes.mPlanes[i].mNormal.z));
     }
   }
-  mFrustum[ updateBufferIndex ? 0 : 1 ] = planes;
+  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 )
+  const FrustumPlanes& planes = mFrustum[bufferIndex];
+  for(uint32_t i = 0; i < 6; ++i)
   {
-    if ( ( planes.mPlanes[ i ].mDistance + planes.mPlanes[ i ].mNormal.Dot( origin ) ) < -radius )
+    if((planes.mPlanes[i].mDistance + planes.mPlanes[i].mNormal.Dot(origin)) < -radius)
     {
       return false;
     }
@@ -452,12 +617,12 @@ bool Camera::CheckSphereInFrustum( BufferIndex bufferIndex, const Vector3& origi
   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 )
+  const FrustumPlanes& planes = mFrustum[bufferIndex];
+  for(uint32_t i = 0; i < 6; ++i)
   {
-    if( planes.mPlanes[ i ].mNormal.Dot( origin + (halfExtents * planes.mSign[i]) ) > -(planes.mPlanes[ i ].mDistance) )
+    if(planes.mPlanes[i].mNormal.Dot(origin + (halfExtents * planes.mSign[i])) > -(planes.mPlanes[i].mDistance))
     {
       continue;
     }
@@ -466,47 +631,119 @@ bool Camera::CheckAABBInFrustum( BufferIndex bufferIndex, const Vector3& origin,
   }
   return true;
 }
+Dali::Rect<int32_t> Camera::GetOrthographicClippingBox(BufferIndex bufferIndex) const
+{
+  const float orthographicSize = mOrthographicSize[bufferIndex];
+  const float aspect           = mAspectRatio[bufferIndex];
 
-unsigned int Camera::UpdateProjection( BufferIndex updateBufferIndex )
+  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)
 {
-  unsigned int retval( mUpdateProjectionFlag );
+  uint32_t retval(mUpdateProjectionFlag);
   // Early-exit if no update required
-  if ( 0u != mUpdateProjectionFlag )
+  if(0u != mUpdateProjectionFlag)
   {
-    if ( COPY_PREVIOUS_MATRIX == mUpdateProjectionFlag )
+    Matrix& finalProjection = mFinalProjection[updateBufferIndex];
+    finalProjection.SetIdentity();
+
+    if(COPY_PREVIOUS_MATRIX == mUpdateProjectionFlag)
     {
       // The projection matrix was updated in the previous frame; copy it
-      mProjectionMatrix.CopyPrevious( updateBufferIndex );
+      mProjectionMatrix.CopyPrevious(updateBufferIndex);
+
+      finalProjection = mFinalProjection[updateBufferIndex ? 0 : 1];
     }
     else // UPDATE_COUNT == mUpdateProjectionFlag
     {
-      switch( mProjectionMode )
+      switch(mProjectionMode)
       {
         case Dali::Camera::PERSPECTIVE_PROJECTION:
         {
-          Matrix &projectionMatrix = mProjectionMatrix.Get( updateBufferIndex );
-          Perspective( projectionMatrix,
-                       mFieldOfView,
-                       mAspectRatio,
+          Matrix& projectionMatrix = mProjectionMatrix.Get(updateBufferIndex);
+          Perspective(projectionMatrix,
+                      mProjectionDirection,
+                      mFieldOfView[updateBufferIndex],
+                      mAspectRatio[updateBufferIndex],
+                      mNearClippingPlane,
+                      mFarClippingPlane,
+                      mInvertYAxis);
+
+          //need to apply custom clipping plane
+          if(mUseReflectionClip)
+          {
+            Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
+            Matrix  viewInv    = viewMatrix;
+            viewInv.Invert();
+            viewInv.Transpose();
+
+            Dali::Vector4 adjReflectPlane = mReflectionPlane;
+            float         d               = mReflectionPlane.Dot(mReflectionEye);
+            if(d < 0)
+            {
+              adjReflectPlane.w = -adjReflectPlane.w;
+            }
+
+            Vector4 customClipping = viewInv * adjReflectPlane;
+            AdjustNearPlaneForPerspective(projectionMatrix, customClipping);
+
+            // Invert Z
+            Matrix matZ;
+            matZ.SetIdentity();
+            float* vZ = matZ.AsFloat();
+            vZ[10]    = -vZ[10];
+            MatrixUtils::Multiply(projectionMatrix, projectionMatrix, matZ);
+          }
+          break;
+        }
+        case Dali::Camera::ORTHOGRAPHIC_PROJECTION:
+        {
+          Matrix& projectionMatrix = mProjectionMatrix.Get(updateBufferIndex);
+          Orthographic(projectionMatrix,
+                       mProjectionDirection,
+                       mOrthographicSize[updateBufferIndex],
+                       mAspectRatio[updateBufferIndex],
                        mNearClippingPlane,
                        mFarClippingPlane,
-                       mInvertYAxis,
-                       mStereoBias );
+                       mInvertYAxis);
           break;
         }
-        case Dali::Camera::ORTHOGRAPHIC_PROJECTION:
+      }
+
+      mProjectionMatrix.SetDirty(updateBufferIndex);
+
+      Quaternion rotationAngle;
+      switch(mProjectionRotation)
+      {
+        case 90:
+        {
+          rotationAngle = Quaternion(Dali::ANGLE_90, Vector3::ZAXIS);
+          break;
+        }
+        case 180:
         {
-          Matrix &projectionMatrix = mProjectionMatrix.Get( updateBufferIndex );
-          Orthographic( projectionMatrix,
-                        mLeftClippingPlane,   mRightClippingPlane,
-                        mBottomClippingPlane, mTopClippingPlane,
-                        mNearClippingPlane,   mFarClippingPlane,
-                        mInvertYAxis );
+          rotationAngle = Quaternion(Dali::ANGLE_180, Vector3::ZAXIS);
           break;
         }
+        case 270:
+        {
+          rotationAngle = Quaternion(Dali::ANGLE_270, Vector3::ZAXIS);
+          break;
+        }
+        default:
+          rotationAngle = Quaternion(Dali::ANGLE_0, Vector3::ZAXIS);
+          break;
       }
 
-      mProjectionMatrix.SetDirty( updateBufferIndex );
+      Matrix rotation;
+      rotation.SetIdentity();
+      rotation.SetTransformComponents(Vector3(1.0f, 1.0f, 1.0f), rotationAngle, Vector3(0.0f, 0.0f, 0.0f));
+
+      MatrixUtils::Multiply(finalProjection, mProjectionMatrix.Get(updateBufferIndex), rotation);
     }
     --mUpdateProjectionFlag;
   }