Text: Quality improvement
[platform/core/uifw/dali-core.git] / dali / internal / update / node-attachments / scene-graph-camera-attachment.cpp
1 //
2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
3 //
4 // Licensed under the Flora License, Version 1.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
7 //
8 //     http://floralicense.org/license/
9 //
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.
15 //
16
17 // CLASS HEADER
18 #include <dali/internal/update/node-attachments/scene-graph-camera-attachment.h>
19
20 // INTERNAL HEADERS
21 #include <dali/public-api/common/dali-common.h>
22 #include <dali/internal/update/nodes/node.h>
23 #include <dali/internal/update/controllers/scene-controller.h>
24 #include <dali/internal/update/resources/resource-manager.h>
25 #include <dali/integration-api/debug.h>
26
27 using namespace std;
28
29 namespace // unnamed namespace
30 {
31 static unsigned int UPDATE_COUNT        = 2u; // Update projection or view matrix this many frames after a change
32 static unsigned int COPY_PREVIOUS_PROJECTION = 1u; // Copy projection matrix from previous frame
33 }
34
35 namespace Dali
36 {
37
38 namespace Internal
39 {
40
41 namespace SceneGraph
42 {
43
44 namespace
45 {
46
47 void LookAt(Matrix& result, const Vector3& eye, const Vector3& target, const Vector3& up)
48 {
49   Vector3 vZ = target - eye;
50   vZ.Normalize();
51
52   Vector3 vX = up.Cross(vZ);
53   vX.Normalize();
54
55   Vector3 vY = vZ.Cross(vX);
56   vY.Normalize();
57
58   result.SetInverseTransformComponents(vX, vY, vZ, eye);
59 }
60
61
62 void Frustum(Matrix& result, float left, float right, float bottom, float top, float near, float far, bool invertYAxis)
63 {
64   float deltaZ = far - near;
65   if ((near <= 0.0f) || (far <= 0.0f) || Equals(right, left) || Equals(bottom, top) || (deltaZ <= 0.0f))
66   {
67     DALI_LOG_ERROR("Invalid parameters passed into Frustum!");
68     DALI_ASSERT_DEBUG("Invalid parameters passed into Frustum!");
69     return;
70   }
71
72   float deltaX = right - left;
73   float deltaY = invertYAxis ? bottom - top : top - bottom;
74
75   result.SetIdentity();
76
77   float* m = result.AsFloat();
78   m[0] = -2.0f * near / deltaX;
79   m[1] = m[2] = m[3] = 0.0f;
80
81   m[5] = -2.0f * near / deltaY;
82   m[4] = m[6] = m[7] = 0.0f;
83
84   m[8] = (right + left) / deltaX;
85   m[9] = (top + bottom) / deltaY;
86   m[10] = (near + far) / deltaZ;
87   m[11] = 1.0f;
88
89   m[14] = -2.0f * near * far / deltaZ;
90   m[12] = m[13] = m[15] = 0.0f;
91 }
92
93 void Perspective(Matrix& result, float fovy, float aspect, float near, float far, bool invertYAxis, float stereoBias )
94 {
95   float frustumH = tanf( fovy * 0.5f ) * near;
96   float frustumW = frustumH * aspect;
97   float bias = stereoBias * 0.5f;
98
99   Frustum(result, -(frustumW + bias), frustumW - bias, -frustumH, frustumH, near, far, invertYAxis);
100 }
101
102 void Orthographic(Matrix& result, float left, float right, float bottom, float top, float near, float far, bool invertYAxis)
103 {
104   if ( Equals(right, left) || Equals(top, bottom) || Equals(far, near) )
105   {
106     DALI_LOG_ERROR( "Cannot create orthographic projection matrix with a zero dimension." );
107     DALI_ASSERT_DEBUG( "Cannot create orthographic projection matrix with a zero dimension." );
108     return;
109   }
110
111   float deltaX = right - left;
112   float deltaY = invertYAxis ? bottom - top : top - bottom;
113   float deltaZ = far - near;
114
115   float *m = result.AsFloat();
116   m[0] = -2.0f / deltaX;
117   m[1] = 0.0f;
118   m[2] = 0.0f;
119   m[3] = 0.0f;
120
121   m[4] = 0.0f;
122   m[5] = -2.0f / deltaY;
123   m[6] = 0.0f;
124   m[7] = 0.0f;
125
126   m[8] = 0.0f;
127   m[9] = 0.0f;
128   m[10] = 2.0f / deltaZ;
129   m[11] = 0.0f;
130   m[12] = -(right + left) / deltaX;
131   m[13] = -(top + bottom) / deltaY;
132   m[14] = -(near + far)   / deltaZ;
133   m[15] = 1.0f;
134 }
135
136 } // unnamed namespace
137
138 const Dali::Camera::Type CameraAttachment::DEFAULT_TYPE( Dali::Camera::LOOK_AT_TARGET );
139 const Dali::Camera::ProjectionMode CameraAttachment::DEFAULT_MODE( Dali::Camera::PERSPECTIVE_PROJECTION );
140 const bool  CameraAttachment::DEFAULT_INVERT_Y_AXIS( false );
141 const float CameraAttachment::DEFAULT_FIELD_OF_VIEW( 45.0f*(M_PI/180.0f) );
142 const float CameraAttachment::DEFAULT_ASPECT_RATIO( 4.0f/3.0f );
143 const float CameraAttachment::DEFAULT_STEREO_BIAS( 0.0f );
144 const float CameraAttachment::DEFAULT_LEFT_CLIPPING_PLANE(-240.0f);
145 const float CameraAttachment::DEFAULT_RIGHT_CLIPPING_PLANE(240.0f);
146 const float CameraAttachment::DEFAULT_TOP_CLIPPING_PLANE(-400.0f);
147 const float CameraAttachment::DEFAULT_BOTTOM_CLIPPING_PLANE(400.0f);
148 const float CameraAttachment::DEFAULT_NEAR_CLIPPING_PLANE( 800.0f ); // default height of the screen
149 const float CameraAttachment::DEFAULT_FAR_CLIPPING_PLANE( DEFAULT_NEAR_CLIPPING_PLANE + 2.f * DEFAULT_NEAR_CLIPPING_PLANE );
150 const Vector3 CameraAttachment::DEFAULT_TARGET_POSITION( 0.0f, 0.0f, 0.0f );
151
152
153 CameraAttachment::CameraAttachment()
154 : NodeAttachment(),
155   mUpdateViewFlag( UPDATE_COUNT ),
156   mUpdateProjectionFlag( UPDATE_COUNT ),
157   mType( DEFAULT_TYPE ),
158   mProjectionMode( DEFAULT_MODE ),
159   mInvertYAxis( DEFAULT_INVERT_Y_AXIS ),
160   mFieldOfView( DEFAULT_FIELD_OF_VIEW ),
161   mAspectRatio( DEFAULT_ASPECT_RATIO ),
162   mStereoBias( DEFAULT_STEREO_BIAS ),
163   mLeftClippingPlane( DEFAULT_LEFT_CLIPPING_PLANE ),
164   mRightClippingPlane( DEFAULT_RIGHT_CLIPPING_PLANE ),
165   mTopClippingPlane( DEFAULT_TOP_CLIPPING_PLANE ),
166   mBottomClippingPlane( DEFAULT_BOTTOM_CLIPPING_PLANE ),
167   mNearClippingPlane( DEFAULT_NEAR_CLIPPING_PLANE ),
168   mFarClippingPlane( DEFAULT_FAR_CLIPPING_PLANE ),
169   mTargetPosition( DEFAULT_TARGET_POSITION ),
170   mViewMatrix( Matrix::IDENTITY ),
171   mProjectionMatrix( Matrix::IDENTITY ),
172   mInverseViewProjection( Matrix::IDENTITY )
173 {
174 }
175
176 CameraAttachment* CameraAttachment::New()
177 {
178   return new CameraAttachment();
179 }
180
181 void CameraAttachment::ConnectToSceneGraph( SceneController& sceneController, BufferIndex updateBufferIndex )
182 {
183   // do nothing
184 }
185
186 void CameraAttachment::OnDestroy()
187 {
188   // do nothing
189 }
190
191 CameraAttachment::~CameraAttachment()
192 {
193 }
194
195 RenderableAttachment* CameraAttachment::GetRenderable()
196 {
197   return NULL;
198 }
199
200 void CameraAttachment::SetType( Dali::Camera::Type type )
201 {
202   mType = type;
203 }
204
205 void CameraAttachment::SetProjectionMode( Dali::Camera::ProjectionMode mode )
206 {
207   mProjectionMode = mode;
208   mUpdateProjectionFlag = UPDATE_COUNT;
209 }
210
211 void CameraAttachment::SetInvertYAxis( bool invertYAxis )
212 {
213   mInvertYAxis = invertYAxis;
214   mUpdateProjectionFlag = UPDATE_COUNT;
215 }
216
217 void CameraAttachment::SetFieldOfView( float fieldOfView )
218 {
219   mFieldOfView = fieldOfView;
220   mUpdateProjectionFlag = UPDATE_COUNT;
221 }
222
223 void CameraAttachment::SetAspectRatio( float aspectRatio )
224 {
225   mAspectRatio = aspectRatio;
226   mUpdateProjectionFlag = UPDATE_COUNT;
227 }
228
229 void CameraAttachment::SetStereoBias( float stereoBias )
230 {
231   mStereoBias = stereoBias;
232   mUpdateProjectionFlag = UPDATE_COUNT;
233 }
234
235 void CameraAttachment::SetLeftClippingPlane( float leftClippingPlane )
236 {
237   mLeftClippingPlane = leftClippingPlane;
238   mUpdateProjectionFlag = UPDATE_COUNT;
239 }
240
241 void CameraAttachment::SetRightClippingPlane( float rightClippingPlane )
242 {
243   mRightClippingPlane = rightClippingPlane;
244   mUpdateProjectionFlag = UPDATE_COUNT;
245 }
246
247 void CameraAttachment::SetTopClippingPlane( float topClippingPlane )
248 {
249   mTopClippingPlane = topClippingPlane;
250   mUpdateProjectionFlag = UPDATE_COUNT;
251 }
252
253 void CameraAttachment::SetBottomClippingPlane( float bottomClippingPlane )
254 {
255   mBottomClippingPlane = bottomClippingPlane;
256   mUpdateProjectionFlag = UPDATE_COUNT;
257 }
258
259 void CameraAttachment::SetNearClippingPlane( float nearClippingPlane )
260 {
261   mNearClippingPlane = nearClippingPlane;
262   mUpdateProjectionFlag = UPDATE_COUNT;
263 }
264
265 void CameraAttachment::SetFarClippingPlane( float farClippingPlane )
266 {
267   mFarClippingPlane = farClippingPlane;
268   mUpdateProjectionFlag = UPDATE_COUNT;
269 }
270
271 void CameraAttachment::SetTargetPosition( const Vector3& targetPosition )
272 {
273   mTargetPosition = targetPosition;
274   mUpdateViewFlag = UPDATE_COUNT;
275 }
276
277 const Matrix& CameraAttachment::GetProjectionMatrix( BufferIndex bufferIndex ) const
278 {
279   return mProjectionMatrix[ bufferIndex ];
280 }
281
282 const Matrix& CameraAttachment::GetViewMatrix( BufferIndex bufferIndex ) const
283 {
284   return mViewMatrix[ bufferIndex ];
285 }
286
287 const Matrix& CameraAttachment::GetInverseViewProjectionMatrix( BufferIndex bufferIndex ) const
288 {
289   return mInverseViewProjection[ bufferIndex ];
290 }
291
292 const PropertyInputImpl* CameraAttachment::GetProjectionMatrix() const
293 {
294   return &mProjectionMatrix;
295 }
296
297 const PropertyInputImpl* CameraAttachment::GetViewMatrix() const
298 {
299   return &mViewMatrix;
300 }
301
302 void CameraAttachment::Update( BufferIndex updateBufferIndex, const Node& owningNode, int nodeDirtyFlags )
303 {
304   // if owning node has changes in world position we need to update camera for next 2 frames
305   if( nodeDirtyFlags & TransformFlag )
306   {
307     mUpdateViewFlag = UPDATE_COUNT;
308   }
309   if( nodeDirtyFlags & VisibleFlag )
310   {
311     // If the visibility changes, the projection matrix needs to be re-calculated.
312     // It may happen the first time an actor is rendered it's rendered only once and becomes invisible,
313     // in the following update the node will be skipped leaving the projection matrix (double buffered)
314     // with the Identity.
315     mUpdateProjectionFlag = UPDATE_COUNT;
316   }
317   if( 0u != mUpdateViewFlag )
318   {
319     --mUpdateViewFlag;
320     switch ( mType )
321     {
322       // camera orientation taken from node - i.e. look in abitrary, unconstrained direction
323       case Dali::Camera::FREE_LOOK:
324       {
325         Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
326         viewMatrix.SetInverseTransformComponents(
327             Vector3::ONE,
328             owningNode.GetWorldRotation(updateBufferIndex),
329             owningNode.GetWorldPosition(updateBufferIndex) );
330
331         mViewMatrix.SetDirty(updateBufferIndex);
332         break;
333       }
334
335       // camera orientation constrained to look at a target
336       case Dali::Camera::LOOK_AT_TARGET:
337       {
338         Matrix& viewMatrix = mViewMatrix.Get(updateBufferIndex);
339         LookAt( viewMatrix,
340                 owningNode.GetWorldPosition(updateBufferIndex),
341                 mTargetPosition,
342                 owningNode.GetWorldRotation(updateBufferIndex).Rotate(Vector3::YAXIS) );
343
344         mViewMatrix.SetDirty(updateBufferIndex);
345         break;
346       }
347     }
348   }
349
350   UpdateProjection( updateBufferIndex );
351 }
352
353 bool CameraAttachment::ViewMatrixUpdated()
354 {
355   return 0u != mUpdateViewFlag;
356 }
357
358 void CameraAttachment::UpdateProjection( BufferIndex updateBufferIndex )
359 {
360   // Early-exit if no update required
361   if ( 0u != mUpdateProjectionFlag )
362   {
363     if ( COPY_PREVIOUS_PROJECTION == mUpdateProjectionFlag )
364     {
365       // The projection matrix was updated in the previous frame; copy it
366       mProjectionMatrix.CopyPrevious( updateBufferIndex );
367     }
368     else // UPDATE_COUNT == mUpdateProjectionFlag
369     {
370       switch( mProjectionMode )
371       {
372         case Dali::Camera::PERSPECTIVE_PROJECTION:
373         {
374           Matrix &projectionMatrix = mProjectionMatrix.Get(updateBufferIndex);
375           Perspective( projectionMatrix,
376                        mFieldOfView,
377                        mAspectRatio,
378                        mNearClippingPlane,
379                        mFarClippingPlane,
380                        mInvertYAxis,
381                        mStereoBias );
382           break;
383         }
384         case Dali::Camera::ORTHOGRAPHIC_PROJECTION:
385         {
386           Matrix &projectionMatrix = mProjectionMatrix.Get(updateBufferIndex);
387           Orthographic( projectionMatrix,
388                         mLeftClippingPlane,   mRightClippingPlane,
389                         mBottomClippingPlane, mTopClippingPlane,
390                         mNearClippingPlane,   mFarClippingPlane,
391                         mInvertYAxis );
392           break;
393         }
394       }
395
396       mProjectionMatrix.SetDirty(updateBufferIndex);
397     }
398     --mUpdateProjectionFlag;
399   }
400
401   // if model or view matrix changed we need to recalculate the inverse VP
402   if( !mViewMatrix.IsClean() || !mProjectionMatrix.IsClean() )
403   {
404     UpdateInverseViewProjection( updateBufferIndex );
405   }
406 }
407
408 void CameraAttachment::UpdateInverseViewProjection( BufferIndex updateBufferIndex )
409 {
410   Matrix::Multiply( mInverseViewProjection[ updateBufferIndex ], mViewMatrix[ updateBufferIndex ], mProjectionMatrix[ updateBufferIndex ] );
411   // ignore the error, if the view projection is incorrect (non inversible) then you will have tough times anyways
412   static_cast< void >( mInverseViewProjection[ updateBufferIndex ].Invert() );
413 }
414
415 } // namespace SceneGraph
416
417 } // namespace Internal
418
419 } // namespace Dali