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