DALi Version 2.2.11
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / internal / controls / model / model-impl.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/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/math/math-utils.h>
28 #include <dali/public-api/object/type-registry-helper.h>
29 #include <dali/public-api/object/type-registry.h>
30 #include <dali/integration-api/adaptor-framework/adaptor.h>
31 #include <filesystem>
32
33 // INTERNAL INCLUDES
34 #include <dali-scene3d/internal/controls/scene-view/scene-view-impl.h>
35 #include <dali-scene3d/public-api/controls/model/model.h>
36 #include <dali-scene3d/public-api/loader/animation-definition.h>
37 #include <dali-scene3d/public-api/loader/camera-parameters.h>
38 #include <dali-scene3d/public-api/loader/dli-loader.h>
39 #include <dali-scene3d/public-api/loader/gltf2-loader.h>
40 #include <dali-scene3d/public-api/loader/light-parameters.h>
41 #include <dali-scene3d/public-api/loader/load-result.h>
42 #include <dali-scene3d/public-api/loader/node-definition.h>
43 #include <dali-scene3d/public-api/loader/scene-definition.h>
44 #include <dali-scene3d/public-api/loader/shader-definition-factory.h>
45
46 using namespace Dali;
47
48 namespace Dali
49 {
50 namespace Scene3D
51 {
52 namespace Internal
53 {
54 namespace
55 {
56 BaseHandle Create()
57 {
58   return Scene3D::Model::New(std::string());
59 }
60
61 // Setup properties, signals and actions using the type-registry.
62 DALI_TYPE_REGISTRATION_BEGIN(Scene3D::Model, Toolkit::Control, Create);
63 DALI_TYPE_REGISTRATION_END()
64
65 static constexpr uint32_t OFFSET_FOR_DIFFUSE_CUBE_TEXTURE  = 2u;
66 static constexpr uint32_t OFFSET_FOR_SPECULAR_CUBE_TEXTURE = 1u;
67
68 static constexpr Vector3 Y_DIRECTION(1.0f, -1.0f, 1.0f);
69
70 static constexpr bool DEFAULT_MODEL_CHILDREN_SENSITIVE = false;
71 static constexpr bool DEFAULT_MODEL_CHILDREN_FOCUSABLE = false;
72
73 struct BoundingVolume
74 {
75   void Init()
76   {
77     pointMin = Vector3(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
78     pointMax = Vector3(std::numeric_limits<float>::min(), std::numeric_limits<float>::min(), std::numeric_limits<float>::min());
79   }
80
81   void ConsiderNewPointInVolume(const Vector3& position)
82   {
83     pointMin.x = std::min(position.x, pointMin.x);
84     pointMin.y = std::min(position.y, pointMin.y);
85     pointMin.z = std::min(position.z, pointMin.z);
86
87     pointMax.x = std::max(position.x, pointMax.x);
88     pointMax.y = std::max(position.y, pointMax.y);
89     pointMax.z = std::max(position.z, pointMax.z);
90   }
91
92   Vector3 CalculateSize()
93   {
94     return pointMax - pointMin;
95   }
96
97   Vector3 CalculatePivot()
98   {
99     Vector3 pivot = pointMin / (pointMin - pointMax);
100     for(uint32_t i = 0; i < 3; ++i)
101     {
102       // To avoid divid by zero
103       if(Dali::Equals(pointMin[i], pointMax[i]))
104       {
105         pivot[i] = 0.5f;
106       }
107     }
108     return pivot;
109   }
110
111   Vector3 pointMin;
112   Vector3 pointMax;
113 };
114
115 void ConfigureBlendShapeShaders(
116   Dali::Scene3D::Loader::ResourceBundle& resources, const Dali::Scene3D::Loader::SceneDefinition& scene, Actor root, std::vector<Dali::Scene3D::Loader::BlendshapeShaderConfigurationRequest>&& requests)
117 {
118   std::vector<std::string> errors;
119   auto                     onError = [&errors](const std::string& msg) { errors.push_back(msg); };
120   if(!scene.ConfigureBlendshapeShaders(resources, root, std::move(requests), onError))
121   {
122     Dali::Scene3D::Loader::ExceptionFlinger flinger(ASSERT_LOCATION);
123     for(auto& msg : errors)
124     {
125       flinger << msg << '\n';
126     }
127   }
128 }
129
130 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)
131 {
132   static constexpr uint32_t BOX_POINT_COUNT             = 8;
133   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}};
134
135   Matrix                                       nodeMatrix;
136   const Dali::Scene3D::Loader::NodeDefinition* node        = scene.GetNode(iNode);
137   Matrix                                       localMatrix = node->GetLocalSpace();
138   Matrix::Multiply(nodeMatrix, localMatrix, parentMatrix);
139
140   Vector3 volume[2];
141   if(node->GetExtents(nodeParams.mResources, volume[0], volume[1]))
142   {
143     for(uint32_t i = 0; i < BOX_POINT_COUNT; ++i)
144     {
145       Vector4 position       = Vector4(volume[BBIndex[i][0]].x, volume[BBIndex[i][1]].y, volume[BBIndex[i][2]].z, 1.0f);
146       Vector4 objectPosition = nodeMatrix * position;
147       objectPosition /= objectPosition.w;
148
149       AABB.ConsiderNewPointInVolume(Vector3(objectPosition));
150     }
151   }
152
153   if(node->mCustomization)
154   {
155     if(!node->mChildren.empty())
156     {
157       auto                         choice = choices.Get(node->mCustomization->mTag);
158       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));
159
160       AddModelTreeToAABB(AABB, scene, choices, node->mChildren[i], nodeParams, nodeMatrix);
161     }
162   }
163   else
164   {
165     for(auto i : node->mChildren)
166     {
167       AddModelTreeToAABB(AABB, scene, choices, i, nodeParams, nodeMatrix);
168     }
169   }
170 }
171
172 } // anonymous namespace
173
174 Model::Model(const std::string& modelUrl, const std::string& resourceDirectoryUrl)
175 : Control(ControlBehaviour(DISABLE_SIZE_NEGOTIATION | DISABLE_STYLE_CHANGE_SIGNALS)),
176   mModelUrl(modelUrl),
177   mResourceDirectoryUrl(resourceDirectoryUrl),
178   mModelRoot(),
179   mNaturalSize(Vector3::ZERO),
180   mModelPivot(AnchorPoint::CENTER),
181   mSceneIblScaleFactor(1.0f),
182   mIblScaleFactor(1.0f),
183   mModelChildrenSensitive(DEFAULT_MODEL_CHILDREN_SENSITIVE),
184   mModelChildrenFocusable(DEFAULT_MODEL_CHILDREN_FOCUSABLE),
185   mModelResourceReady(false),
186   mIblDiffuseResourceReady(true),
187   mIblSpecularResourceReady(true),
188   mIblDiffuseDirty(false),
189   mIblSpecularDirty(false)
190 {
191 }
192
193 Model::~Model()
194 {
195   ResetResourceTasks();
196 }
197
198 Dali::Scene3D::Model Model::New(const std::string& modelUrl, const std::string& resourceDirectoryUrl)
199 {
200   Model* impl = new Model(modelUrl, resourceDirectoryUrl);
201
202   Dali::Scene3D::Model handle = Dali::Scene3D::Model(*impl);
203
204   // Second-phase init of the implementation
205   // This can only be done after the CustomActor connection has been made...
206   impl->Initialize();
207
208   return handle;
209 }
210
211 const Actor Model::GetModelRoot() const
212 {
213   return mModelRoot;
214 }
215
216 void Model::SetChildrenSensitive(bool enable)
217 {
218   if(mModelChildrenSensitive != enable)
219   {
220     mModelChildrenSensitive = enable;
221     if(mModelRoot)
222     {
223       mModelRoot.SetProperty(Dali::Actor::Property::SENSITIVE, mModelChildrenSensitive);
224     }
225   }
226 }
227
228 bool Model::GetChildrenSensitive() const
229 {
230   return mModelChildrenSensitive;
231 }
232
233 void Model::SetChildrenFocusable(bool enable)
234 {
235   if(mModelChildrenFocusable != enable)
236   {
237     mModelChildrenFocusable = enable;
238     if(mModelRoot)
239     {
240       mModelRoot.SetProperty(Dali::Actor::Property::KEYBOARD_FOCUSABLE, mModelChildrenFocusable);
241       mModelRoot.SetProperty(Dali::DevelActor::Property::KEYBOARD_FOCUSABLE_CHILDREN, mModelChildrenFocusable);
242     }
243   }
244 }
245
246 bool Model::GetChildrenFocusable() const
247 {
248   return mModelChildrenFocusable;
249 }
250
251 void Model::SetImageBasedLightSource(const std::string& diffuseUrl, const std::string& specularUrl, float scaleFactor)
252 {
253   bool needIblReset = false;
254   bool isOnScene = Self().GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE);
255   if(mDiffuseIblUrl != diffuseUrl)
256   {
257     mDiffuseIblUrl = diffuseUrl;
258     if(mDiffuseIblUrl.empty())
259     {
260       needIblReset             = true;
261     }
262     else
263     {
264       mIblDiffuseDirty         = true;
265       mIblDiffuseResourceReady = false;
266     }
267   }
268
269   if(mSpecularIblUrl != specularUrl)
270   {
271     mSpecularIblUrl = specularUrl;
272     if(mSpecularIblUrl.empty())
273     {
274       needIblReset              = true;
275     }
276     else
277     {
278       mIblSpecularDirty         = true;
279       mIblSpecularResourceReady = false;
280     }
281   }
282
283   // If one or both of diffuse url and specular url are empty,
284   // we don't need to request to load texture.
285   if(needIblReset)
286   {
287     if(mIblDiffuseLoadTask)
288     {
289       Dali::AsyncTaskManager::Get().RemoveTask(mIblDiffuseLoadTask);
290       mIblDiffuseLoadTask.Reset();
291     }
292
293     if(mIblSpecularLoadTask)
294     {
295       Dali::AsyncTaskManager::Get().RemoveTask(mIblSpecularLoadTask);
296       mIblSpecularLoadTask.Reset();
297     }
298
299     mIblDiffuseDirty          = false;
300     mIblSpecularDirty         = false;
301     mIblDiffuseResourceReady  = true;
302     mIblSpecularResourceReady = true;
303
304     mDiffuseTexture.Reset();
305     mSpecularTexture.Reset();
306     UpdateImageBasedLightTexture();
307   }
308   else
309   {
310     if(isOnScene && mIblDiffuseDirty)
311     {
312       if(mIblDiffuseLoadTask)
313       {
314         Dali::AsyncTaskManager::Get().RemoveTask(mIblDiffuseLoadTask);
315         mIblDiffuseLoadTask.Reset();
316       }
317       mIblDiffuseLoadTask = new EnvironmentMapLoadTask(mDiffuseIblUrl, Scene3D::EnvironmentMapType::CUBEMAP, MakeCallback(this, &Model::OnIblDiffuseLoadComplete));
318       Dali::AsyncTaskManager::Get().AddTask(mIblDiffuseLoadTask);
319       mIblDiffuseDirty = false;
320     }
321
322     if(isOnScene && mIblSpecularDirty)
323     {
324       if(mIblSpecularLoadTask)
325       {
326         Dali::AsyncTaskManager::Get().RemoveTask(mIblSpecularLoadTask);
327         mIblSpecularLoadTask.Reset();
328       }
329       mIblSpecularLoadTask = new EnvironmentMapLoadTask(mSpecularIblUrl, Scene3D::EnvironmentMapType::CUBEMAP, MakeCallback(this, &Model::OnIblSpecularLoadComplete));
330       Dali::AsyncTaskManager::Get().AddTask(mIblSpecularLoadTask);
331       mIblSpecularDirty = false;
332     }
333   }
334
335   if(!Dali::Equals(mIblScaleFactor, scaleFactor))
336   {
337     mIblScaleFactor = scaleFactor;
338     UpdateImageBasedLightScaleFactor();
339   }
340
341   // If diffuse and specular textures are already loaded, emits resource ready signal here.
342   if(IsResourceReady())
343   {
344     Control::SetResourceReady(false);
345   }
346 }
347
348 void Model::SetImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float scaleFactor)
349 {
350   // If input texture is wrong, Model is rendered with SceneView's IBL.
351   if(mDiffuseTexture != diffuseTexture || mSpecularTexture != specularTexture)
352   {
353     mDiffuseTexture  = diffuseTexture;
354     mSpecularTexture = specularTexture;
355     mIblScaleFactor  = scaleFactor;
356     UpdateImageBasedLightTexture();
357   }
358 }
359
360 void Model::SetImageBasedLightScaleFactor(float scaleFactor)
361 {
362   mIblScaleFactor = scaleFactor;
363   if(mDiffuseTexture && mSpecularTexture)
364   {
365     UpdateImageBasedLightScaleFactor();
366   }
367 }
368
369 float Model::GetImageBasedLightScaleFactor() const
370 {
371   return mIblScaleFactor;
372 }
373
374 uint32_t Model::GetAnimationCount() const
375 {
376   return mAnimations.size();
377 }
378
379 Dali::Animation Model::GetAnimation(uint32_t index) const
380 {
381   Dali::Animation animation;
382   if(mAnimations.size() > index)
383   {
384     animation = mAnimations[index].second;
385   }
386   return animation;
387 }
388
389 Dali::Animation Model::GetAnimation(const std::string& name) const
390 {
391   Dali::Animation animation;
392   if(!name.empty())
393   {
394     for(auto&& animationData : mAnimations)
395     {
396       if(animationData.first == name)
397       {
398         animation = animationData.second;
399         break;
400       }
401     }
402   }
403   return animation;
404 }
405
406 ///////////////////////////////////////////////////////////
407 //
408 // Private methods
409 //
410
411 void Model::OnInitialize()
412 {
413   // Make ParentOrigin as Center.
414   Self().SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
415 }
416
417 void Model::OnSceneConnection(int depth)
418 {
419   if(!mModelLoadTask && !mModelRoot)
420   {
421     Scene3D::Loader::InitializeGltfLoader();
422     mModelLoadTask = new ModelLoadTask(mModelUrl, mResourceDirectoryUrl, MakeCallback(this, &Model::OnModelLoadComplete));
423     Dali::AsyncTaskManager::Get().AddTask(mModelLoadTask);
424   }
425   // If diffuse and specular url is not valid, IBL does not need to be loaded.
426   if(!mDiffuseIblUrl.empty() && !mSpecularIblUrl.empty())
427   {
428     SetImageBasedLightSource(mDiffuseIblUrl, mSpecularIblUrl, mIblScaleFactor);
429   }
430
431   Actor parent = Self().GetParent();
432   while(parent)
433   {
434     Scene3D::SceneView sceneView = Scene3D::SceneView::DownCast(parent);
435     if(sceneView)
436     {
437       GetImpl(sceneView).RegisterSceneItem(this);
438       mParentSceneView = sceneView;
439       break;
440     }
441     parent = parent.GetParent();
442   }
443
444   Control::OnSceneConnection(depth);
445 }
446
447 void Model::OnSceneDisconnection()
448 {
449   Scene3D::SceneView sceneView = mParentSceneView.GetHandle();
450   if(sceneView)
451   {
452     GetImpl(sceneView).UnregisterSceneItem(this);
453     mParentSceneView.Reset();
454   }
455   Control::OnSceneDisconnection();
456 }
457
458 Vector3 Model::GetNaturalSize()
459 {
460   if(!mModelRoot)
461   {
462     DALI_LOG_ERROR("Model is still not loaded.\n");
463     return Vector3::ZERO;
464   }
465
466   return mNaturalSize;
467 }
468
469 float Model::GetHeightForWidth(float width)
470 {
471   Extents padding;
472   padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
473   return Control::GetHeightForWidth(width) + padding.top + padding.bottom;
474 }
475
476 float Model::GetWidthForHeight(float height)
477 {
478   Extents padding;
479   padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
480   return Control::GetWidthForHeight(height) + padding.start + padding.end;
481 }
482
483 void Model::OnRelayout(const Vector2& size, RelayoutContainer& container)
484 {
485   Control::OnRelayout(size, container);
486   ScaleModel();
487 }
488
489 bool Model::IsResourceReady() const
490 {
491   return mModelResourceReady && mIblDiffuseResourceReady && mIblSpecularResourceReady;
492 }
493
494 void Model::ScaleModel()
495 {
496   if(mModelRoot)
497   {
498     float   scale = 1.0f;
499     Vector3 size  = Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
500     if(size.x > 0.0f && size.y > 0.0f)
501     {
502       scale = MAXFLOAT;
503       scale = std::min(size.x / mNaturalSize.x, scale);
504       scale = std::min(size.y / mNaturalSize.y, scale);
505     }
506     // Models in glTF and dli are defined as right hand coordinate system.
507     // DALi uses left hand coordinate system. Scaling negative is for change winding order.
508     mModelRoot.SetProperty(Dali::Actor::Property::SCALE, Y_DIRECTION * scale);
509   }
510 }
511
512 void Model::FitModelPosition()
513 {
514   if(mModelRoot)
515   {
516     // Loaded model pivot is not the model center.
517     mModelRoot.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
518     mModelRoot.SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3::ONE - mModelPivot);
519   }
520 }
521
522 void Model::CollectRenderableActor(Actor actor)
523 {
524   uint32_t rendererCount = actor.GetRendererCount();
525   if(rendererCount)
526   {
527     mRenderableActors.push_back(actor);
528   }
529
530   uint32_t childrenCount = actor.GetChildCount();
531   for(uint32_t i = 0; i < childrenCount; ++i)
532   {
533     CollectRenderableActor(actor.GetChildAt(i));
534   }
535 }
536
537 void Model::UpdateImageBasedLightTexture()
538 {
539   Dali::Texture currentDiffuseTexture  = (mDiffuseTexture && mSpecularTexture) ? mDiffuseTexture : mSceneDiffuseTexture;
540   Dali::Texture currentSpecularTexture = (mDiffuseTexture && mSpecularTexture) ? mSpecularTexture : mSceneSpecularTexture;
541   float         currentIblScaleFactor  = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
542
543   if(!currentDiffuseTexture || !currentSpecularTexture)
544   {
545     currentDiffuseTexture  = mDefaultDiffuseTexture;
546     currentSpecularTexture = mDefaultSpecularTexture;
547     currentIblScaleFactor  = Dali::Scene3D::Loader::EnvironmentDefinition::GetDefaultIntensity();
548   }
549
550   for(auto&& actor : mRenderableActors)
551   {
552     Actor renderableActor = actor.GetHandle();
553     if(renderableActor)
554     {
555       uint32_t rendererCount = renderableActor.GetRendererCount();
556       for(uint32_t i = 0; i < rendererCount; ++i)
557       {
558         Dali::Renderer renderer = renderableActor.GetRendererAt(i);
559         if(renderer)
560         {
561           Dali::TextureSet textures = renderer.GetTextures();
562           if(textures)
563           {
564             uint32_t textureCount = textures.GetTextureCount();
565             // EnvMap requires at least 2 texture, diffuse and specular
566             if(textureCount > 2u)
567             {
568               textures.SetTexture(textureCount - OFFSET_FOR_DIFFUSE_CUBE_TEXTURE, currentDiffuseTexture);
569               textures.SetTexture(textureCount - OFFSET_FOR_SPECULAR_CUBE_TEXTURE, currentSpecularTexture);
570             }
571           }
572         }
573       }
574       renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), currentIblScaleFactor);
575     }
576   }
577 }
578
579 void Model::UpdateImageBasedLightScaleFactor()
580 {
581   if((!mDiffuseTexture || !mSpecularTexture) &&
582      (!mSceneDiffuseTexture || !mSceneSpecularTexture))
583   {
584     return;
585   }
586
587   float currentIblScaleFactor = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
588   for(auto&& actor : mRenderableActors)
589   {
590     Actor renderableActor = actor.GetHandle();
591     if(renderableActor)
592     {
593       renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), currentIblScaleFactor);
594     }
595   }
596 }
597
598 void Model::NotifyImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float scaleFactor)
599 {
600   if(mSceneDiffuseTexture != diffuseTexture || mSceneSpecularTexture != specularTexture)
601   {
602     mSceneDiffuseTexture  = diffuseTexture;
603     mSceneSpecularTexture = specularTexture;
604     mSceneIblScaleFactor  = scaleFactor;
605     // If Model IBL is not set, use SceneView's IBL.
606     if(!mDiffuseTexture || !mSpecularTexture)
607     {
608       UpdateImageBasedLightTexture();
609     }
610   }
611 }
612
613 void Model::NotifyImageBasedLightScaleFactor(float scaleFactor)
614 {
615   mSceneIblScaleFactor = scaleFactor;
616   if(mSceneDiffuseTexture && mSceneSpecularTexture)
617   {
618     UpdateImageBasedLightScaleFactor();
619   }
620 }
621
622 void Model::OnModelLoadComplete()
623 {
624   if(!mModelLoadTask->HasSucceeded())
625   {
626     ResetResourceTasks();
627     return;
628   }
629
630   mModelRoot = Actor::New();
631   mModelRoot.SetProperty(Actor::Property::COLOR_MODE, ColorMode::USE_OWN_MULTIPLY_PARENT_COLOR);
632
633   BoundingVolume                                      AABB;
634   auto*                                               resources = &(mModelLoadTask->mResources);
635   auto*                                               scene     = &(mModelLoadTask->mScene);
636   Dali::Scene3D::Loader::Transforms                   xforms{Dali::Scene3D::Loader::MatrixStack{}, Dali::Scene3D::Loader::ViewProjection{}};
637   Dali::Scene3D::Loader::NodeDefinition::CreateParams nodeParams{*resources, xforms, {}, {}, {}};
638   uint32_t rootCount = 0u;
639   for(auto iRoot : scene->GetRoots())
640   {
641     resources->GenerateResources(mModelLoadTask->mResourceRefCounts[rootCount]);
642
643     if(auto actor = scene->CreateNodes(iRoot, mModelLoadTask->mResourceChoices, nodeParams))
644     {
645       scene->ConfigureSkeletonJoints(iRoot, resources->mSkeletons, actor);
646       scene->ConfigureSkinningShaders(*resources, actor, std::move(nodeParams.mSkinnables));
647       ConfigureBlendShapeShaders(*resources, *scene, actor, std::move(nodeParams.mBlendshapeRequests));
648
649       scene->ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
650
651       mModelRoot.Add(actor);
652     }
653
654     AddModelTreeToAABB(AABB, *scene, mModelLoadTask->mResourceChoices, iRoot, nodeParams, Matrix::IDENTITY);
655     rootCount++;
656   }
657
658   if(!resources->mEnvironmentMaps.empty())
659   {
660     mDefaultDiffuseTexture  = resources->mEnvironmentMaps.front().second.mDiffuse;
661     mDefaultSpecularTexture = resources->mEnvironmentMaps.front().second.mSpecular;
662   }
663
664   if(!mModelLoadTask->mAnimations.empty())
665   {
666     auto getActor = [&](const Scene3D::Loader::AnimatedProperty& property)
667     {
668       Dali::Actor actor;
669       if(property.mNodeIndex != Scene3D::Loader::INVALID_INDEX)
670       {
671         auto* node = scene->GetNode(property.mNodeIndex);
672         if(node != nullptr)
673         {
674           actor = mModelRoot.FindChildById(node->mNodeId);
675         }
676       }
677       else
678       {
679         actor = mModelRoot.FindChildByName(property.mNodeName);
680       }
681       return actor;
682     };
683
684     mAnimations.clear();
685     for(auto&& animation : mModelLoadTask->mAnimations)
686     {
687       Dali::Animation anim = animation.ReAnimate(getActor);
688
689       mAnimations.push_back({animation.mName, anim});
690     }
691   }
692
693   mRenderableActors.clear();
694   CollectRenderableActor(mModelRoot);
695
696   UpdateImageBasedLightTexture();
697   UpdateImageBasedLightScaleFactor();
698
699   mNaturalSize = AABB.CalculateSize();
700   mModelPivot  = AABB.CalculatePivot();
701   mModelRoot.SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
702   Vector3 controlSize = Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
703   if(Dali::EqualsZero(controlSize.x) || Dali::EqualsZero(controlSize.y))
704   {
705     Self().SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
706   }
707
708   FitModelPosition();
709   ScaleModel();
710
711   mModelRoot.SetProperty(Dali::Actor::Property::SENSITIVE, mModelChildrenSensitive);
712   mModelRoot.SetProperty(Dali::Actor::Property::KEYBOARD_FOCUSABLE, mModelChildrenFocusable);
713   mModelRoot.SetProperty(Dali::DevelActor::Property::KEYBOARD_FOCUSABLE_CHILDREN, mModelChildrenFocusable);
714
715   Self().Add(mModelRoot);
716
717   Self().SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3(mModelPivot.x, 1.0f - mModelPivot.y, mModelPivot.z));
718
719   mModelResourceReady = true;
720
721   if(IsResourceReady())
722   {
723     Control::SetResourceReady(false);
724   }
725   mModelLoadTask.Reset();
726 }
727
728 void Model::OnIblDiffuseLoadComplete()
729 {
730   mDiffuseTexture = (mIblDiffuseLoadTask->HasSucceeded()) ? mIblDiffuseLoadTask->GetEnvironmentMap().GetTexture() : Texture();
731   mIblDiffuseResourceReady = true;
732   if(mIblDiffuseResourceReady && mIblSpecularResourceReady)
733   {
734     OnIblLoadComplete();
735   }
736   mIblDiffuseLoadTask.Reset();
737 }
738
739 void Model::OnIblSpecularLoadComplete()
740 {
741   mSpecularTexture = (mIblSpecularLoadTask->HasSucceeded()) ? mIblSpecularLoadTask->GetEnvironmentMap().GetTexture() : Texture();
742   mIblSpecularResourceReady = true;
743   if(mIblDiffuseResourceReady && mIblSpecularResourceReady)
744   {
745     OnIblLoadComplete();
746   }
747   mIblSpecularLoadTask.Reset();
748 }
749
750 void Model::OnIblLoadComplete()
751 {
752   UpdateImageBasedLightTexture();
753
754   if(IsResourceReady())
755   {
756     Control::SetResourceReady(false);
757   }
758 }
759
760 void Model::ResetResourceTasks()
761 {
762   if(Dali::Adaptor::IsAvailable())
763   {
764     if(mModelLoadTask)
765     {
766       Dali::AsyncTaskManager::Get().RemoveTask(mModelLoadTask);
767       mModelLoadTask.Reset();
768     }
769     if(mIblDiffuseLoadTask)
770     {
771       Dali::AsyncTaskManager::Get().RemoveTask(mIblDiffuseLoadTask);
772       mIblDiffuseLoadTask.Reset();
773     }
774     if(mIblSpecularLoadTask)
775     {
776       Dali::AsyncTaskManager::Get().RemoveTask(mIblSpecularLoadTask);
777       mIblSpecularLoadTask.Reset();
778     }
779   }
780 }
781
782 } // namespace Internal
783 } // namespace Scene3D
784 } // namespace Dali