2 * Copyright (c) 2019 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/event/actors/camera-actor-impl.h>
23 #include <cstring> // for strcmp
26 #include <dali/public-api/common/stage.h>
27 #include <dali/public-api/object/type-registry.h>
28 #include <dali/integration-api/debug.h>
29 #include <dali/internal/event/common/property-helper.h>
30 #include <dali/internal/event/common/stage-impl.h>
31 #include <dali/internal/event/common/scene-impl.h>
32 #include <dali/internal/event/render-tasks/render-task-impl.h>
33 #include <dali/internal/event/render-tasks/render-task-list-impl.h>
34 #include <dali/internal/event/common/projection.h>
35 #include <dali/internal/event/common/thread-local-storage.h>
36 #include <dali/internal/update/render-tasks/scene-graph-camera.h>
50 * We want to discourage the use of property strings (minimize string comparisons),
51 * particularly for the default properties.
52 * Name Type writable animatable constraint-input enum for index-checking
54 DALI_PROPERTY_TABLE_BEGIN
55 DALI_PROPERTY( "type", STRING, true, false, true, Dali::CameraActor::Property::TYPE )
56 DALI_PROPERTY( "projectionMode", STRING, true, false, true, Dali::CameraActor::Property::PROJECTION_MODE )
57 DALI_PROPERTY( "fieldOfView", FLOAT, true, false, true, Dali::CameraActor::Property::FIELD_OF_VIEW )
58 DALI_PROPERTY( "aspectRatio", FLOAT, true, false, true, Dali::CameraActor::Property::ASPECT_RATIO )
59 DALI_PROPERTY( "nearPlaneDistance", FLOAT, true, false, true, Dali::CameraActor::Property::NEAR_PLANE_DISTANCE )
60 DALI_PROPERTY( "farPlaneDistance", FLOAT, true, false, true, Dali::CameraActor::Property::FAR_PLANE_DISTANCE )
61 DALI_PROPERTY( "leftPlaneDistance", FLOAT, true, false, true, Dali::CameraActor::Property::LEFT_PLANE_DISTANCE )
62 DALI_PROPERTY( "rightPlaneDistance", FLOAT, true, false, true, Dali::CameraActor::Property::RIGHT_PLANE_DISTANCE )
63 DALI_PROPERTY( "topPlaneDistance", FLOAT, true, false, true, Dali::CameraActor::Property::TOP_PLANE_DISTANCE )
64 DALI_PROPERTY( "bottomPlaneDistance", FLOAT, true, false, true, Dali::CameraActor::Property::BOTTOM_PLANE_DISTANCE )
65 DALI_PROPERTY( "targetPosition", VECTOR3, true, false, true, Dali::CameraActor::Property::TARGET_POSITION )
66 DALI_PROPERTY( "projectionMatrix", MATRIX, false, false, true, Dali::CameraActor::Property::PROJECTION_MATRIX )
67 DALI_PROPERTY( "viewMatrix", MATRIX, false, false, true, Dali::CameraActor::Property::VIEW_MATRIX )
68 DALI_PROPERTY( "invertYAxis", BOOLEAN, true, false, true, Dali::CameraActor::Property::INVERT_Y_AXIS )
69 DALI_PROPERTY_TABLE_END( DEFAULT_DERIVED_ACTOR_PROPERTY_START_INDEX, CameraDefaultProperties )
71 // calculate the far plane distance for a 16bit depth buffer with 4 bits per unit precision
72 void CalculateClippingAndZ( float width, float height, float& nearClippingPlane, float& farClippingPlane, float& cameraZ )
74 nearClippingPlane = std::max( width, height );
75 farClippingPlane = nearClippingPlane + static_cast<float>( 0xFFFF >> 4 );
76 cameraZ = 2.0f * nearClippingPlane;
81 return Dali::CameraActor::New();
84 TypeRegistration mType( typeid( Dali::CameraActor ), typeid( Dali::Actor ), Create, CameraDefaultProperties );
87 * Builds the picking ray in the world reference system from an orthographic camera
88 * The ray origin is the screen coordinate in the near plane translated to a parallel
89 * plane at the camera origin. The ray direction is the direction the camera is facing
90 * (i.e. Z=-1 in view space).
92 void BuildOrthoPickingRay( const Matrix& viewMatrix,
93 const Matrix& projectionMatrix,
94 const Viewport& viewport,
99 float nearPlaneDistance )
101 // inv( modelMatrix ) inv( viewMatrix ) inv( projectionMatrix ) normalize
102 // <----------------- <----------------- <-------------- <-------------
103 // Local World Camera Normalized Screen
104 // reference reference reference clip coordinates
105 // system system system coordinates
106 // -----------------> -----------------> --------------> ------------->
107 // modelMatrix viewMatrix projectionMatrix viewport
109 // Transforms the touch point from the screen reference system to the world reference system.
110 Matrix invViewProjection( false ); // Don't initialize.
111 Matrix::Multiply( invViewProjection, viewMatrix, projectionMatrix );
112 if( !invViewProjection.Invert() )
114 DALI_ASSERT_DEBUG( false );
117 Vector4 near( screenX - static_cast<float>( viewport.x ),
118 static_cast<float>( viewport.height ) - (screenY - static_cast<float>( viewport.y ) ),
120 if( !Unproject( near, invViewProjection, static_cast<float>( viewport.width ), static_cast<float>( viewport.height ), rayOrigin ) )
122 DALI_ASSERT_DEBUG( false );
125 Matrix invView = viewMatrix;
126 if( !invView.Invert() )
128 DALI_ASSERT_DEBUG( false );
131 Vector4 cameraOrigin = invView * Vector4( 0.f, 0.f, 0.f, 1.f );
132 Vector4 nearPlaneOrigin = invView * Vector4( 0.0f, 0.0f, -nearPlaneDistance, 1.0f);
134 // Vector pointing from the camera to the near plane
135 rayDir = cameraOrigin - nearPlaneOrigin;
143 CameraActorPtr CameraActor::New( const Size& size )
145 CameraActorPtr actor( new CameraActor( *CreateNode() ) );
147 // Second-phase construction
150 actor->SetName( "DefaultCamera" );
151 actor->SetPerspectiveProjection( size );
153 // By default Actors face in the positive Z direction in world space
154 // CameraActors should face in the negative Z direction, towards the other actors
155 actor->SetOrientation( Quaternion( Dali::ANGLE_180, Vector3::YAXIS ) );
160 CameraActor::CameraActor( const SceneGraph::Node& node )
161 : Actor( Actor::BASIC, node ),
162 mSceneObject( NULL ),
163 mTarget( SceneGraph::Camera::DEFAULT_TARGET_POSITION ),
164 mType( SceneGraph::Camera::DEFAULT_TYPE ),
165 mProjectionMode( SceneGraph::Camera::DEFAULT_MODE ),
166 mFieldOfView( SceneGraph::Camera::DEFAULT_FIELD_OF_VIEW ),
167 mAspectRatio( SceneGraph::Camera::DEFAULT_ASPECT_RATIO ),
168 mNearClippingPlane( SceneGraph::Camera::DEFAULT_NEAR_CLIPPING_PLANE ),
169 mFarClippingPlane( SceneGraph::Camera::DEFAULT_FAR_CLIPPING_PLANE ),
170 mLeftClippingPlane( SceneGraph::Camera::DEFAULT_LEFT_CLIPPING_PLANE ),
171 mRightClippingPlane( SceneGraph::Camera::DEFAULT_RIGHT_CLIPPING_PLANE ),
172 mTopClippingPlane( SceneGraph::Camera::DEFAULT_TOP_CLIPPING_PLANE ),
173 mBottomClippingPlane( SceneGraph::Camera::DEFAULT_BOTTOM_CLIPPING_PLANE ),
174 mInvertYAxis( SceneGraph::Camera::DEFAULT_INVERT_Y_AXIS )
178 CameraActor::~CameraActor()
180 if( EventThreadServices::IsCoreRunning() )
182 // Create scene-object and transfer ownership through message
183 RemoveCameraMessage( GetEventThreadServices().GetUpdateManager(), mSceneObject );
187 void CameraActor::OnInitialize()
189 // Create scene-object and keep raw pointer for message passing.
190 SceneGraph::Camera* sceneGraphCamera = SceneGraph::Camera::New();
192 // Store a pointer to this camera node inside the scene-graph camera.
193 sceneGraphCamera->SetNode( &GetNode() );
195 mSceneObject = sceneGraphCamera;
196 OwnerPointer< SceneGraph::Camera > sceneGraphCameraOwner( sceneGraphCamera );
198 // Send message to inform update of this camera (and move ownership).
199 AddCameraMessage( GetEventThreadServices().GetUpdateManager(), sceneGraphCameraOwner );
202 void CameraActor::SetTarget( const Vector3& target )
204 if( target != mTarget ) // using range epsilon
208 SetTargetPositionMessage( GetEventThreadServices(), *mSceneObject, mTarget );
212 Vector3 CameraActor::GetTarget() const
217 void CameraActor::SetType( Dali::Camera::Type type )
223 // sceneObject is being used in a separate thread; queue a message to set
224 SetTypeMessage( GetEventThreadServices(), *mSceneObject, mType );
228 Dali::Camera::Type CameraActor::GetType() const
233 void CameraActor::SetProjectionMode( Dali::Camera::ProjectionMode mode )
235 if( mode != mProjectionMode )
237 mProjectionMode = mode;
239 // sceneObject is being used in a separate thread; queue a message to set
240 SetProjectionModeMessage( GetEventThreadServices(), *mSceneObject, mProjectionMode );
244 Dali::Camera::ProjectionMode CameraActor::GetProjectionMode() const
246 return mProjectionMode;
249 void CameraActor::SetFieldOfView( float fieldOfView )
251 if( ! Equals( fieldOfView, mFieldOfView ) )
253 mFieldOfView = fieldOfView;
255 // sceneObject is being used in a separate thread; queue a message to set
256 SetFieldOfViewMessage( GetEventThreadServices(), *mSceneObject, mFieldOfView );
260 float CameraActor::GetFieldOfView() const
265 void CameraActor::SetAspectRatio( float aspectRatio )
267 if( ! Equals( aspectRatio, mAspectRatio ) )
269 mAspectRatio = aspectRatio;
271 // sceneObject is being used in a separate thread; queue a message to set
272 SetAspectRatioMessage( GetEventThreadServices(), *mSceneObject, mAspectRatio );
276 float CameraActor::GetAspectRatio() const
281 void CameraActor::SetNearClippingPlane( float nearClippingPlane )
283 if( ! Equals( nearClippingPlane, mNearClippingPlane ) )
285 mNearClippingPlane = nearClippingPlane;
287 // sceneObject is being used in a separate thread; queue a message to set
288 SetNearClippingPlaneMessage( GetEventThreadServices(), *mSceneObject, mNearClippingPlane );
292 float CameraActor::GetNearClippingPlane() const
294 return mNearClippingPlane;
297 void CameraActor::SetFarClippingPlane( float farClippingPlane )
299 if( ! Equals( farClippingPlane, mFarClippingPlane ) )
301 mFarClippingPlane = farClippingPlane;
303 // sceneObject is being used in a separate thread; queue a message to set
304 SetFarClippingPlaneMessage( GetEventThreadServices(), *mSceneObject, mFarClippingPlane );
308 float CameraActor::GetFarClippingPlane() const
310 return mFarClippingPlane;
313 void CameraActor::SetLeftClippingPlane( float leftClippingPlane )
315 if( ! Equals( leftClippingPlane, mLeftClippingPlane ) )
317 mLeftClippingPlane = leftClippingPlane;
319 // sceneObject is being used in a separate thread; queue a message to set
320 SetLeftClippingPlaneMessage( GetEventThreadServices(), *mSceneObject, mLeftClippingPlane );
324 void CameraActor::SetRightClippingPlane( float rightClippingPlane )
326 if( ! Equals( rightClippingPlane, mRightClippingPlane ) )
328 mRightClippingPlane = rightClippingPlane;
330 // sceneObject is being used in a separate thread; queue a message to set
331 SetRightClippingPlaneMessage( GetEventThreadServices(), *mSceneObject, mRightClippingPlane );
335 void CameraActor::SetTopClippingPlane( float topClippingPlane )
337 if( ! Equals( topClippingPlane, mTopClippingPlane ) )
339 mTopClippingPlane = topClippingPlane;
341 // sceneObject is being used in a separate thread; queue a message to set
342 SetTopClippingPlaneMessage( GetEventThreadServices(), *mSceneObject, mTopClippingPlane );
346 void CameraActor::SetBottomClippingPlane( float bottomClippingPlane )
348 if( ! Equals( bottomClippingPlane, mBottomClippingPlane ) )
350 mBottomClippingPlane = bottomClippingPlane;
352 // sceneObject is being used in a separate thread; queue a message to set
353 SetBottomClippingPlaneMessage( GetEventThreadServices(), *mSceneObject, mBottomClippingPlane );
357 void CameraActor::SetInvertYAxis(bool invertYAxis)
359 if( invertYAxis != mInvertYAxis )
361 mInvertYAxis = invertYAxis;
363 // sceneObject is being used in a separate thread; queue a message to set
364 SetInvertYAxisMessage( GetEventThreadServices(), *mSceneObject, mInvertYAxis );
368 bool CameraActor::GetInvertYAxis() const
373 void CameraActor::SetPerspectiveProjection( const Size& size )
375 if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
377 // Not allowed to set the canvas size to be 0.
378 DALI_LOG_ERROR( "Canvas size can not be 0\n" );
382 float width = size.width;
383 float height = size.height;
385 float nearClippingPlane;
386 float farClippingPlane;
388 CalculateClippingAndZ( width, height, nearClippingPlane, farClippingPlane, cameraZ );
390 // calculate the position of the camera to have the desired aspect ratio
391 const float fieldOfView = 2.0f * std::atan( height * 0.5f / cameraZ );
393 // unless it is too small, we want at least as much space to the back as we have torwards the front
394 const float minClippingFarPlane = 2.f * nearClippingPlane;
395 if ( farClippingPlane < minClippingFarPlane )
397 farClippingPlane = minClippingFarPlane;
400 const float aspectRatio = width / height;
402 // sceneObject is being used in a separate thread; queue a message to set
403 SetProjectionMode(Dali::Camera::PERSPECTIVE_PROJECTION);
404 SetFieldOfView( fieldOfView );
405 SetNearClippingPlane( nearClippingPlane );
406 SetFarClippingPlane( farClippingPlane );
407 SetAspectRatio( aspectRatio );
412 void CameraActor::SetOrthographicProjection( const Vector2& size )
414 // Choose near, far and Z parameters to match the SetPerspectiveProjection above.
415 float nearClippingPlane;
416 float farClippingPlane;
418 CalculateClippingAndZ( size.width, size.height, nearClippingPlane, farClippingPlane, cameraZ );
419 SetOrthographicProjection( -size.x*0.5f, size.x*0.5f, size.y*0.5f, -size.y*0.5f,
420 nearClippingPlane, farClippingPlane );
424 void CameraActor::SetOrthographicProjection( float left, float right, float top, float bottom, float near, float far )
426 SetLeftClippingPlane( left );
427 SetRightClippingPlane( right );
428 SetTopClippingPlane( top );
429 SetBottomClippingPlane( bottom );
430 SetNearClippingPlane( near );
431 SetFarClippingPlane( far );
432 SetProjectionMode( Dali::Camera::ORTHOGRAPHIC_PROJECTION );
435 bool CameraActor::BuildPickingRay( const Vector2& screenCoordinates,
436 const Viewport& viewport,
438 Vector4& rayDirection )
441 if( mProjectionMode == Dali::Camera::PERSPECTIVE_PROJECTION )
443 // Build a picking ray in the world reference system.
444 // ray starts from the camera world position
445 rayOrigin = GetNode().GetWorldMatrix(0).GetTranslation();
448 // Transform the touch point from the screen coordinate system to the world coordinates system.
449 Vector4 near( screenCoordinates.x - static_cast<float>(viewport.x),
450 static_cast<float>( viewport.height ) - (screenCoordinates.y - static_cast<float>( viewport.y ) ),
452 const Matrix& inverseViewProjection = mSceneObject->GetInverseViewProjectionMatrix( GetEventThreadServices().GetEventBufferIndex() );
453 success = Unproject( near, inverseViewProjection, static_cast<float>( viewport.width ), static_cast<float>( viewport.height ), near );
455 // Compute the ray's director vector.
456 rayDirection.x = near.x - rayOrigin.x;
457 rayDirection.y = near.y - rayOrigin.y;
458 rayDirection.z = near.z - rayOrigin.z;
459 rayDirection.Normalize();
460 rayDirection.w = 1.f;
464 float nearPlaneDistance = GetNearClippingPlane();
465 BuildOrthoPickingRay( GetViewMatrix(),
466 GetProjectionMatrix(),
467 viewport, screenCoordinates.x,
477 const Matrix& CameraActor::GetViewMatrix() const
481 return mSceneObject->GetViewMatrix( GetEventThreadServices().GetEventBufferIndex() );
485 return Matrix::IDENTITY;
489 const Matrix& CameraActor::GetProjectionMatrix() const
493 return mSceneObject->GetProjectionMatrix( GetEventThreadServices().GetEventBufferIndex() );
497 return Matrix::IDENTITY;
500 const SceneGraph::Camera* CameraActor::GetCamera() const
505 void CameraActor::SetDefaultProperty( Property::Index index, const Property::Value& propertyValue )
507 if(index < DEFAULT_ACTOR_PROPERTY_MAX_COUNT)
509 Actor::SetDefaultProperty(index, propertyValue);
515 case Dali::CameraActor::Property::TYPE:
517 std::string s( propertyValue.Get<std::string>() );
518 if(s == "LOOK_AT_TARGET")
520 SetType( Dali::Camera::LOOK_AT_TARGET );
522 else if(s == "FREE_LOOK")
524 SetType( Dali::Camera::FREE_LOOK );
528 case Dali::CameraActor::Property::PROJECTION_MODE:
530 std::string s( propertyValue.Get<std::string>() );
531 if( s == "PERSPECTIVE_PROJECTION" )
533 SetProjectionMode( Dali::Camera::PERSPECTIVE_PROJECTION );
535 else if( s == "ORTHOGRAPHIC_PROJECTION" )
537 SetProjectionMode( Dali::Camera::ORTHOGRAPHIC_PROJECTION );
541 case Dali::CameraActor::Property::FIELD_OF_VIEW:
543 SetFieldOfView( propertyValue.Get<float>() ); // set to 0 in case property is not float
546 case Dali::CameraActor::Property::ASPECT_RATIO:
548 SetAspectRatio( propertyValue.Get<float>() ); // set to 0 in case property is not float
551 case Dali::CameraActor::Property::NEAR_PLANE_DISTANCE:
553 SetNearClippingPlane( propertyValue.Get<float>() ); // set to 0 in case property is not float
556 case Dali::CameraActor::Property::FAR_PLANE_DISTANCE:
558 SetFarClippingPlane( propertyValue.Get<float>() ); // set to 0 in case property is not float
561 case Dali::CameraActor::Property::LEFT_PLANE_DISTANCE:
563 SetLeftClippingPlane( propertyValue.Get<float>() ); // set to 0 in case property is not float
566 case Dali::CameraActor::Property::RIGHT_PLANE_DISTANCE:
568 SetRightClippingPlane( propertyValue.Get<float>() ); // set to 0 in case property is not float
571 case Dali::CameraActor::Property::TOP_PLANE_DISTANCE:
573 SetTopClippingPlane( propertyValue.Get<float>() ); // set to 0 in case property is not float
576 case Dali::CameraActor::Property::BOTTOM_PLANE_DISTANCE:
578 SetBottomClippingPlane( propertyValue.Get<float>() ); // set to 0 in case property is not float
581 case Dali::CameraActor::Property::TARGET_POSITION:
583 SetTarget( propertyValue.Get<Vector3>() ); // set to 0 in case property is not Vector3
586 case Dali::CameraActor::Property::PROJECTION_MATRIX:
588 DALI_LOG_WARNING( "projection-matrix is read-only\n" );
591 case Dali::CameraActor::Property::VIEW_MATRIX:
593 DALI_LOG_WARNING( "view-matrix is read-only\n" );
596 case Dali::CameraActor::Property::INVERT_Y_AXIS:
598 SetInvertYAxis( propertyValue.Get<bool>() ); // set to false in case property is not bool
603 DALI_LOG_WARNING( "Unknown property (%d)\n", index );
611 Property::Value CameraActor::GetDefaultProperty( Property::Index index ) const
614 if( index < DEFAULT_ACTOR_PROPERTY_MAX_COUNT )
616 ret = Actor::GetDefaultProperty(index);
622 case Dali::CameraActor::Property::TYPE:
624 if( Dali::Camera::LOOK_AT_TARGET == mType )
626 ret = "LOOK_AT_TARGET";
628 else if( Dali::Camera::FREE_LOOK == mType )
634 case Dali::CameraActor::Property::PROJECTION_MODE:
636 if( Dali::Camera::PERSPECTIVE_PROJECTION == mProjectionMode )
638 ret = "PERSPECTIVE_PROJECTION";
640 else if( Dali::Camera::ORTHOGRAPHIC_PROJECTION == mProjectionMode )
642 ret = "ORTHOGRAPHIC_PROJECTION";
646 case Dali::CameraActor::Property::FIELD_OF_VIEW:
651 case Dali::CameraActor::Property::ASPECT_RATIO:
656 case Dali::CameraActor::Property::NEAR_PLANE_DISTANCE:
658 ret = mNearClippingPlane;
661 case Dali::CameraActor::Property::FAR_PLANE_DISTANCE:
663 ret = mFarClippingPlane;
666 case Dali::CameraActor::Property::LEFT_PLANE_DISTANCE:
668 ret = mLeftClippingPlane;
671 case Dali::CameraActor::Property::RIGHT_PLANE_DISTANCE:
673 ret = mRightClippingPlane;
676 case Dali::CameraActor::Property::TOP_PLANE_DISTANCE:
678 ret = mTopClippingPlane;
681 case Dali::CameraActor::Property::BOTTOM_PLANE_DISTANCE:
683 ret = mBottomClippingPlane;
686 case Dali::CameraActor::Property::TARGET_POSITION:
691 case Dali::CameraActor::Property::PROJECTION_MATRIX:
693 ret = GetProjectionMatrix(); // Only on scene-graph
696 case Dali::CameraActor::Property::VIEW_MATRIX:
698 ret = GetViewMatrix(); // Only on scene-graph
701 case Dali::CameraActor::Property::INVERT_Y_AXIS:
712 Property::Value CameraActor::GetDefaultPropertyCurrentValue( Property::Index index ) const
715 if( index < DEFAULT_ACTOR_PROPERTY_MAX_COUNT )
717 ret = Actor::GetDefaultPropertyCurrentValue(index);
721 ret = GetDefaultProperty( index ); // Most are event-side properties, the scene-graph properties are only on the scene-graph
727 const PropertyInputImpl* CameraActor::GetSceneObjectInputProperty( Property::Index index ) const
729 const PropertyInputImpl* property( NULL );
733 case Dali::CameraActor::Property::PROJECTION_MATRIX:
735 property = mSceneObject->GetProjectionMatrix();
738 case Dali::CameraActor::Property::VIEW_MATRIX:
740 property = mSceneObject->GetViewMatrix();
743 // no default on purpose as we chain method up to actor
747 property = Actor::GetSceneObjectInputProperty( index );
753 } // namespace Internal