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