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