[dali_2.3.21] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / public-api / loader / camera-parameters.cpp
1 /*
2  * Copyright (c) 2023 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-scene3d/public-api/loader/camera-parameters.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/actors/camera-actor-devel.h>
23 #include <dali/integration-api/debug.h>
24 #include <dali/public-api/actors/camera-actor.h>
25 #include <dali/public-api/math/quaternion.h>
26
27 // INTERNAL INCLUDES
28 #include <dali-scene3d/internal/loader/gltf2-asset.h> // for gltf2::UNDEFINED_FLOAT_VALUE
29 #include <dali-scene3d/public-api/loader/utils.h>
30
31 namespace Dali::Scene3D::Loader
32 {
33 namespace
34 {
35 /**
36  * @brief Creates a perspective matrix.
37  *
38  * @param[out] result The perspective matrix.
39  * @param[in] left The coordinate for the left vertical clipping plane.
40  * @param[in] right The coordinate for the right vertical clipping plane.
41  * @param[in] bottom The coordinate for the bottom horizontal clipping plane.
42  * @param[in] top The coordinate for the top horizontal clipping plane.
43  * @param[in] near The distance to the near depth clipping plane.
44  * @param[in] far The distance to the far depth clipping plane.
45  * @param[in] invertYAxis Whether to invert the 'Y' axis.
46  */
47 void Frustum(Matrix& result, float left, float right, float bottom, float top, float nearPlane, float farPlane, bool invertYAxis)
48 {
49   float deltaZ = farPlane - nearPlane;
50   if((nearPlane <= 0.0f) || (farPlane <= 0.0f) || Equals(right, left) || Equals(bottom, top) || (deltaZ <= 0.0f))
51   {
52     DALI_LOG_ERROR("Invalid parameters passed into Frustum!\n");
53     DALI_ASSERT_DEBUG("Invalid parameters passed into Frustum!");
54     return;
55   }
56
57   float deltaX = right - left;
58   float deltaY = invertYAxis ? bottom - top : top - bottom;
59
60   result.SetIdentity();
61
62   float* m = result.AsFloat();
63   m[0]     = -2.0f * nearPlane / deltaX;
64   m[1] = m[2] = m[3] = 0.0f;
65
66   m[5] = -2.0f * nearPlane / deltaY;
67   m[4] = m[6] = m[7] = 0.0f;
68
69   m[8]  = (right + left) / deltaX;
70   m[9]  = (top + bottom) / deltaY;
71   m[10] = (nearPlane + farPlane) / deltaZ;
72   m[11] = 1.0f;
73
74   m[14] = -2.0f * nearPlane * farPlane / deltaZ;
75   m[12] = m[13] = m[15] = 0.0f;
76 }
77
78 /**
79  * @brief Creates a perspective projection matrix.
80  *
81  * It calls Frustum()
82  *
83  * @param[out] result The perspective projection matrix.
84  * @param[in] fovy The vertical field of view.
85  * @param[in] aspect The aspect ratio.
86  * @param[in] nearPlane The distance to the near depth clipping plane.
87  * @param[in] farPlane The distance to the far depth clipping plane.
88  * @param[in] invertYAxis Whether to invert the 'Y' axis.
89  */
90 void Perspective(Matrix& result, float fovy, float aspect, float nearPlane, float farPlane, bool invertYAxis)
91 {
92   float frustumH = tanf(fovy * 0.5f) * nearPlane;
93   float frustumW = frustumH * aspect;
94
95   Frustum(result, -frustumW, frustumW, -frustumH, frustumH, nearPlane, farPlane, invertYAxis);
96 }
97
98 /**
99 * @brief Creates an orthographic projection matrix.
100 *
101 * @param[out] result The orthographic projection matrix.
102 * @param[in] left The coordinate for the left vertical clipping plane.
103 * @param[in] right The coordinate for the right vertical clipping plane.
104 * @param[in] bottom The coordinate for the bottom horizontal clipping plane.
105 * @param[in] top The coordinate for the top horizontal clipping plane.
106 * @param[in] nearPlane The distance to the near depth clipping plane.
107 * @param[in] farPlane The distance to the far depth clipping plane.
108 * @param[in] invertYAxis Whether to invert the 'Y' axis.
109 */
110 void Orthographic(Matrix& result, float left, float right, float bottom, float top, float nearPlane, float farPlane, bool invertYAxis)
111 {
112   if(Equals(right, left) || Equals(top, bottom) || Equals(farPlane, nearPlane))
113   {
114     DALI_LOG_ERROR("Cannot create orthographic projection matrix with a zero dimension.\n");
115     DALI_ASSERT_DEBUG("Cannot create orthographic projection matrix with a zero dimension.");
116     return;
117   }
118
119   float deltaX = right - left;
120   float deltaY = invertYAxis ? bottom - top : top - bottom;
121   float deltaZ = farPlane - nearPlane;
122
123   float* m = result.AsFloat();
124   m[0]     = -2.0f / deltaX;
125   m[1]     = 0.0f;
126   m[2]     = 0.0f;
127   m[3]     = 0.0f;
128
129   m[4] = 0.0f;
130   m[5] = -2.0f / deltaY;
131   m[6] = 0.0f;
132   m[7] = 0.0f;
133
134   m[8]  = 0.0f;
135   m[9]  = 0.0f;
136   m[10] = 2.0f / deltaZ;
137   m[11] = 0.0f;
138   m[12] = -(right + left) / deltaX;
139   m[13] = -(top + bottom) / deltaY;
140   m[14] = -(nearPlane + farPlane) / deltaZ;
141   m[15] = 1.0f;
142 }
143
144 } // namespace
145
146 ViewProjection CameraParameters::GetViewProjection() const
147 {
148   ViewProjection viewProjection;
149
150   // The projection matrix.
151   if(isPerspective)
152   {
153     Perspective(viewProjection.GetProjection(),
154                 Radian(yFovDegree),
155                 1.f,
156                 zNear,
157                 zFar,
158                 true);
159   }
160   else
161   {
162     Orthographic(viewProjection.GetProjection(),
163                  -orthographicSize * aspectRatio,
164                  orthographicSize * aspectRatio,
165                  orthographicSize,
166                  -orthographicSize,
167                  zNear,
168                  zFar,
169                  true);
170   }
171
172   // The view matrix.
173   const Quaternion viewQuaternion(ANGLE_180, Vector3::YAXIS);
174   Vector3          translation;
175   Quaternion       cameraOrientation;
176   Vector3          scale;
177   matrix.GetTransformComponents(translation, cameraOrientation, scale);
178   cameraOrientation *= viewQuaternion;
179
180   viewProjection.GetView().SetInverseTransformComponents(scale,
181                                                          cameraOrientation,
182                                                          translation);
183
184   viewProjection.Update();
185   return viewProjection;
186 }
187
188 void CameraParameters::CalculateTransformComponents(Vector3& position, Quaternion& orientation, Vector3& scale) const
189 {
190   matrix.GetTransformComponents(position, orientation, scale);
191
192   // The CameraActor is expected to look down the negative Z axis, towards the scene.
193   // Here we emulate the default direction of the camera in DALi.
194   Quaternion viewQuaternion(ANGLE_180, Vector3::YAXIS);
195   orientation *= viewQuaternion;
196 }
197
198 bool CameraParameters::ConfigureCamera(CameraActor& camera, bool invertY) const
199 {
200   camera[Actor::Property::NAME] = name;
201
202   if(isPerspective)
203   {
204     if(Dali::Equals(zNear, gltf2::UNDEFINED_FLOAT_VALUE) ||
205        Dali::Equals(yFovDegree.degree, gltf2::UNDEFINED_FLOAT_VALUE))
206     {
207       return false;
208     }
209
210     camera.SetProjectionMode(Camera::PERSPECTIVE_PROJECTION);
211     camera.SetNearClippingPlane(zNear);
212     camera.SetFieldOfView(Radian(yFovDegree));
213
214     if(!Dali::Equals(zFar, gltf2::UNDEFINED_FLOAT_VALUE))
215     {
216       camera.SetFarClippingPlane(zFar);
217     }
218     else
219     {
220       // TODO : Infinite perspective projection didn't support yet. Just set big enough value now
221       camera.SetFarClippingPlane(1000.0f);
222     }
223
224     if(!Dali::Equals(aspectRatio, gltf2::UNDEFINED_FLOAT_VALUE))
225     {
226       // TODO: By gltf 2.0 spec, we should not 'crop' and 'non-uniform scaling' by viewport.
227       // If we skip to setup this value, 'non-uniform scaling' problem fixed.
228       // But we need to resolve 'crop' case in future.
229       //camera.SetAspectRatio(aspectRatio);
230     }
231   }
232   else
233   {
234     if(Dali::Equals(zNear, gltf2::UNDEFINED_FLOAT_VALUE) ||
235        Dali::Equals(zFar, gltf2::UNDEFINED_FLOAT_VALUE) ||
236        Dali::Equals(orthographicSize, gltf2::UNDEFINED_FLOAT_VALUE))
237     {
238       return false;
239     }
240
241     camera.SetProjectionMode(Camera::ORTHOGRAPHIC_PROJECTION);
242     camera.SetNearClippingPlane(zNear);
243     camera.SetFarClippingPlane(zFar);
244     camera.SetProperty(Dali::DevelCameraActor::Property::ORTHOGRAPHIC_SIZE, orthographicSize);
245
246     if(!Dali::Equals(aspectRatio, gltf2::UNDEFINED_FLOAT_VALUE))
247     {
248       // TODO: By gltf 2.0 spec, we should not 'crop' and 'non-uniform scaling' by viewport.
249       // If we skip to setup this value, 'non-uniform scaling' problem fixed.
250       // But we need to resolve 'crop' case in future.
251       //camera.SetAspectRatio(aspectRatio);
252     }
253   }
254
255   SetActorCentered(camera);
256
257   // model
258   Vector3    camTranslation;
259   Vector3    camScale;
260   Quaternion camOrientation;
261   CalculateTransformComponents(camTranslation, camOrientation, camScale);
262
263   camera.SetInvertYAxis(invertY);
264   camera.SetProperty(Actor::Property::POSITION, camTranslation);
265   camera.SetProperty(Actor::Property::ORIENTATION, camOrientation);
266   camera.SetProperty(Actor::Property::SCALE, camScale);
267
268   return true;
269 }
270
271 } // namespace Dali::Scene3D::Loader