Remove 3D layer from ModelView
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / internal / controls / model-view / model-view-impl.cpp
1 /*
2  * Copyright (c) 2022 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 "model-view-impl.h"
20
21 // EXTERNAL INCLUDES
22 #include <dali-toolkit/dali-toolkit.h>
23 #include <dali-toolkit/devel-api/controls/control-devel.h>
24 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
25 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
26 #include <dali/integration-api/debug.h>
27 #include <dali/public-api/object/type-registry-helper.h>
28 #include <dali/public-api/object/type-registry.h>
29 #include <filesystem>
30
31 // INTERNAL INCLUDES
32 #include <dali-scene3d/public-api/controls/model-view/model-view.h>
33 #include <dali-scene3d/internal/controls/scene-view/scene-view-impl.h>
34 #include <dali-scene3d/public-api/loader/animation-definition.h>
35 #include <dali-scene3d/public-api/loader/camera-parameters.h>
36 #include <dali-scene3d/public-api/loader/cube-map-loader.h>
37 #include <dali-scene3d/public-api/loader/dli-loader.h>
38 #include <dali-scene3d/public-api/loader/gltf2-loader.h>
39 #include <dali-scene3d/public-api/loader/light-parameters.h>
40 #include <dali-scene3d/public-api/loader/load-result.h>
41 #include <dali-scene3d/public-api/loader/node-definition.h>
42 #include <dali-scene3d/public-api/loader/scene-definition.h>
43 #include <dali-scene3d/public-api/loader/shader-definition-factory.h>
44
45 using namespace Dali;
46
47 namespace Dali
48 {
49 namespace Scene3D
50 {
51 namespace Internal
52 {
53 namespace
54 {
55 BaseHandle Create()
56 {
57   return Scene3D::ModelView::New(std::string());
58 }
59
60 // Setup properties, signals and actions using the type-registry.
61 DALI_TYPE_REGISTRATION_BEGIN(Scene3D::ModelView, Toolkit::Control, Create);
62 DALI_TYPE_REGISTRATION_END()
63
64 static constexpr uint32_t OFFSET_FOR_DIFFUSE_CUBE_TEXTURE  = 2u;
65 static constexpr uint32_t OFFSET_FOR_SPECULAR_CUBE_TEXTURE = 1u;
66
67 static constexpr Vector3 Y_DIRECTION(1.0f, -1.0f, 1.0f);
68
69 static constexpr std::string_view KTX_EXTENSION  = ".ktx";
70 static constexpr std::string_view OBJ_EXTENSION  = ".obj";
71 static constexpr std::string_view GLTF_EXTENSION = ".gltf";
72 static constexpr std::string_view DLI_EXTENSION  = ".dli";
73
74 struct BoundingVolume
75 {
76   void Init()
77   {
78     pointMin = Vector3(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
79     pointMax = Vector3(std::numeric_limits<float>::min(), std::numeric_limits<float>::min(), std::numeric_limits<float>::min());
80   }
81
82   void ConsiderNewPointInVolume(const Vector3& position)
83   {
84     pointMin.x = std::min(position.x, pointMin.x);
85     pointMin.y = std::min(position.y, pointMin.y);
86     pointMin.z = std::min(position.z, pointMin.z);
87
88     pointMax.x = std::max(position.x, pointMax.x);
89     pointMax.y = std::max(position.y, pointMax.y);
90     pointMax.z = std::max(position.z, pointMax.z);
91   }
92
93   Vector3 CalculateSize()
94   {
95     return pointMax - pointMin;
96   }
97
98   Vector3 CalculatePivot()
99   {
100     Vector3 pivot = pointMin / (pointMin - pointMax);
101     for(uint32_t i = 0; i < 3; ++i)
102     {
103       // To avoid divid by zero
104       if(pointMin[i] == pointMax[i])
105       {
106         pivot[i] = 0.5f;
107       }
108     }
109     return pivot;
110   }
111
112   Vector3 pointMin;
113   Vector3 pointMax;
114 };
115
116 void ConfigureBlendShapeShaders(
117   Dali::Scene3D::Loader::ResourceBundle& resources, const Dali::Scene3D::Loader::SceneDefinition& scene, Actor root, std::vector<Dali::Scene3D::Loader::BlendshapeShaderConfigurationRequest>&& requests)
118 {
119   std::vector<std::string> errors;
120   auto                     onError = [&errors](const std::string& msg) { errors.push_back(msg); };
121   if(!scene.ConfigureBlendshapeShaders(resources, root, std::move(requests), onError))
122   {
123     Dali::Scene3D::Loader::ExceptionFlinger flinger(ASSERT_LOCATION);
124     for(auto& msg : errors)
125     {
126       flinger << msg << '\n';
127     }
128   }
129 }
130
131 void AddModelTreeToAABB(BoundingVolume& AABB, const Dali::Scene3D::Loader::SceneDefinition& scene, const Dali::Scene3D::Loader::Customization::Choices& choices, Dali::Scene3D::Loader::Index iNode, Dali::Scene3D::Loader::NodeDefinition::CreateParams& nodeParams, Matrix parentMatrix)
132 {
133   static constexpr uint32_t BOX_POINT_COUNT             = 8;
134   static uint32_t           BBIndex[BOX_POINT_COUNT][3] = {{0, 0, 0}, {0, 1, 0}, {1, 0, 0}, {1, 1, 0}, {0, 0, 1}, {0, 1, 1}, {1, 0, 1}, {1, 1, 1}};
135
136   Matrix                                       nodeMatrix;
137   const Dali::Scene3D::Loader::NodeDefinition* node        = scene.GetNode(iNode);
138   Matrix                                       localMatrix = node->GetLocalSpace();
139   Matrix::Multiply(nodeMatrix, localMatrix, parentMatrix);
140
141   Vector3 volume[2];
142   if(node->GetExtents(nodeParams.mResources, volume[0], volume[1]))
143   {
144     for(uint32_t i = 0; i < BOX_POINT_COUNT; ++i)
145     {
146       Vector4 position       = Vector4(volume[BBIndex[i][0]].x, volume[BBIndex[i][1]].y, volume[BBIndex[i][2]].z, 1.0f);
147       Vector4 objectPosition = nodeMatrix * position;
148       objectPosition /= objectPosition.w;
149
150       AABB.ConsiderNewPointInVolume(Vector3(objectPosition));
151     }
152   }
153
154   if(node->mCustomization)
155   {
156     if(!node->mChildren.empty())
157     {
158       auto                         choice = choices.Get(node->mCustomization->mTag);
159       Dali::Scene3D::Loader::Index i      = std::min(choice != Dali::Scene3D::Loader::Customization::NONE ? choice : 0, static_cast<Dali::Scene3D::Loader::Index>(node->mChildren.size() - 1));
160
161       AddModelTreeToAABB(AABB, scene, choices, node->mChildren[i], nodeParams, nodeMatrix);
162     }
163   }
164   else
165   {
166     for(auto i : node->mChildren)
167     {
168       AddModelTreeToAABB(AABB, scene, choices, i, nodeParams, nodeMatrix);
169     }
170   }
171 }
172
173 } // anonymous namespace
174
175 ModelView::ModelView(const std::string& modelPath, const std::string& resourcePath)
176 : Control(ControlBehaviour(CONTROL_BEHAVIOUR_DEFAULT)),
177   mModelPath(modelPath),
178   mResourcePath(resourcePath),
179   mModelRoot(),
180   mNaturalSize(Vector3::ZERO),
181   mModelPivot(AnchorPoint::CENTER),
182   mIblScaleFactor(1.0f),
183   mFitSize(true),
184   mFitCenter(true)
185 {
186 }
187
188 ModelView::~ModelView()
189 {
190 }
191
192 Dali::Scene3D::ModelView ModelView::New(const std::string& modelPath, const std::string& resourcePath)
193 {
194   ModelView* impl = new ModelView(modelPath, resourcePath);
195
196   Dali::Scene3D::ModelView handle = Dali::Scene3D::ModelView(*impl);
197
198   // Second-phase init of the implementation
199   // This can only be done after the CustomActor connection has been made...
200   impl->Initialize();
201
202   return handle;
203 }
204
205 const Actor ModelView::GetModelRoot()
206 {
207   return mModelRoot;
208 }
209
210 void ModelView::FitSize(bool fit)
211 {
212   mFitSize = fit;
213   ScaleModel();
214 }
215
216 void ModelView::FitCenter(bool fit)
217 {
218   mFitCenter = fit;
219   FitModelPosition();
220 }
221
222 void ModelView::SetImageBasedLightSource(const std::string& diffuse, const std::string& specular, float scaleFactor)
223 {
224   Texture diffuseTexture  = Dali::Scene3D::Loader::LoadCubeMap(diffuse);
225   Texture specularTexture = Dali::Scene3D::Loader::LoadCubeMap(specular);
226   SetImageBasedLightTexture(diffuseTexture, specularTexture, scaleFactor);
227 }
228
229 void ModelView::SetImageBasedLightTexture(Dali::Texture diffuse, Dali::Texture specular, float scaleFactor)
230 {
231   if(diffuse && specular)
232   {
233     mDiffuseTexture  = diffuse;
234     mSpecularTexture = specular;
235     mIblScaleFactor  = scaleFactor;
236
237     UpdateImageBasedLight();
238   }
239 }
240
241 uint32_t ModelView::GetAnimationCount()
242 {
243   return mAnimations.size();
244 }
245
246 Dali::Animation ModelView::GetAnimation(uint32_t index)
247 {
248   Dali::Animation animation;
249   if(mAnimations.size() > index)
250   {
251     animation = mAnimations[index].second;
252   }
253   return animation;
254 }
255
256 Dali::Animation ModelView::GetAnimation(const std::string& name)
257 {
258   Dali::Animation animation;
259   if(!name.empty())
260   {
261     for(auto&& animationData : mAnimations)
262     {
263       if(animationData.first == name)
264       {
265         animation = animationData.second;
266         break;
267       }
268     }
269   }
270   return animation;
271 }
272
273 ///////////////////////////////////////////////////////////
274 //
275 // Private methods
276 //
277
278 void ModelView::OnSceneConnection(int depth)
279 {
280   if(!mModelRoot)
281   {
282     LoadModel();
283   }
284
285   Actor parent = Self().GetParent();
286   while(parent)
287   {
288     Scene3D::SceneView sceneView = Scene3D::SceneView::DownCast(parent);
289     if(sceneView)
290     {
291       GetImpl(sceneView).RegisterModelView(Scene3D::ModelView::DownCast(Self()));
292       mParentSceneView = sceneView;
293       break;
294     }
295     parent = parent.GetParent();
296   }
297
298   Control::OnSceneConnection(depth);
299 }
300
301 void ModelView::OnSceneDisconnection()
302 {
303   Scene3D::SceneView sceneView = mParentSceneView.GetHandle();
304   if(sceneView)
305   {
306     GetImpl(sceneView).UnregisterModelView(Scene3D::ModelView::DownCast(Self()));
307     mParentSceneView.Reset();
308   }
309   Control::OnSceneDisconnection();
310 }
311
312 Vector3 ModelView::GetNaturalSize()
313 {
314   if(!mModelRoot)
315   {
316     LoadModel();
317   }
318
319   return mNaturalSize;
320 }
321
322 float ModelView::GetHeightForWidth(float width)
323 {
324   Extents padding;
325   padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
326   return Control::GetHeightForWidth(width) + padding.top + padding.bottom;
327 }
328
329 float ModelView::GetWidthForHeight(float height)
330 {
331   Extents padding;
332   padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
333   return Control::GetWidthForHeight(height) + padding.start + padding.end;
334 }
335
336 void ModelView::OnRelayout(const Vector2& size, RelayoutContainer& container)
337 {
338   Control::OnRelayout(size, container);
339   ScaleModel();
340 }
341
342 void ModelView::LoadModel()
343 {
344   std::filesystem::path modelPath(mModelPath);
345   if(mResourcePath.empty())
346   {
347     mResourcePath = std::string(modelPath.parent_path()) + "/";
348   }
349   std::string extension = modelPath.extension();
350   std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
351
352   Dali::Scene3D::Loader::ResourceBundle::PathProvider pathProvider = [&](Dali::Scene3D::Loader::ResourceType::Value type) {
353     return mResourcePath;
354   };
355
356   Dali::Scene3D::Loader::ResourceBundle                        resources;
357   Dali::Scene3D::Loader::SceneDefinition                       scene;
358   std::vector<Dali::Scene3D::Loader::AnimationGroupDefinition> animGroups;
359   std::vector<Dali::Scene3D::Loader::CameraParameters>         cameraParameters;
360   std::vector<Dali::Scene3D::Loader::LightParameters>          lights;
361
362   std::vector<Dali::Scene3D::Loader::AnimationDefinition> animations;
363   animations.clear();
364
365   Dali::Scene3D::Loader::LoadResult output{resources, scene, animations, animGroups, cameraParameters, lights};
366
367   if(extension == DLI_EXTENSION)
368   {
369     Dali::Scene3D::Loader::DliLoader              loader;
370     Dali::Scene3D::Loader::DliLoader::InputParams input{
371       pathProvider(Dali::Scene3D::Loader::ResourceType::Mesh),
372       nullptr,
373       {},
374       {},
375       nullptr,
376       {}};
377     Dali::Scene3D::Loader::DliLoader::LoadParams loadParams{input, output};
378     if(!loader.LoadScene(mModelPath, loadParams))
379     {
380       Dali::Scene3D::Loader::ExceptionFlinger(ASSERT_LOCATION) << "Failed to load scene from '" << mModelPath << "': " << loader.GetParseError();
381     }
382   }
383   else if(extension == GLTF_EXTENSION)
384   {
385     Dali::Scene3D::Loader::ShaderDefinitionFactory sdf;
386     sdf.SetResources(resources);
387     Dali::Scene3D::Loader::LoadGltfScene(mModelPath, sdf, output);
388
389     resources.mEnvironmentMaps.push_back({});
390   }
391   else
392   {
393     DALI_LOG_ERROR("Unsupported model type.\n");
394   }
395
396   Dali::Scene3D::Loader::Transforms                   xforms{Dali::Scene3D::Loader::MatrixStack{}, Dali::Scene3D::Loader::ViewProjection{}};
397   Dali::Scene3D::Loader::NodeDefinition::CreateParams nodeParams{resources, xforms, {}, {}, {}};
398   Dali::Scene3D::Loader::Customization::Choices       choices;
399
400   mModelRoot = Actor::New();
401
402   BoundingVolume AABB;
403   for(auto iRoot : scene.GetRoots())
404   {
405     auto resourceRefs = resources.CreateRefCounter();
406     scene.CountResourceRefs(iRoot, choices, resourceRefs);
407     resources.CountEnvironmentReferences(resourceRefs);
408
409     resources.LoadResources(resourceRefs, pathProvider);
410
411     // glTF Mesh is defined in right hand coordinate system, with positive Y for Up direction.
412     // Because DALi uses left hand system, Y direciton will be flipped for environment map sampling.
413     for(auto&& env : resources.mEnvironmentMaps)
414     {
415       env.first.mYDirection = Y_DIRECTION;
416     }
417
418     if(auto actor = scene.CreateNodes(iRoot, choices, nodeParams))
419     {
420       scene.ConfigureSkeletonJoints(iRoot, resources.mSkeletons, actor);
421       scene.ConfigureSkinningShaders(resources, actor, std::move(nodeParams.mSkinnables));
422       ConfigureBlendShapeShaders(resources, scene, actor, std::move(nodeParams.mBlendshapeRequests));
423
424       scene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
425
426       mModelRoot.Add(actor);
427     }
428
429     AddModelTreeToAABB(AABB, scene, choices, iRoot, nodeParams, Matrix::IDENTITY);
430   }
431
432   if(!animations.empty())
433   {
434     auto getActor = [&](const std::string& name) {
435       return mModelRoot.FindChildByName(name);
436     };
437
438     mAnimations.clear();
439     for(auto&& animation : animations)
440     {
441       Dali::Animation anim = animation.ReAnimate(getActor);
442
443       mAnimations.push_back({animation.mName, anim});
444     }
445   }
446
447   mRenderableActors.clear();
448   CollectRenderableActor(mModelRoot);
449   UpdateImageBasedLight();
450
451   mNaturalSize = AABB.CalculateSize();
452   mModelPivot  = AABB.CalculatePivot();
453   mModelRoot.SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
454   Vector3 controlSize = Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
455   if(controlSize.x == 0.0f || controlSize.y == 0.0f)
456   {
457     Self().SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
458   }
459
460   FitModelPosition();
461   ScaleModel();
462
463   Self().Add(mModelRoot);
464
465   Self().SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
466   Self().SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3(mModelPivot.x, 1.0f - mModelPivot.y, mModelPivot.z));
467 }
468
469 void ModelView::ScaleModel()
470 {
471   if(mModelRoot)
472   {
473     Vector3 size = Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
474     if(mFitSize && size.x > 0.0f && size.y > 0.0f)
475     {
476       float scaleFactor = MAXFLOAT;
477       scaleFactor       = std::min(size.x / mNaturalSize.x, scaleFactor);
478       scaleFactor       = std::min(size.y / mNaturalSize.y, scaleFactor);
479       // Models in glTF and dli are defined as right hand coordinate system.
480       // DALi uses left hand coordinate system. Scaling negative is for change winding order.
481       mModelRoot.SetProperty(Dali::Actor::Property::SCALE, Y_DIRECTION * scaleFactor);
482     }
483     else
484     {
485       // Models in glTF and dli are defined as right hand coordinate system.
486       // DALi uses left hand coordinate system. Scaling negative is for change winding order.
487       mModelRoot.SetProperty(Dali::Actor::Property::SCALE, Y_DIRECTION);
488     }
489   }
490 }
491
492 void ModelView::FitModelPosition()
493 {
494   if(mModelRoot)
495   {
496     if(mFitCenter)
497     {
498       // Loaded model pivot is not the model center.
499       mModelRoot.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
500       mModelRoot.SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3::ONE - mModelPivot);
501     }
502     else
503     {
504       mModelRoot.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
505       mModelRoot.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
506     }
507   }
508 }
509
510 void ModelView::CollectRenderableActor(Actor actor)
511 {
512   uint32_t rendererCount = actor.GetRendererCount();
513   if(rendererCount)
514   {
515     mRenderableActors.push_back(actor);
516   }
517
518   uint32_t childrenCount = actor.GetChildCount();
519   for(uint32_t i = 0; i < childrenCount; ++i)
520   {
521     CollectRenderableActor(actor.GetChildAt(i));
522   }
523 }
524
525 void ModelView::UpdateImageBasedLight()
526 {
527   if(!mDiffuseTexture || !mSpecularTexture)
528   {
529     return;
530   }
531
532   for(auto&& actor : mRenderableActors)
533   {
534     Actor renderableActor = actor.GetHandle();
535     if(renderableActor)
536     {
537       renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), mIblScaleFactor);
538
539       uint32_t rendererCount = renderableActor.GetRendererCount();
540       for(uint32_t i = 0; i < rendererCount; ++i)
541       {
542         Dali::Renderer renderer = renderableActor.GetRendererAt(i);
543         if(renderer)
544         {
545           Dali::TextureSet textures = renderer.GetTextures();
546           if(textures)
547           {
548             uint32_t textureCount = textures.GetTextureCount();
549             // EnvMap requires at least 2 texture, diffuse and specular
550             if(textureCount > 2u)
551             {
552               textures.SetTexture(textureCount - OFFSET_FOR_DIFFUSE_CUBE_TEXTURE, mDiffuseTexture);
553               textures.SetTexture(textureCount - OFFSET_FOR_SPECULAR_CUBE_TEXTURE, mSpecularTexture);
554             }
555           }
556         }
557       }
558     }
559   }
560 }
561
562 } // namespace Internal
563 } // namespace Scene3D
564 } // namespace Dali