2 * Copyright (c) 2014 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/internal/event/actor-attachments/camera-attachment-impl.h>
29 #include <dali/internal/event/common/property-helper.h>
30 #include <dali/internal/event/common/stage-impl.h>
31 #include <dali/internal/event/render-tasks/render-task-impl.h>
32 #include <dali/internal/event/render-tasks/render-task-list-impl.h>
33 #include <dali/internal/event/common/projection.h>
34 #include <dali/integration-api/debug.h>
48 * We want to discourage the use of property strings (minimize string comparisons),
49 * particularly for the default properties.
50 * Name Type writable animatable constraint-input enum for index-checking
52 DALI_PROPERTY_TABLE_BEGIN
53 DALI_PROPERTY( "type", STRING, true, false, true, Dali::CameraActor::Property::TYPE )
54 DALI_PROPERTY( "projection-mode", STRING, true, false, true, Dali::CameraActor::Property::PROJECTION_MODE )
55 DALI_PROPERTY( "field-of-view", FLOAT, true, false, true, Dali::CameraActor::Property::FIELD_OF_VIEW )
56 DALI_PROPERTY( "aspect-ratio", FLOAT, true, false, true, Dali::CameraActor::Property::ASPECT_RATIO )
57 DALI_PROPERTY( "near-plane-distance", FLOAT, true, false, true, Dali::CameraActor::Property::NEAR_PLANE_DISTANCE )
58 DALI_PROPERTY( "far-plane-distance", FLOAT, true, false, true, Dali::CameraActor::Property::FAR_PLANE_DISTANCE )
59 DALI_PROPERTY( "left-plane-distance", FLOAT, true, false, true, Dali::CameraActor::Property::LEFT_PLANE_DISTANCE )
60 DALI_PROPERTY( "right-plane-distance", FLOAT, true, false, true, Dali::CameraActor::Property::RIGHT_PLANE_DISTANCE )
61 DALI_PROPERTY( "top-plane-distance", FLOAT, true, false, true, Dali::CameraActor::Property::TOP_PLANE_DISTANCE )
62 DALI_PROPERTY( "bottom-plane-distance", FLOAT, true, false, true, Dali::CameraActor::Property::BOTTOM_PLANE_DISTANCE )
63 DALI_PROPERTY( "target-position", VECTOR3, true, false, true, Dali::CameraActor::Property::TARGET_POSITION )
64 DALI_PROPERTY( "projection-matrix", MATRIX, false, false, true, Dali::CameraActor::Property::PROJECTION_MATRIX )
65 DALI_PROPERTY( "view-matrix", MATRIX, false, false, true, Dali::CameraActor::Property::VIEW_MATRIX )
66 DALI_PROPERTY( "invert-y-axis", BOOLEAN, true, false, true, Dali::CameraActor::Property::INVERT_Y_AXIS )
67 DALI_PROPERTY_TABLE_END( DEFAULT_DERIVED_ACTOR_PROPERTY_START_INDEX )
69 // calculate the far plane distance for a 16bit depth buffer with 4 bits per unit precision
70 void CalculateClippingAndZ( float width, float height, float& nearClippingPlane, float& farClippingPlane, float& cameraZ )
72 nearClippingPlane = std::max( width, height );
73 farClippingPlane = nearClippingPlane + static_cast<float>( 0xFFFF >> 4 );
74 cameraZ = 2.0f * nearClippingPlane;
79 return Dali::CameraActor::New();
82 TypeRegistration mType( typeid( Dali::CameraActor ), typeid( Dali::Actor ), Create );
85 * Builds the picking ray in the world reference system from an orthographic camera
86 * The ray origin is the screen coordinate in the near plane translated to a parallel
87 * plane at the camera origin. The ray direction is the direction the camera is facing
88 * (i.e. Z=-1 in view space).
90 void BuildOrthoPickingRay( const Matrix& viewMatrix,
91 const Matrix& projectionMatrix,
92 const Viewport& viewport,
97 float nearPlaneDistance )
99 // inv( modelMatrix ) inv( viewMatrix ) inv( projectionMatrix ) normalize
100 // <----------------- <----------------- <-------------- <-------------
101 // Local World Camera Normalized Screen
102 // reference reference reference clip coordinates
103 // system system system coordinates
104 // -----------------> -----------------> --------------> ------------->
105 // modelMatrix viewMatrix projectionMatrix viewport
107 // Transforms the touch point from the screen reference system to the world reference system.
108 Matrix invViewProjection( false ); // Don't initialize.
109 Matrix::Multiply( invViewProjection, viewMatrix, projectionMatrix );
110 if( !invViewProjection.Invert() )
112 DALI_ASSERT_DEBUG( false );
115 Vector4 near( screenX - viewport.x, viewport.height - (screenY - viewport.y), 0.f, 1.f );
116 if( !Unproject( near, invViewProjection, viewport.width, viewport.height, rayOrigin ) )
118 DALI_ASSERT_DEBUG( false );
121 Matrix invView = viewMatrix;
122 if( !invView.Invert() )
124 DALI_ASSERT_DEBUG( false );
127 Vector4 cameraOrigin = invView * Vector4( 0.f, 0.f, 0.f, 1.f );
128 Vector4 nearPlaneOrigin = invView * Vector4( 0.0f, 0.0f, -nearPlaneDistance, 1.0f);
130 // Vector pointing from the camera to the near plane
131 rayDir = cameraOrigin - nearPlaneOrigin;
139 CameraActorPtr CameraActor::New( const Size& size )
141 CameraActorPtr actor(new CameraActor());
143 // Second-phase construction
147 actor->SetName("DefaultCamera");
149 // Create the attachment
150 actor->mCameraAttachment = CameraAttachment::New( actor->GetEventThreadServices(), *actor->mNode );
152 actor->Attach(*actor->mCameraAttachment);
154 actor->SetPerspectiveProjection( size );
156 // By default Actors face in the positive Z direction in world space
157 // CameraActors should face in the negative Z direction, towards the other actors
158 actor->SetOrientation( Quaternion( Dali::ANGLE_180, Vector3::YAXIS ) );
163 void CameraActor::OnInitialize()
167 CameraActor::CameraActor()
168 : Actor( Actor::BASIC )
172 CameraActor::~CameraActor()
176 void CameraActor::SetType( Dali::Camera::Type type )
178 mCameraAttachment->SetType(type);
181 Dali::Camera::Type CameraActor::GetType() const
183 return mCameraAttachment->GetType();
186 void CameraActor::SetProjectionMode( Dali::Camera::ProjectionMode mode )
188 mCameraAttachment->SetProjectionMode(mode);
191 Dali::Camera::ProjectionMode CameraActor::GetProjectionMode() const
193 return mCameraAttachment->GetProjectionMode();
196 void CameraActor::SetFieldOfView( float fieldOfView )
198 mCameraAttachment->SetFieldOfView(fieldOfView);
201 float CameraActor::GetFieldOfView( ) const
203 return mCameraAttachment->GetFieldOfView();
206 void CameraActor::SetAspectRatio( float aspectRatio )
208 mCameraAttachment->SetAspectRatio(aspectRatio);
211 float CameraActor::GetAspectRatio( ) const
213 return mCameraAttachment->GetAspectRatio();
216 void CameraActor::SetNearClippingPlane( float nearClippingPlane )
218 mCameraAttachment->SetNearClippingPlane(nearClippingPlane);
221 float CameraActor::GetNearClippingPlane( ) const
223 return mCameraAttachment->GetNearClippingPlane();
226 void CameraActor::SetFarClippingPlane( float farClippingPlane )
228 mCameraAttachment->SetFarClippingPlane(farClippingPlane);
231 float CameraActor::GetFarClippingPlane( ) const
233 return mCameraAttachment->GetFarClippingPlane();
236 void CameraActor::SetTargetPosition(const Vector3& target)
238 mCameraAttachment->SetTargetPosition(target);
241 Vector3 CameraActor::GetTargetPosition() const
243 return mCameraAttachment->GetTargetPosition();
246 void CameraActor::SetInvertYAxis(bool invertYAxis)
248 mCameraAttachment->SetInvertYAxis(invertYAxis);
251 bool CameraActor::GetInvertYAxis() const
253 return mCameraAttachment->GetInvertYAxis();
256 void CameraActor::SetPerspectiveProjection( const Size& size, const Vector2& stereoBias /* = Vector2::ZERO */ )
258 float width = size.width;
259 float height = size.height;
261 if( Size::ZERO == size )
263 StagePtr stage = Stage::GetCurrent();
266 const Size& stageSize = stage->GetSize();
268 width = stageSize.width;
269 height = stageSize.height;
273 if( ( width < Math::MACHINE_EPSILON_1000 ) || ( height < Math::MACHINE_EPSILON_1000 ) )
275 // On the stage initialization this method is called but the size has not been set.
276 // There is no point to set any value if width or height is zero.
280 float nearClippingPlane;
281 float farClippingPlane;
283 CalculateClippingAndZ( width, height, nearClippingPlane, farClippingPlane, cameraZ );
285 // calculate the position of the camera to have the desired aspect ratio
286 const float fieldOfView = 2.0f * std::atan( height * 0.5f / cameraZ );
288 // unless it is too small, we want at least as much space to the back as we have torwards the front
289 const float minClippingFarPlane = 2.f * nearClippingPlane;
290 if ( farClippingPlane < minClippingFarPlane )
292 farClippingPlane = minClippingFarPlane;
295 const float aspectRatio = width / height;
297 SetProjectionMode(Dali::Camera::PERSPECTIVE_PROJECTION);
298 SetFieldOfView( fieldOfView );
299 SetNearClippingPlane( nearClippingPlane );
300 SetFarClippingPlane( farClippingPlane );
301 SetAspectRatio( aspectRatio );
302 mCameraAttachment->SetStereoBias( stereoBias );
307 void CameraActor::SetOrthographicProjection( const Vector2& size )
309 // Choose near, far and Z parameters to match the SetPerspectiveProjection above.
310 float nearClippingPlane;
311 float farClippingPlane;
313 CalculateClippingAndZ( size.width, size.height, nearClippingPlane, farClippingPlane, cameraZ );
314 SetOrthographicProjection( -size.x*0.5f, size.x*0.5f, size.y*0.5f, -size.y*0.5f,
315 nearClippingPlane, farClippingPlane );
319 void CameraActor::SetOrthographicProjection( float left, float right, float top, float bottom, float near, float far )
321 mCameraAttachment->SetLeftClippingPlane(left);
322 mCameraAttachment->SetRightClippingPlane(right);
323 mCameraAttachment->SetTopClippingPlane(top);
324 mCameraAttachment->SetBottomClippingPlane(bottom);
325 SetNearClippingPlane( near );
326 SetFarClippingPlane( far );
327 SetProjectionMode(Dali::Camera::ORTHOGRAPHIC_PROJECTION);
330 bool CameraActor::BuildPickingRay( const Vector2& screenCoordinates,
331 const Viewport& viewport,
333 Vector4& rayDirection )
336 if( GetProjectionMode() == Dali::Camera::PERSPECTIVE_PROJECTION )
338 // Build a picking ray in the world reference system.
339 // ray starts from the camera world position
340 rayOrigin = mNode->GetWorldPosition( GetEventThreadServices().GetEventBufferIndex() );
343 // Transform the touch point from the screen coordinate system to the world coordinates system.
344 Vector4 near( screenCoordinates.x - viewport.x, viewport.height - (screenCoordinates.y - viewport.y), 0.f, 1.f );
345 if( !Unproject( near, mCameraAttachment->GetInverseViewProjectionMatrix(), viewport.width, viewport.height, near ) )
347 // unproject failed so no picking ray possible
351 // Compute the ray's director vector.
352 rayDirection.x = near.x - rayOrigin.x;
353 rayDirection.y = near.y - rayOrigin.y;
354 rayDirection.z = near.z - rayOrigin.z;
355 rayDirection.Normalize();
356 rayDirection.w = 1.f;
360 float nearPlaneDistance = GetNearClippingPlane();
361 BuildOrthoPickingRay( GetViewMatrix(),
362 GetProjectionMatrix(),
363 viewport, screenCoordinates.x,
373 const Matrix& CameraActor::GetViewMatrix() const
377 return mCameraAttachment->GetViewMatrix();
381 return Matrix::IDENTITY;
385 const Matrix& CameraActor::GetProjectionMatrix() const
389 return mCameraAttachment->GetProjectionMatrix();
393 return Matrix::IDENTITY;
397 unsigned int CameraActor::GetDefaultPropertyCount() const
399 return Actor::GetDefaultPropertyCount() + DEFAULT_PROPERTY_COUNT;
402 void CameraActor::GetDefaultPropertyIndices( Property::IndexContainer& indices ) const
404 Actor::GetDefaultPropertyIndices( indices ); // Actor class properties
406 indices.Reserve( indices.Size() + DEFAULT_PROPERTY_COUNT );
408 int index = DEFAULT_DERIVED_ACTOR_PROPERTY_START_INDEX;
409 for ( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i, ++index )
411 indices.PushBack( index );
415 bool CameraActor::IsDefaultPropertyWritable( Property::Index index ) const
417 if( index < DEFAULT_ACTOR_PROPERTY_MAX_COUNT )
419 return Actor::IsDefaultPropertyWritable( index );
422 return DEFAULT_PROPERTY_DETAILS[index - DEFAULT_DERIVED_ACTOR_PROPERTY_START_INDEX].writable;
425 bool CameraActor::IsDefaultPropertyAnimatable( Property::Index index ) const
427 if( index < DEFAULT_ACTOR_PROPERTY_MAX_COUNT )
429 return Actor::IsDefaultPropertyAnimatable( index );
432 return DEFAULT_PROPERTY_DETAILS[index - DEFAULT_DERIVED_ACTOR_PROPERTY_START_INDEX].animatable;
435 bool CameraActor::IsDefaultPropertyAConstraintInput( Property::Index index ) const
437 if( index < DEFAULT_ACTOR_PROPERTY_MAX_COUNT )
439 return Actor::IsDefaultPropertyAConstraintInput( index );
442 return DEFAULT_PROPERTY_DETAILS[index - DEFAULT_DERIVED_ACTOR_PROPERTY_START_INDEX].constraintInput;
445 Property::Type CameraActor::GetDefaultPropertyType( Property::Index index ) const
447 if( index < DEFAULT_ACTOR_PROPERTY_MAX_COUNT )
449 return Actor::GetDefaultPropertyType( index );
453 index -= DEFAULT_DERIVED_ACTOR_PROPERTY_START_INDEX;
455 if ( ( index >= 0 ) && ( index < DEFAULT_PROPERTY_COUNT ) )
457 return DEFAULT_PROPERTY_DETAILS[index].type;
461 // index out-of-bounds
462 return Property::NONE;
467 const char* CameraActor::GetDefaultPropertyName( Property::Index index ) const
469 if(index < DEFAULT_ACTOR_PROPERTY_MAX_COUNT)
471 return Actor::GetDefaultPropertyName(index);
475 index -= DEFAULT_DERIVED_ACTOR_PROPERTY_START_INDEX;
477 if ( ( index >= 0 ) && ( index < DEFAULT_PROPERTY_COUNT ) )
479 return DEFAULT_PROPERTY_DETAILS[index].name;
485 Property::Index CameraActor::GetDefaultPropertyIndex(const std::string& name) const
487 Property::Index index = Property::INVALID_INDEX;
489 // Look for name in current class' default properties
490 for( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
492 if( 0 == strcmp( name.c_str(), DEFAULT_PROPERTY_DETAILS[i].name ) ) // dont want to convert rhs to string
494 index = i + DEFAULT_DERIVED_ACTOR_PROPERTY_START_INDEX;
499 // If not found, check in base class
500 if( Property::INVALID_INDEX == index )
502 index = Actor::GetDefaultPropertyIndex( name );
508 void CameraActor::SetDefaultProperty( Property::Index index, const Property::Value& propertyValue )
510 if(index < DEFAULT_ACTOR_PROPERTY_MAX_COUNT)
512 Actor::SetDefaultProperty(index, propertyValue);
516 DALI_ASSERT_DEBUG(mCameraAttachment && "where is the camera?");
519 case Dali::CameraActor::Property::TYPE:
521 std::string s( propertyValue.Get<std::string>() );
522 if(s == "LOOK_AT_TARGET")
524 mCameraAttachment->SetType(Dali::Camera::LOOK_AT_TARGET);
526 else if(s == "FREE_LOOK")
528 mCameraAttachment->SetType(Dali::Camera::FREE_LOOK);
532 DALI_LOG_WARNING("Unknown camera type\n");
536 case Dali::CameraActor::Property::PROJECTION_MODE:
538 std::string s(propertyValue.Get<std::string>());
539 if(s == "PERSPECTIVE_PROJECTION")
541 mCameraAttachment->SetProjectionMode(Dali::Camera::PERSPECTIVE_PROJECTION);
543 else if(s == "ORTHOGRAPHIC_PROJECTION")
545 mCameraAttachment->SetProjectionMode(Dali::Camera::ORTHOGRAPHIC_PROJECTION);
549 DALI_LOG_WARNING("Unknown projection mode\n");
553 case Dali::CameraActor::Property::FIELD_OF_VIEW:
555 mCameraAttachment->SetFieldOfView(propertyValue.Get<float>());
558 case Dali::CameraActor::Property::ASPECT_RATIO:
560 mCameraAttachment->SetAspectRatio(propertyValue.Get<float>());
563 case Dali::CameraActor::Property::LEFT_PLANE_DISTANCE:
565 mCameraAttachment->SetLeftClippingPlane(propertyValue.Get<float>());
568 case Dali::CameraActor::Property::RIGHT_PLANE_DISTANCE:
570 mCameraAttachment->SetRightClippingPlane(propertyValue.Get<float>());
573 case Dali::CameraActor::Property::TOP_PLANE_DISTANCE:
575 mCameraAttachment->SetTopClippingPlane(propertyValue.Get<float>());
578 case Dali::CameraActor::Property::BOTTOM_PLANE_DISTANCE:
580 mCameraAttachment->SetBottomClippingPlane(propertyValue.Get<float>());
583 case Dali::CameraActor::Property::NEAR_PLANE_DISTANCE:
585 mCameraAttachment->SetNearClippingPlane(propertyValue.Get<float>());
588 case Dali::CameraActor::Property::FAR_PLANE_DISTANCE:
590 mCameraAttachment->SetFarClippingPlane(propertyValue.Get<float>());
593 case Dali::CameraActor::Property::TARGET_POSITION:
595 mCameraAttachment->SetTargetPosition(propertyValue.Get<Vector3>());
598 case Dali::CameraActor::Property::PROJECTION_MATRIX:
600 DALI_LOG_WARNING("projection-matrix property is not animatable \n");
603 case Dali::CameraActor::Property::VIEW_MATRIX:
605 DALI_LOG_WARNING("view-matrix property is not animatable \n");
608 case Dali::CameraActor::Property::INVERT_Y_AXIS:
610 mCameraAttachment->SetInvertYAxis(propertyValue.Get<bool>());
615 DALI_LOG_WARNING("Unknown property (%d)\n", index);
623 Property::Value CameraActor::GetDefaultProperty( Property::Index index ) const
626 if(index < DEFAULT_ACTOR_PROPERTY_MAX_COUNT)
628 ret = Actor::GetDefaultProperty(index);
632 DALI_ASSERT_DEBUG(mCameraAttachment && "where is the camera?");
635 case Dali::CameraActor::Property::TYPE:
637 if(mCameraAttachment->GetType() == Dali::Camera::LOOK_AT_TARGET)
639 ret = "LOOK_AT_TARGET";
641 else if(mCameraAttachment->GetType() == Dali::Camera::FREE_LOOK)
648 DALI_ASSERT_DEBUG("Unknown camera type\n");
652 case Dali::CameraActor::Property::PROJECTION_MODE:
654 if(mCameraAttachment->GetProjectionMode() == Dali::Camera::PERSPECTIVE_PROJECTION)
656 ret = "PERSPECTIVE_PROJECTION";
658 else if(mCameraAttachment->GetProjectionMode() == Dali::Camera::ORTHOGRAPHIC_PROJECTION)
660 ret = "ORTHOGRAPHIC_PROJECTION";
665 DALI_ASSERT_DEBUG("Unknown projection mode\n");
669 case Dali::CameraActor::Property::FIELD_OF_VIEW:
671 ret = mCameraAttachment->GetFieldOfView();
674 case Dali::CameraActor::Property::ASPECT_RATIO:
676 ret = mCameraAttachment->GetAspectRatio();
679 case Dali::CameraActor::Property::LEFT_PLANE_DISTANCE:
681 ret = mCameraAttachment->GetLeftClippingPlane();
684 case Dali::CameraActor::Property::RIGHT_PLANE_DISTANCE:
686 ret = mCameraAttachment->GetRightClippingPlane();
689 case Dali::CameraActor::Property::TOP_PLANE_DISTANCE:
691 ret = mCameraAttachment->GetTopClippingPlane();
694 case Dali::CameraActor::Property::BOTTOM_PLANE_DISTANCE:
696 ret = mCameraAttachment->GetBottomClippingPlane();
699 case Dali::CameraActor::Property::NEAR_PLANE_DISTANCE:
701 ret = mCameraAttachment->GetNearClippingPlane();
704 case Dali::CameraActor::Property::FAR_PLANE_DISTANCE:
706 ret = mCameraAttachment->GetFarClippingPlane();
709 case Dali::CameraActor::Property::TARGET_POSITION:
711 ret = mCameraAttachment->GetTargetPosition();
714 case Dali::CameraActor::Property::PROJECTION_MATRIX:
716 ret = mCameraAttachment->GetProjectionMatrix();
719 case Dali::CameraActor::Property::VIEW_MATRIX:
721 ret = mCameraAttachment->GetViewMatrix();
724 case Dali::CameraActor::Property::INVERT_Y_AXIS:
726 ret = mCameraAttachment->GetInvertYAxis();
731 DALI_LOG_WARNING("Unknown property (%d)\n", index);
740 const SceneGraph::PropertyBase* CameraActor::GetSceneObjectAnimatableProperty( Property::Index index ) const
742 DALI_ASSERT_ALWAYS( IsPropertyAnimatable(index) && "Property is not animatable" );
744 const SceneGraph::PropertyBase* property( NULL );
746 // This method should only return a property of an object connected to the scene-graph
752 // let actor handle animatable properties, we have no animatable properties
753 if( index < DEFAULT_ACTOR_PROPERTY_MAX_COUNT )
755 property = Actor::GetSceneObjectAnimatableProperty(index);
761 const PropertyInputImpl* CameraActor::GetSceneObjectInputProperty( Property::Index index ) const
763 const PropertyInputImpl* property( NULL );
765 // This method should only return a property of an object connected to the scene-graph
771 // if its an actor default property or a custom property (actor already handles custom properties)
772 if( ( index < DEFAULT_ACTOR_PROPERTY_MAX_COUNT ) || ( index >= DEFAULT_PROPERTY_MAX_COUNT ) )
774 property = Actor::GetSceneObjectInputProperty(index);
780 case Dali::CameraActor::Property::PROJECTION_MATRIX:
782 property = mCameraAttachment->GetProjectionMatrixProperty();
785 case Dali::CameraActor::Property::VIEW_MATRIX:
787 property = mCameraAttachment->GetViewMatrixProperty();
791 DALI_LOG_WARNING("Not an input property (%d)\n", index);
800 } // namespace Internal