Merge "Refactoring model-impl.cpp" into devel/master
[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/adaptor-framework/adaptor.h>
27 #include <dali/integration-api/debug.h>
28 #include <dali/public-api/math/math-utils.h>
29 #include <dali/public-api/object/type-registry-helper.h>
30 #include <dali/public-api/object/type-registry.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)
120   { 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     ResetResourceTask(mIblDiffuseLoadTask);
289     ResetResourceTask(mIblSpecularLoadTask);
290
291     mIblDiffuseDirty          = false;
292     mIblSpecularDirty         = false;
293     mIblDiffuseResourceReady  = true;
294     mIblSpecularResourceReady = true;
295
296     mDiffuseTexture.Reset();
297     mSpecularTexture.Reset();
298     UpdateImageBasedLightTexture();
299   }
300   else
301   {
302     if(isOnScene && mIblDiffuseDirty)
303     {
304       ResetResourceTask(mIblDiffuseLoadTask);
305       mIblDiffuseLoadTask = new EnvironmentMapLoadTask(mDiffuseIblUrl, Scene3D::EnvironmentMapType::CUBEMAP, MakeCallback(this, &Model::OnIblDiffuseLoadComplete));
306       Dali::AsyncTaskManager::Get().AddTask(mIblDiffuseLoadTask);
307       mIblDiffuseDirty = false;
308     }
309
310     if(isOnScene && mIblSpecularDirty)
311     {
312       ResetResourceTask(mIblSpecularLoadTask);
313       mIblSpecularLoadTask = new EnvironmentMapLoadTask(mSpecularIblUrl, Scene3D::EnvironmentMapType::CUBEMAP, MakeCallback(this, &Model::OnIblSpecularLoadComplete));
314       Dali::AsyncTaskManager::Get().AddTask(mIblSpecularLoadTask);
315       mIblSpecularDirty = false;
316     }
317   }
318
319   if(!Dali::Equals(mIblScaleFactor, scaleFactor))
320   {
321     mIblScaleFactor = scaleFactor;
322     UpdateImageBasedLightScaleFactor();
323   }
324
325   // If diffuse and specular textures are already loaded, emits resource ready signal here.
326   NotifyResourceReady();
327 }
328
329 void Model::SetImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float scaleFactor)
330 {
331   // If input texture is wrong, Model is rendered with SceneView's IBL.
332   if(mDiffuseTexture != diffuseTexture || mSpecularTexture != specularTexture)
333   {
334     mDiffuseTexture  = diffuseTexture;
335     mSpecularTexture = specularTexture;
336     mIblScaleFactor  = scaleFactor;
337     UpdateImageBasedLightTexture();
338   }
339 }
340
341 void Model::SetImageBasedLightScaleFactor(float scaleFactor)
342 {
343   mIblScaleFactor = scaleFactor;
344   if(mDiffuseTexture && mSpecularTexture)
345   {
346     UpdateImageBasedLightScaleFactor();
347   }
348 }
349
350 float Model::GetImageBasedLightScaleFactor() const
351 {
352   return mIblScaleFactor;
353 }
354
355 uint32_t Model::GetAnimationCount() const
356 {
357   return mAnimations.size();
358 }
359
360 Dali::Animation Model::GetAnimation(uint32_t index) const
361 {
362   Dali::Animation animation;
363   if(mAnimations.size() > index)
364   {
365     animation = mAnimations[index].second;
366   }
367   return animation;
368 }
369
370 Dali::Animation Model::GetAnimation(const std::string& name) const
371 {
372   Dali::Animation animation;
373   if(!name.empty())
374   {
375     for(auto&& animationData : mAnimations)
376     {
377       if(animationData.first == name)
378       {
379         animation = animationData.second;
380         break;
381       }
382     }
383   }
384   return animation;
385 }
386
387 ///////////////////////////////////////////////////////////
388 //
389 // Private methods
390 //
391
392 void Model::OnInitialize()
393 {
394   // Make ParentOrigin as Center.
395   Self().SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
396 }
397
398 void Model::OnSceneConnection(int depth)
399 {
400   if(!mModelLoadTask && !mModelRoot)
401   {
402     Scene3D::Loader::InitializeGltfLoader();
403     mModelLoadTask = new ModelLoadTask(mModelUrl, mResourceDirectoryUrl, MakeCallback(this, &Model::OnModelLoadComplete));
404     Dali::AsyncTaskManager::Get().AddTask(mModelLoadTask);
405   }
406   // If diffuse and specular url is not valid, IBL does not need to be loaded.
407   if(!mDiffuseIblUrl.empty() && !mSpecularIblUrl.empty())
408   {
409     SetImageBasedLightSource(mDiffuseIblUrl, mSpecularIblUrl, mIblScaleFactor);
410   }
411
412   Actor parent = Self().GetParent();
413   while(parent)
414   {
415     Scene3D::SceneView sceneView = Scene3D::SceneView::DownCast(parent);
416     if(sceneView)
417     {
418       GetImpl(sceneView).RegisterSceneItem(this);
419       mParentSceneView = sceneView;
420       break;
421     }
422     parent = parent.GetParent();
423   }
424
425   Control::OnSceneConnection(depth);
426 }
427
428 void Model::OnSceneDisconnection()
429 {
430   Scene3D::SceneView sceneView = mParentSceneView.GetHandle();
431   if(sceneView)
432   {
433     GetImpl(sceneView).UnregisterSceneItem(this);
434     mParentSceneView.Reset();
435   }
436   Control::OnSceneDisconnection();
437 }
438
439 Vector3 Model::GetNaturalSize()
440 {
441   if(!mModelRoot)
442   {
443     DALI_LOG_ERROR("Model is still not loaded.\n");
444     return Vector3::ZERO;
445   }
446
447   return mNaturalSize;
448 }
449
450 float Model::GetHeightForWidth(float width)
451 {
452   Extents padding;
453   padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
454   return Control::GetHeightForWidth(width) + padding.top + padding.bottom;
455 }
456
457 float Model::GetWidthForHeight(float height)
458 {
459   Extents padding;
460   padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
461   return Control::GetWidthForHeight(height) + padding.start + padding.end;
462 }
463
464 void Model::OnRelayout(const Vector2& size, RelayoutContainer& container)
465 {
466   Control::OnRelayout(size, container);
467   ScaleModel();
468 }
469
470 bool Model::IsResourceReady() const
471 {
472   return mModelResourceReady && mIblDiffuseResourceReady && mIblSpecularResourceReady;
473 }
474
475 void Model::ScaleModel()
476 {
477   if(!mModelRoot)
478   {
479     return;
480   }
481
482   float   scale = 1.0f;
483   Vector3 size  = Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
484   if(size.x > 0.0f && size.y > 0.0f)
485   {
486     scale = MAXFLOAT;
487     scale = std::min(size.x / mNaturalSize.x, scale);
488     scale = std::min(size.y / mNaturalSize.y, scale);
489   }
490   // Models in glTF and dli are defined as right hand coordinate system.
491   // DALi uses left hand coordinate system. Scaling negative is for change winding order.
492   mModelRoot.SetProperty(Dali::Actor::Property::SCALE, Y_DIRECTION * scale);
493 }
494
495 void Model::FitModelPosition()
496 {
497   if(!mModelRoot)
498   {
499     return;
500   }
501   // Loaded model pivot is not the model center.
502   mModelRoot.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
503   mModelRoot.SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3::ONE - mModelPivot);
504 }
505
506 void Model::CollectRenderableActor(Actor actor)
507 {
508   uint32_t rendererCount = actor.GetRendererCount();
509   if(rendererCount)
510   {
511     mRenderableActors.push_back(actor);
512   }
513
514   uint32_t childrenCount = actor.GetChildCount();
515   for(uint32_t i = 0; i < childrenCount; ++i)
516   {
517     CollectRenderableActor(actor.GetChildAt(i));
518   }
519 }
520
521 void Model::UpdateImageBasedLightTexture()
522 {
523   Dali::Texture currentDiffuseTexture  = (mDiffuseTexture && mSpecularTexture) ? mDiffuseTexture : mSceneDiffuseTexture;
524   Dali::Texture currentSpecularTexture = (mDiffuseTexture && mSpecularTexture) ? mSpecularTexture : mSceneSpecularTexture;
525   float         currentIblScaleFactor  = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
526
527   if(!currentDiffuseTexture || !currentSpecularTexture)
528   {
529     currentDiffuseTexture  = mDefaultDiffuseTexture;
530     currentSpecularTexture = mDefaultSpecularTexture;
531     currentIblScaleFactor  = Dali::Scene3D::Loader::EnvironmentDefinition::GetDefaultIntensity();
532   }
533
534   for(auto&& actor : mRenderableActors)
535   {
536     Actor renderableActor = actor.GetHandle();
537     if(!renderableActor)
538     {
539       continue;
540     }
541
542     uint32_t rendererCount = renderableActor.GetRendererCount();
543     for(uint32_t i = 0; i < rendererCount; ++i)
544     {
545       Dali::Renderer renderer = renderableActor.GetRendererAt(i);
546       if(!renderer)
547       {
548         continue;
549       }
550       Dali::TextureSet textures = renderer.GetTextures();
551       if(!textures)
552       {
553         continue;
554       }
555       uint32_t textureCount = textures.GetTextureCount();
556       // EnvMap requires at least 2 texture, diffuse and specular
557       if(textureCount > 2u)
558       {
559         textures.SetTexture(textureCount - OFFSET_FOR_DIFFUSE_CUBE_TEXTURE, currentDiffuseTexture);
560         textures.SetTexture(textureCount - OFFSET_FOR_SPECULAR_CUBE_TEXTURE, currentSpecularTexture);
561       }
562     }
563     renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), currentIblScaleFactor);
564   }
565 }
566
567 void Model::UpdateImageBasedLightScaleFactor()
568 {
569   if((!mDiffuseTexture || !mSpecularTexture) &&
570      (!mSceneDiffuseTexture || !mSceneSpecularTexture))
571   {
572     return;
573   }
574
575   float currentIblScaleFactor = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
576   for(auto&& actor : mRenderableActors)
577   {
578     Actor renderableActor = actor.GetHandle();
579     if(renderableActor)
580     {
581       renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), currentIblScaleFactor);
582     }
583   }
584 }
585
586 void Model::NotifyImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float scaleFactor)
587 {
588   if(mSceneDiffuseTexture != diffuseTexture || mSceneSpecularTexture != specularTexture)
589   {
590     mSceneDiffuseTexture  = diffuseTexture;
591     mSceneSpecularTexture = specularTexture;
592     mSceneIblScaleFactor  = scaleFactor;
593     // If Model IBL is not set, use SceneView's IBL.
594     if(!mDiffuseTexture || !mSpecularTexture)
595     {
596       UpdateImageBasedLightTexture();
597     }
598   }
599 }
600
601 void Model::NotifyImageBasedLightScaleFactor(float scaleFactor)
602 {
603   mSceneIblScaleFactor = scaleFactor;
604   if(mSceneDiffuseTexture && mSceneSpecularTexture)
605   {
606     UpdateImageBasedLightScaleFactor();
607   }
608 }
609
610 void Model::OnModelLoadComplete()
611 {
612   if(!mModelLoadTask->HasSucceeded())
613   {
614     ResetResourceTasks();
615     return;
616   }
617
618   CreateModel();
619   mRenderableActors.clear();
620   CollectRenderableActor(mModelRoot);
621
622   auto* resources = &(mModelLoadTask->mResources);
623   auto* scene     = &(mModelLoadTask->mScene);
624   CreateAnimations(*scene);
625   if(!resources->mEnvironmentMaps.empty())
626   {
627     mDefaultDiffuseTexture  = resources->mEnvironmentMaps.front().second.mDiffuse;
628     mDefaultSpecularTexture = resources->mEnvironmentMaps.front().second.mSpecular;
629   }
630
631   UpdateImageBasedLightTexture();
632   UpdateImageBasedLightScaleFactor();
633
634   mModelRoot.SetProperty(Dali::Actor::Property::SENSITIVE, mModelChildrenSensitive);
635   mModelRoot.SetProperty(Dali::Actor::Property::KEYBOARD_FOCUSABLE, mModelChildrenFocusable);
636   mModelRoot.SetProperty(Dali::DevelActor::Property::KEYBOARD_FOCUSABLE_CHILDREN, mModelChildrenFocusable);
637
638   Self().Add(mModelRoot);
639   Self().SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3(mModelPivot.x, 1.0f - mModelPivot.y, mModelPivot.z));
640
641   mModelResourceReady = true;
642   NotifyResourceReady();
643   ResetResourceTask(mModelLoadTask);
644 }
645
646 void Model::OnIblDiffuseLoadComplete()
647 {
648   mDiffuseTexture = mIblDiffuseLoadTask->GetLoadedTexture();
649   ResetResourceTask(mIblDiffuseLoadTask);
650   mIblDiffuseResourceReady = true;
651   if(mIblDiffuseResourceReady && mIblSpecularResourceReady)
652   {
653     OnIblLoadComplete();
654   }
655 }
656
657 void Model::OnIblSpecularLoadComplete()
658 {
659   mSpecularTexture = mIblSpecularLoadTask->GetLoadedTexture();
660   ResetResourceTask(mIblSpecularLoadTask);
661   mIblSpecularResourceReady = true;
662   if(mIblDiffuseResourceReady && mIblSpecularResourceReady)
663   {
664     OnIblLoadComplete();
665   }
666 }
667
668 void Model::OnIblLoadComplete()
669 {
670   UpdateImageBasedLightTexture();
671   NotifyResourceReady();
672 }
673
674 void Model::ResetResourceTasks()
675 {
676   if(!Dali::Adaptor::IsAvailable())
677   {
678     return;
679   }
680   ResetResourceTask(mModelLoadTask);
681   ResetResourceTask(mIblDiffuseLoadTask);
682   ResetResourceTask(mIblSpecularLoadTask);
683 }
684
685 void Model::ResetResourceTask(IntrusivePtr<AsyncTask> asyncTask)
686 {
687   if(!asyncTask)
688   {
689     return;
690   }
691   Dali::AsyncTaskManager::Get().RemoveTask(asyncTask);
692   asyncTask.Reset();
693 }
694
695 void Model::NotifyResourceReady()
696 {
697   if(!IsResourceReady())
698   {
699     return;
700   }
701   Control::SetResourceReady(false);
702 }
703
704 void Model::CreateModel()
705 {
706   mModelRoot = Actor::New();
707   mModelRoot.SetProperty(Actor::Property::COLOR_MODE, ColorMode::USE_OWN_MULTIPLY_PARENT_COLOR);
708
709   BoundingVolume                                      AABB;
710   auto*                                               resources = &(mModelLoadTask->mResources);
711   auto*                                               scene     = &(mModelLoadTask->mScene);
712   Dali::Scene3D::Loader::Transforms                   xforms{Dali::Scene3D::Loader::MatrixStack{}, Dali::Scene3D::Loader::ViewProjection{}};
713   Dali::Scene3D::Loader::NodeDefinition::CreateParams nodeParams{*resources, xforms, {}, {}, {}};
714   uint32_t                                            rootCount = 0u;
715   for(auto iRoot : scene->GetRoots())
716   {
717     resources->GenerateResources(mModelLoadTask->mResourceRefCounts[rootCount]);
718
719     if(auto actor = scene->CreateNodes(iRoot, mModelLoadTask->mResourceChoices, nodeParams))
720     {
721       scene->ConfigureSkeletonJoints(iRoot, resources->mSkeletons, actor);
722       scene->ConfigureSkinningShaders(*resources, actor, std::move(nodeParams.mSkinnables));
723       ConfigureBlendShapeShaders(*resources, *scene, actor, std::move(nodeParams.mBlendshapeRequests));
724
725       scene->ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
726
727       mModelRoot.Add(actor);
728     }
729
730     AddModelTreeToAABB(AABB, *scene, mModelLoadTask->mResourceChoices, iRoot, nodeParams, Matrix::IDENTITY);
731     rootCount++;
732   }
733
734   mNaturalSize = AABB.CalculateSize();
735   mModelPivot  = AABB.CalculatePivot();
736   mModelRoot.SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
737   Vector3 controlSize = Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
738   if(Dali::EqualsZero(controlSize.x) || Dali::EqualsZero(controlSize.y))
739   {
740     Self().SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
741   }
742   FitModelPosition();
743   ScaleModel();
744 }
745
746 void Model::CreateAnimations(Dali::Scene3D::Loader::SceneDefinition& scene)
747 {
748   if(!mModelLoadTask->mAnimations.empty())
749   {
750     auto getActor = [&](const Scene3D::Loader::AnimatedProperty& property)
751     {
752       if(property.mNodeIndex == Scene3D::Loader::INVALID_INDEX)
753       {
754         return mModelRoot.FindChildByName(property.mNodeName);
755       }
756       auto* node = scene.GetNode(property.mNodeIndex);
757       if(node == nullptr)
758       {
759         return Dali::Actor();
760       }
761       return mModelRoot.FindChildById(node->mNodeId);
762     };
763
764     mAnimations.clear();
765     for(auto&& animation : mModelLoadTask->mAnimations)
766     {
767       Dali::Animation anim = animation.ReAnimate(getActor);
768       mAnimations.push_back({animation.mName, anim});
769     }
770   }
771 }
772
773 } // namespace Internal
774 } // namespace Scene3D
775 } // namespace Dali