#include <cmath>
#include <dali/public-api/dali-core.h>
#include <dali/devel-api/actors/actor-devel.h>
-
+#include <dali/devel-api/actors/camera-actor-devel.h>
#include "dali-test-suite-utils/dali-test-suite-utils.h"
}
END_TEST;
}
+
+int UtcDaliCameraActorReflectionByPlane(void)
+{
+ TestApplication application;
+
+ Stage stage = Stage::GetCurrent();
+
+ Vector3 targetPosition( Vector3::ZERO );
+ Vector3 cameraOffset( 0.0f, 100.0f, 100.0f );
+
+ CameraActor freeLookCameraActor = stage.GetRenderTaskList().GetTask(0).GetCameraActor();
+ freeLookCameraActor.SetType(Camera::LOOK_AT_TARGET);
+ freeLookCameraActor.SetTargetPosition( targetPosition );
+ freeLookCameraActor.SetPosition(cameraOffset);
+
+ stage.GetRootLayer().SetPosition( 1, 0 );
+
+ application.SendNotification();
+ application.Render();
+ application.SendNotification();
+ application.Render();
+
+ Matrix matrixBefore, matrixAfter;
+ freeLookCameraActor.GetProperty( CameraActor::CameraActor::Property::VIEW_MATRIX ).Get( matrixBefore );
+ freeLookCameraActor.SetProperty( Dali::DevelCameraActor::Property::REFLECTION_PLANE, Vector4( 0.0f, 1.0f, 0.0f, 0.0f));
+ stage.GetRootLayer().SetPosition( 0, 0 );
+ application.SendNotification();
+ application.Render();
+ application.SendNotification();
+ application.Render();
+
+ freeLookCameraActor.GetProperty( CameraActor::CameraActor::Property::VIEW_MATRIX ).Get( matrixAfter );
+
+ Vector3 position, scale;
+ Quaternion rotation;
+ matrixAfter.GetTransformComponents( position, rotation, scale );
+
+ Quaternion reflected( 0, 0, 1, 0 );
+
+ DALI_TEST_EQUALS( reflected, rotation, 0.01f, TEST_LOCATION );
+
+ // Test Free Look camera
+ freeLookCameraActor.SetType(Camera::FREE_LOOK);
+
+ // Make sure the recalculation will take place
+ freeLookCameraActor.SetProperty( Dali::DevelCameraActor::Property::REFLECTION_PLANE, Vector4( 0.0f, 1.0f, 0.0f, 0.0f));
+
+ application.SendNotification();
+ application.Render();
+ application.SendNotification();
+ application.Render();
+
+ // Nothing should change despite of different camera type
+ matrixAfter.GetTransformComponents( position, rotation, scale );
+ DALI_TEST_EQUALS( reflected, rotation, 0.01f, TEST_LOCATION );
+
+ END_TEST;
+}
\ No newline at end of file
--- /dev/null
+#ifndef DALI_CAMERA_ACTOR_DEVEL_H
+#define DALI_CAMERA_ACTOR_DEVEL_H
+
+/*
+ * Copyright (c) 2020 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <dali/public-api/actors/actor.h>
+
+namespace Dali
+{
+namespace DevelCameraActor
+{
+namespace Property
+{
+enum
+{
+ /**
+ * @brief Defines reflection plane for the camera
+ * @details Type Property::VECTOR4
+ * @note Optional
+ */
+ REFLECTION_PLANE = CameraActor::Property::INVERT_Y_AXIS + 1
+};
+
+} // Namespace Property
+} // Namesapce DevelCameraActor
+} // Namespace Dali
+
+#endif // DALI_CAMERA_ACTOR_DEVEL_H
${devel_api_src_dir}/actors/actor-devel.h
${devel_api_src_dir}/actors/custom-actor-devel.h
${devel_api_src_dir}/actors/layer-devel.h
+ ${devel_api_src_dir}/actors/camera-actor-devel.h
)
// INTERNAL INCLUDES
#include <dali/public-api/common/stage.h>
#include <dali/public-api/object/type-registry.h>
+#include <dali/devel-api/actors/camera-actor-devel.h>
#include <dali/integration-api/debug.h>
#include <dali/internal/event/common/property-helper.h>
#include <dali/internal/event/common/stage-impl.h>
}
}
+void CameraActor::SetReflectByPlane(const Vector4& plane) {
+ SceneGraph::Camera* cam = const_cast<SceneGraph::Camera*>(GetCamera());
+ if (cam)
+ {
+ cam->SetReflectByPlane(plane);
+ }
+}
+
void CameraActor::SetTarget( const Vector3& target )
{
if( target != mTarget ) // using range epsilon
SetInvertYAxis( propertyValue.Get<bool>() ); // set to false in case property is not bool
break;
}
+ case Dali::DevelCameraActor::Property::REFLECTION_PLANE:
+ {
+ SetReflectByPlane( propertyValue.Get<Vector4>() );
+ break;
+ }
+
default:
{
DALI_LOG_WARNING( "Unknown property (%d)\n", index );
static CameraActorPtr New( const Size& size );
/**
+ * Sets the reflection plane for the camera
+ *
+ * @param[in] plane Plane parameters
+ *
+ * @note plane.xyz are normal vector of the plane.
+ */
+ void SetReflectByPlane( const Vector4& plane );
+
+ /**
* @copydoc Dali::CameraActor::SetTargetPosition
*/
void SetTarget( const Vector3& targetPosition );
const Matrix& projectionMatrix,
Integration::DepthBufferAvailable depthBufferAvailable,
Integration::StencilBufferAvailable stencilBufferAvailable,
- Vector<GLuint>& boundTextures )
+ Vector<GLuint>& boundTextures,
+ const RenderInstruction& instruction
+ )
{
DALI_PRINT_RENDER_LIST( renderList );
// Render the item.
item.mRenderer->Render( context, bufferIndex, *item.mNode, item.mModelMatrix, item.mModelViewMatrix,
- viewMatrix, projectionMatrix, item.mSize, !item.mIsOpaque, boundTextures );
+ viewMatrix, projectionMatrix, item.mSize, !item.mIsOpaque, boundTextures, instruction ); // Added instruction for reflection effect
}
}
}
*projectionMatrix,
depthBufferAvailable,
stencilBufferAvailable,
- boundTextures );
+ boundTextures,
+ instruction //added for reflection effect
+ );
}
}
}
const Matrix& projectionMatrix,
Integration::DepthBufferAvailable depthBufferAvailable,
Integration::StencilBufferAvailable stencilBufferAvailable,
- Vector<GLuint>& boundTextures );
+ Vector<GLuint>& boundTextures,
+ const Dali::Internal::SceneGraph::RenderInstruction& instruction // in the case of reflection, things like CullFace need to be adjusted for reflection world
+ );
// Prevent copying:
RenderAlgorithms( RenderAlgorithms& rhs );
// inlined as this is called once per frame per render instruction
return &mCamera->GetProjectionMatrix( index );
}
+ // for reflection effect
+ const Camera* GetCamera() const
+ {
+ return mCamera;
+ }
private:
#include <dali/internal/render/shaders/scene-graph-shader.h>
#include <dali/internal/render/shaders/program.h>
#include <dali/internal/render/data-providers/node-data-provider.h>
+#include <dali/internal/render/common/render-instruction.h>
namespace Dali
{
const Matrix& projectionMatrix,
const Vector3& size,
bool blend,
- Vector<GLuint>& boundTextures )
+ Vector<GLuint>& boundTextures,
+ const Dali::Internal::SceneGraph::RenderInstruction& instruction )
{
// Get the program to use:
Program* program = mRenderDataProvider->GetShader().GetProgram();
}
//Set cull face mode
- context.CullFace( mFaceCullingMode );
+ const Dali::Internal::SceneGraph::Camera* cam = instruction.GetCamera();
+ if (cam->GetReflectionUsed())
+ {
+ auto adjFaceCullingMode = mFaceCullingMode;
+ switch( mFaceCullingMode )
+ {
+ case FaceCullingMode::Type::FRONT:
+ {
+ adjFaceCullingMode = FaceCullingMode::Type::BACK;
+ break;
+ }
+ case FaceCullingMode::Type::BACK:
+ {
+ adjFaceCullingMode = FaceCullingMode::Type::FRONT;
+ break;
+ }
+ default:
+ {
+ // nothing to do, leave culling as it is
+ }
+ }
+ context.CullFace( adjFaceCullingMode );
+ }
+ else
+ {
+ context.CullFace( mFaceCullingMode );
+ }
//Set blending mode
SetBlending( context, blend );
class SceneController;
class Shader;
class NodeDataProvider;
+
+class RenderInstruction; //for relfection effect
}
namespace Render
* @param[in] size Size of the render item
* @param[in] blend If true, blending is enabled
* @param[in] boundTextures The textures bound for rendering
+ * @param[in] instruction. for use case like reflection where CullFace needs to be adjusted
+
*/
void Render( Context& context,
BufferIndex bufferIndex,
const Matrix& projectionMatrix,
const Vector3& size,
bool blend,
- Vector<GLuint>& boundTextures );
+ Vector<GLuint>& boundTextures,
+ const Dali::Internal::SceneGraph::RenderInstruction& instruction //for reflection effect
+ );
/**
* Write the renderer's sort attributes to the passed in reference
{
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 Dali
mUpdateViewFlag = UPDATE_COUNT;
}
+
+
+void VectorReflectedByPlane(Vector4 &out, Vector4 &in, Vector4 &plane)
+{
+ 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::AdjustNearPlaneForPerspective( Matrix& perspective, const Vector4& clipPlane )
+{
+ 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;
+}
+
const Matrix& Camera::GetProjectionMatrix( BufferIndex bufferIndex ) const
{
return mProjectionMatrix[ bufferIndex ];
{
Matrix& viewMatrix = mViewMatrix.Get( updateBufferIndex );
viewMatrix = mNode->GetWorldMatrix( updateBufferIndex );
+
+ if (mUseReflection)
+ {
+ const Matrix& owningNodeMatrix( mNode->GetWorldMatrix( updateBufferIndex ) );
+ Vector3 position{}, scale{};
+ Quaternion orientation{};
+ owningNodeMatrix.GetTransformComponents( position, orientation, scale );
+ mReflectionEye = position;
+ mUseReflectionClip = true;
+
+ Matrix& viewMatrix = mViewMatrix.Get( updateBufferIndex );
+ Matrix oldViewMatrix( viewMatrix );
+ Matrix::Multiply(viewMatrix, oldViewMatrix, mReflectionMtx);
+ }
+
viewMatrix.Invert();
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( mNode->GetWorldMatrix( updateBufferIndex ) );
Quaternion orientation;
owningNodeMatrix.GetTransformComponents( position, orientation, scale );
Matrix& viewMatrix = mViewMatrix.Get( updateBufferIndex );
- LookAt( viewMatrix, position, mTargetPosition, orientation.Rotate( Vector3::YAXIS ) );
+
+ 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));
+ Matrix::Multiply(viewMatrix, oldViewMatrix, tmp);
+
+ mReflectionEye = positionNew;
+ mUseReflectionClip = true;
+ }
+ else
+ {
+ LookAt( viewMatrix, position, mTargetPosition, orientation.Rotate( Vector3::YAXIS ) );
+ }
mViewMatrix.SetDirty( updateBufferIndex );
break;
}
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];
+ Matrix::Multiply(projectionMatrix, projectionMatrix , matZ);
+ }
break;
}
case Dali::Camera::ORTHOGRAPHIC_PROJECTION:
void SetTargetPosition( const Vector3& targetPosition );
/**
+ * Sets the reflection plane
+ * @param[in] plane reflection plane
+ */
+ void SetReflectByPlane( const Vector4& plane );
+
+ /**
+ * Tests whether reflection is used
+ * @return True if used, False otherwise
+ */
+ bool GetReflectionUsed() const
+ {
+ return mUseReflection;
+ }
+
+ /**
* Retrieve the view-matrix; this is double buffered for input handling.
* @param[in] bufferIndex The buffer to read from.
* @return The view-matrix.
*/
void UpdateFrustum( BufferIndex updateBufferIndex, bool normalize = true );
+ /**
+ * Adjust near plane for reflection
+ * @param perspective Perspective matrix
+ * @param clipPlane Clipping plane
+ */
+ void AdjustNearPlaneForPerspective( Matrix& perspective, const Vector4& clipPlane );
+
uint32_t mUpdateViewFlag; ///< This is non-zero if the view matrix requires an update
uint32_t mUpdateProjectionFlag; ///< This is non-zero if the projection matrix requires an update
const Node* mNode; ///< The node this scene graph camera belongs to
float mFarClippingPlane;
Vector3 mTargetPosition;
+ Dali::Matrix mReflectionMtx;
+ Dali::Vector4 mReflectionPlane;
+ Dali::Vector4 mReflectionEye;
+ bool mUseReflection{ false };
+ bool mUseReflectionClip{ false };
+
InheritedMatrix mViewMatrix; ///< The viewMatrix; this is double buffered for input handling.
InheritedMatrix mProjectionMatrix; ///< The projectionMatrix; this is double buffered for input handling.