Support model size change
[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/common/model-cache-manager.h>
35 #include <dali-scene3d/internal/controls/scene-view/scene-view-impl.h>
36 #include <dali-scene3d/internal/model-components/model-node-impl.h>
37 #include <dali-scene3d/public-api/controls/model/model.h>
38 #include <dali-scene3d/public-api/loader/animation-definition.h>
39 #include <dali-scene3d/public-api/loader/camera-parameters.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 /**
57  * Creates control through type registry
58  */
59 BaseHandle Create()
60 {
61   return Scene3D::Model::New(std::string());
62 }
63
64 // Setup properties, signals and actions using the type-registry.
65 DALI_TYPE_REGISTRATION_BEGIN(Scene3D::Model, Toolkit::Control, Create);
66 DALI_TYPE_REGISTRATION_END()
67
68 static constexpr Vector3 Y_DIRECTION(1.0f, -1.0f, 1.0f);
69 static constexpr float   SIZE_STEP_CONDITION = 0.1f;
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)
121   { errors.push_back(msg); };
122   if(!scene.ConfigureBlendshapeShaders(resources, root, std::move(requests), onError))
123   {
124     Dali::Scene3D::Loader::ExceptionFlinger flinger(ASSERT_LOCATION);
125     for(auto& msg : errors)
126     {
127       flinger << msg << '\n';
128     }
129   }
130 }
131
132 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)
133 {
134   static constexpr uint32_t BOX_POINT_COUNT             = 8;
135   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}};
136
137   Matrix                                       nodeMatrix;
138   const Dali::Scene3D::Loader::NodeDefinition* node        = scene.GetNode(iNode);
139   Matrix                                       localMatrix = node->GetLocalSpace();
140   Matrix::Multiply(nodeMatrix, localMatrix, parentMatrix);
141
142   Vector3 volume[2];
143   if(node->GetExtents(nodeParams.mResources, volume[0], volume[1]))
144   {
145     for(uint32_t i = 0; i < BOX_POINT_COUNT; ++i)
146     {
147       Vector4 position       = Vector4(volume[BBIndex[i][0]].x, volume[BBIndex[i][1]].y, volume[BBIndex[i][2]].z, 1.0f);
148       Vector4 objectPosition = nodeMatrix * position;
149       objectPosition /= objectPosition.w;
150
151       AABB.ConsiderNewPointInVolume(Vector3(objectPosition));
152     }
153   }
154
155   if(node->mCustomization)
156   {
157     if(!node->mChildren.empty())
158     {
159       auto                         choice = choices.Get(node->mCustomization->mTag);
160       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));
161
162       AddModelTreeToAABB(AABB, scene, choices, node->mChildren[i], nodeParams, nodeMatrix);
163     }
164   }
165   else
166   {
167     for(auto i : node->mChildren)
168     {
169       AddModelTreeToAABB(AABB, scene, choices, i, nodeParams, nodeMatrix);
170     }
171   }
172 }
173
174 } // anonymous namespace
175
176 Model::Model(const std::string& modelUrl, const std::string& resourceDirectoryUrl)
177 : Control(ControlBehaviour(DISABLE_SIZE_NEGOTIATION | DISABLE_STYLE_CHANGE_SIGNALS)),
178   mModelUrl(modelUrl),
179   mResourceDirectoryUrl(resourceDirectoryUrl),
180   mModelRoot(),
181   mNaturalSize(Vector3::ZERO),
182   mModelPivot(AnchorPoint::CENTER),
183   mSceneIblScaleFactor(1.0f),
184   mIblScaleFactor(1.0f),
185   mSceneSpecularMipmapLevels(1u),
186   mSpecularMipmapLevels(1u),
187   mModelChildrenSensitive(DEFAULT_MODEL_CHILDREN_SENSITIVE),
188   mModelChildrenFocusable(DEFAULT_MODEL_CHILDREN_FOCUSABLE),
189   mModelResourceReady(false),
190   mIblDiffuseResourceReady(true),
191   mIblSpecularResourceReady(true),
192   mIblDiffuseDirty(false),
193   mIblSpecularDirty(false)
194 {
195 }
196
197 Model::~Model()
198 {
199   if(ModelCacheManager::Get() && !mModelUrl.empty())
200   {
201     ModelCacheManager::Get().UnreferenceModelCache(mModelUrl);
202   }
203
204   ResetResourceTasks();
205 }
206
207 Dali::Scene3D::Model Model::New(const std::string& modelUrl, const std::string& resourceDirectoryUrl)
208 {
209   Model* impl = new Model(modelUrl, resourceDirectoryUrl);
210
211   Dali::Scene3D::Model handle = Dali::Scene3D::Model(*impl);
212
213   // Second-phase init of the implementation
214   // This can only be done after the CustomActor connection has been made...
215   impl->Initialize();
216
217   return handle;
218 }
219
220 const Scene3D::ModelNode Model::GetModelRoot() const
221 {
222   return mModelRoot;
223 }
224
225 void Model::AddModelNode(Scene3D::ModelNode modelNode)
226 {
227   if(!mModelRoot)
228   {
229     CreateModelRoot();
230   }
231
232   mModelRoot.Add(modelNode);
233   if(mModelUrl.empty())
234   {
235     mModelResourceReady = true;
236   }
237
238   if(mIblDiffuseResourceReady && mIblSpecularResourceReady)
239   {
240     UpdateImageBasedLightTexture();
241     UpdateImageBasedLightScaleFactor();
242   }
243
244   if(Self().GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE))
245   {
246     NotifyResourceReady();
247   }
248 }
249
250 void Model::RemoveModelNode(Scene3D::ModelNode modelNode)
251 {
252   if(mModelRoot)
253   {
254     mModelRoot.Remove(modelNode);
255   }
256 }
257
258 void Model::SetChildrenSensitive(bool enable)
259 {
260   if(mModelChildrenSensitive != enable)
261   {
262     mModelChildrenSensitive = enable;
263     if(mModelRoot)
264     {
265       mModelRoot.SetProperty(Dali::Actor::Property::SENSITIVE, mModelChildrenSensitive);
266     }
267   }
268 }
269
270 bool Model::GetChildrenSensitive() const
271 {
272   return mModelChildrenSensitive;
273 }
274
275 void Model::SetChildrenFocusable(bool enable)
276 {
277   if(mModelChildrenFocusable != enable)
278   {
279     mModelChildrenFocusable = enable;
280     if(mModelRoot)
281     {
282       mModelRoot.SetProperty(Dali::Actor::Property::KEYBOARD_FOCUSABLE, mModelChildrenFocusable);
283       mModelRoot.SetProperty(Dali::DevelActor::Property::KEYBOARD_FOCUSABLE_CHILDREN, mModelChildrenFocusable);
284     }
285   }
286 }
287
288 bool Model::GetChildrenFocusable() const
289 {
290   return mModelChildrenFocusable;
291 }
292
293 void Model::SetImageBasedLightSource(const std::string& diffuseUrl, const std::string& specularUrl, float scaleFactor)
294 {
295   bool needIblReset = false;
296   bool isOnScene    = Self().GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE);
297   if(mDiffuseIblUrl != diffuseUrl)
298   {
299     mDiffuseIblUrl = diffuseUrl;
300     if(mDiffuseIblUrl.empty())
301     {
302       needIblReset = true;
303     }
304     else
305     {
306       mIblDiffuseDirty         = true;
307       mIblDiffuseResourceReady = false;
308     }
309   }
310
311   if(mSpecularIblUrl != specularUrl)
312   {
313     mSpecularIblUrl = specularUrl;
314     if(mSpecularIblUrl.empty())
315     {
316       needIblReset = true;
317     }
318     else
319     {
320       mIblSpecularDirty         = true;
321       mIblSpecularResourceReady = false;
322     }
323   }
324
325   // If one or both of diffuse url and specular url are empty,
326   // we don't need to request to load texture.
327   if(needIblReset)
328   {
329     ResetResourceTask(mIblDiffuseLoadTask);
330     ResetResourceTask(mIblSpecularLoadTask);
331
332     mIblDiffuseDirty          = false;
333     mIblSpecularDirty         = false;
334     mIblDiffuseResourceReady  = true;
335     mIblSpecularResourceReady = true;
336
337     mDiffuseTexture.Reset();
338     mSpecularTexture.Reset();
339     UpdateImageBasedLightTexture();
340   }
341   else
342   {
343     if(isOnScene && mIblDiffuseDirty)
344     {
345       ResetResourceTask(mIblDiffuseLoadTask);
346       mIblDiffuseLoadTask = new EnvironmentMapLoadTask(mDiffuseIblUrl, Scene3D::EnvironmentMapType::CUBEMAP, MakeCallback(this, &Model::OnIblDiffuseLoadComplete));
347       Dali::AsyncTaskManager::Get().AddTask(mIblDiffuseLoadTask);
348       mIblDiffuseDirty = false;
349     }
350
351     if(isOnScene && mIblSpecularDirty)
352     {
353       ResetResourceTask(mIblSpecularLoadTask);
354       mIblSpecularLoadTask = new EnvironmentMapLoadTask(mSpecularIblUrl, Scene3D::EnvironmentMapType::CUBEMAP, MakeCallback(this, &Model::OnIblSpecularLoadComplete));
355       Dali::AsyncTaskManager::Get().AddTask(mIblSpecularLoadTask);
356       mIblSpecularDirty = false;
357     }
358   }
359
360   if(!Dali::Equals(mIblScaleFactor, scaleFactor))
361   {
362     mIblScaleFactor = scaleFactor;
363     UpdateImageBasedLightScaleFactor();
364   }
365
366   // If diffuse and specular textures are already loaded, emits resource ready signal here.
367   NotifyResourceReady();
368 }
369
370 void Model::SetImageBasedLightScaleFactor(float scaleFactor)
371 {
372   mIblScaleFactor = scaleFactor;
373   if(mDiffuseTexture && mSpecularTexture)
374   {
375     UpdateImageBasedLightScaleFactor();
376   }
377 }
378
379 float Model::GetImageBasedLightScaleFactor() const
380 {
381   return mIblScaleFactor;
382 }
383
384 uint32_t Model::GetAnimationCount() const
385 {
386   return mAnimations.size();
387 }
388
389 Dali::Animation Model::GetAnimation(uint32_t index) const
390 {
391   Dali::Animation animation;
392   if(mAnimations.size() > index)
393   {
394     animation = mAnimations[index].second;
395   }
396   return animation;
397 }
398
399 Dali::Animation Model::GetAnimation(const std::string& name) const
400 {
401   Dali::Animation animation;
402   if(!name.empty())
403   {
404     for(auto&& animationData : mAnimations)
405     {
406       if(animationData.first == name)
407       {
408         animation = animationData.second;
409         break;
410       }
411     }
412   }
413   return animation;
414 }
415
416 uint32_t Model::GetCameraCount() const
417 {
418   return mCameraParameters.size();
419 }
420
421 Dali::CameraActor Model::GenerateCamera(uint32_t index) const
422 {
423   Dali::CameraActor camera;
424   if(mCameraParameters.size() > index)
425   {
426     camera = Dali::CameraActor::New3DCamera();
427     if(!mCameraParameters[index].ConfigureCamera(camera, false))
428     {
429       DALI_LOG_ERROR("Fail to generate %u's camera actor : Some property was not defined. Please check model file.\n", index);
430       camera.Reset();
431       return camera;
432     }
433
434     ApplyCameraTransform(camera);
435   }
436   return camera;
437 }
438
439 bool Model::ApplyCamera(uint32_t index, Dali::CameraActor camera) const
440 {
441   if(camera && mCameraParameters.size() > index)
442   {
443     if(!mCameraParameters[index].ConfigureCamera(camera, false))
444     {
445       DALI_LOG_ERROR("Fail to apply %u's camera actor : Some property was not defined. Please check model file.\n", index);
446       return false;
447     }
448
449     ApplyCameraTransform(camera);
450     return true;
451   }
452   return false;
453 }
454
455 Scene3D::ModelNode Model::FindChildModelNodeByName(std::string_view nodeName)
456 {
457   Actor childActor = Self().FindChildByName(nodeName);
458   return Scene3D::ModelNode::DownCast(childActor);
459 }
460
461 ///////////////////////////////////////////////////////////
462 //
463 // Private methods
464 //
465
466 void Model::OnInitialize()
467 {
468   // Make ParentOrigin as Center.
469   Self().SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
470 }
471
472 void Model::OnSceneConnection(int depth)
473 {
474   if(!mModelLoadTask && !mModelResourceReady && !mModelUrl.empty())
475   {
476     // Request model load only if we setup url.
477     if(ModelCacheManager::Get())
478     {
479       ModelCacheManager::Get().ReferenceModelCache(mModelUrl);
480     }
481     mModelLoadTask = new ModelLoadTask(mModelUrl, mResourceDirectoryUrl, MakeCallback(this, &Model::OnModelLoadComplete));
482     Dali::AsyncTaskManager::Get().AddTask(mModelLoadTask);
483   }
484
485   // If diffuse and specular url is not valid, IBL does not need to be loaded.
486   if(!mDiffuseIblUrl.empty() && !mSpecularIblUrl.empty())
487   {
488     SetImageBasedLightSource(mDiffuseIblUrl, mSpecularIblUrl, mIblScaleFactor);
489   }
490
491   Actor parent = Self().GetParent();
492   while(parent)
493   {
494     Scene3D::SceneView sceneView = Scene3D::SceneView::DownCast(parent);
495     if(sceneView)
496     {
497       GetImpl(sceneView).RegisterSceneItem(this);
498       mParentSceneView = sceneView;
499       break;
500     }
501     parent = parent.GetParent();
502   }
503
504   NotifyResourceReady();
505
506   mSizeNotification = Self().AddPropertyNotification(Actor::Property::SIZE, StepCondition(SIZE_STEP_CONDITION));
507   mSizeNotification.NotifySignal().Connect(this, &Model::OnSizeNotification);
508   Control::OnSceneConnection(depth);
509 }
510
511 void Model::OnSceneDisconnection()
512 {
513   Scene3D::SceneView sceneView = mParentSceneView.GetHandle();
514   if(sceneView)
515   {
516     GetImpl(sceneView).UnregisterSceneItem(this);
517     mParentSceneView.Reset();
518   }
519
520   mSizeNotification.NotifySignal().Disconnect(this, &Model::OnSizeNotification);
521   Self().RemovePropertyNotification(mSizeNotification);
522   mSizeNotification.Reset();
523
524   Control::OnSceneDisconnection();
525 }
526
527 void Model::OnSizeSet(const Vector3& size)
528 {
529   ScaleModel(false);
530 }
531
532 Vector3 Model::GetNaturalSize()
533 {
534   if(!mModelRoot)
535   {
536     DALI_LOG_ERROR("Model is still not loaded.\n");
537     return Vector3::ZERO;
538   }
539
540   return mNaturalSize;
541 }
542
543 float Model::GetHeightForWidth(float width)
544 {
545   Extents padding;
546   padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
547   return Control::GetHeightForWidth(width) + padding.top + padding.bottom;
548 }
549
550 float Model::GetWidthForHeight(float height)
551 {
552   Extents padding;
553   padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
554   return Control::GetWidthForHeight(height) + padding.start + padding.end;
555 }
556
557 void Model::OnRelayout(const Vector2& size, RelayoutContainer& container)
558 {
559   Control::OnRelayout(size, container);
560   ScaleModel(false);
561 }
562
563 bool Model::IsResourceReady() const
564 {
565   return mModelResourceReady && mIblDiffuseResourceReady && mIblSpecularResourceReady;
566 }
567
568 void Model::CreateModelRoot()
569 {
570   mModelRoot = Scene3D::ModelNode::New();
571   mModelRoot.SetProperty(Actor::Property::COLOR_MODE, ColorMode::USE_OWN_MULTIPLY_PARENT_COLOR);
572   mModelRoot.SetProperty(Dali::Actor::Property::SCALE, Y_DIRECTION);
573   mModelRoot.SetProperty(Dali::Actor::Property::SENSITIVE, mModelChildrenSensitive);
574   mModelRoot.SetProperty(Dali::Actor::Property::KEYBOARD_FOCUSABLE, mModelChildrenFocusable);
575   mModelRoot.SetProperty(Dali::DevelActor::Property::KEYBOARD_FOCUSABLE_CHILDREN, mModelChildrenFocusable);
576   Self().Add(mModelRoot);
577 }
578
579 void Model::ScaleModel(bool useCurrentSize)
580 {
581   if(!mModelRoot)
582   {
583     return;
584   }
585
586   float   scale = 1.0f;
587   Vector3 size  = (useCurrentSize) ? Self().GetCurrentProperty<Vector3>(Dali::Actor::Property::SIZE) : Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
588   if(size.x > 0.0f && size.y > 0.0f)
589   {
590     scale = MAXFLOAT;
591     scale = std::min(size.x / mNaturalSize.x, scale);
592     scale = std::min(size.y / mNaturalSize.y, scale);
593   }
594   // Models in glTF and dli are defined as right hand coordinate system.
595   // DALi uses left hand coordinate system. Scaling negative is for change winding order.
596   mModelRoot.SetProperty(Dali::Actor::Property::SCALE, Y_DIRECTION * scale);
597 }
598
599 void Model::FitModelPosition()
600 {
601   if(!mModelRoot)
602   {
603     return;
604   }
605   // Loaded model pivot is not the model center.
606   mModelRoot.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
607   mModelRoot.SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3::ONE - mModelPivot);
608 }
609
610 void Model::UpdateImageBasedLightTextureRecursively(Scene3D::ModelNode node, Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels)
611 {
612   if(!node)
613   {
614     return;
615   }
616
617   GetImplementation(node).SetImageBasedLightTexture(diffuseTexture, specularTexture, iblScaleFactor, specularMipmapLevels);
618   uint32_t childrenCount = node.GetChildCount();
619   for(uint32_t i = 0; i < childrenCount; ++i)
620   {
621     Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i));
622     if(!childNode)
623     {
624       continue;
625     }
626     UpdateImageBasedLightTextureRecursively(childNode, diffuseTexture, specularTexture, iblScaleFactor, specularMipmapLevels);
627   }
628 }
629
630 void Model::UpdateImageBasedLightScaleFactorRecursively(Scene3D::ModelNode node, float iblScaleFactor)
631 {
632   if(!node)
633   {
634     return;
635   }
636
637   node.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), iblScaleFactor);
638   GetImplementation(node).SetImageBasedLightScaleFactor(iblScaleFactor);
639
640   uint32_t childrenCount = node.GetChildCount();
641   for(uint32_t i = 0; i < childrenCount; ++i)
642   {
643     Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i));
644     if(!childNode)
645     {
646       continue;
647     }
648     UpdateImageBasedLightScaleFactorRecursively(childNode, iblScaleFactor);
649   }
650 }
651
652 void Model::UpdateImageBasedLightTexture()
653 {
654   Dali::Texture currentDiffuseTexture          = (mDiffuseTexture && mSpecularTexture) ? mDiffuseTexture : mSceneDiffuseTexture;
655   Dali::Texture currentSpecularTexture         = (mDiffuseTexture && mSpecularTexture) ? mSpecularTexture : mSceneSpecularTexture;
656   float         currentIblScaleFactor          = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
657   uint32_t      currentIblSpecularMipmapLevels = (mDiffuseTexture && mSpecularTexture) ? mSpecularMipmapLevels : mSceneSpecularMipmapLevels;
658
659   if(!currentDiffuseTexture || !currentSpecularTexture)
660   {
661     currentDiffuseTexture          = mDefaultDiffuseTexture;
662     currentSpecularTexture         = mDefaultSpecularTexture;
663     currentIblScaleFactor          = Dali::Scene3D::Loader::EnvironmentDefinition::GetDefaultIntensity();
664     currentIblSpecularMipmapLevels = 1u;
665   }
666
667   UpdateImageBasedLightTextureRecursively(mModelRoot, currentDiffuseTexture, currentSpecularTexture, currentIblScaleFactor, currentIblSpecularMipmapLevels);
668 }
669
670 void Model::UpdateImageBasedLightScaleFactor()
671 {
672   if((!mDiffuseTexture || !mSpecularTexture) &&
673      (!mSceneDiffuseTexture || !mSceneSpecularTexture))
674   {
675     return;
676   }
677
678   float currentIblScaleFactor = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
679   UpdateImageBasedLightScaleFactorRecursively(mModelRoot, currentIblScaleFactor);
680 }
681
682 void Model::ApplyCameraTransform(Dali::CameraActor camera) const
683 {
684   Vector3    selfPosition    = Self().GetProperty<Vector3>(Actor::Property::POSITION);
685   Quaternion selfOrientation = Self().GetProperty<Quaternion>(Actor::Property::ORIENTATION);
686   Vector3    selfScale       = Self().GetProperty<Vector3>(Actor::Property::SCALE);
687
688   Vector3    cameraPosition    = camera.GetProperty<Vector3>(Actor::Property::POSITION);
689   Quaternion cameraOrientation = camera.GetProperty<Quaternion>(Actor::Property::ORIENTATION);
690   Vector3    cameraScale       = camera.GetProperty<Vector3>(Actor::Property::SCALE);
691
692   // Models in glTF and dli are defined as right hand coordinate system.
693   // DALi uses left hand coordinate system. Scaling negative is for change winding order.
694   if(!Dali::Equals(Y_DIRECTION.Dot(Vector3::YAXIS), 1.0f))
695   {
696     // Reflect by XZ plane
697     cameraPosition.y = -cameraPosition.y;
698     Quaternion yDirectionQuaternion;
699     yDirectionQuaternion.mVector = Vector3::YAXIS;
700     // Reflect orientation
701     cameraOrientation = yDirectionQuaternion * cameraOrientation * yDirectionQuaternion;
702   }
703
704   Vector3    resultPosition;
705   Quaternion resultOrientation;
706   Vector3    resultScale;
707
708   Matrix selfMatrix(false);
709   Matrix cameraMatrix(false);
710   Matrix resultMatrix(false);
711   selfMatrix.SetTransformComponents(selfScale, selfOrientation, selfPosition);
712   cameraMatrix.SetTransformComponents(cameraScale, cameraOrientation, cameraPosition);
713   Matrix::Multiply(resultMatrix, cameraMatrix, selfMatrix);
714   resultMatrix.GetTransformComponents(resultPosition, resultOrientation, resultScale);
715
716   camera.SetProperty(Actor::Property::POSITION, resultPosition);
717   camera.SetProperty(Actor::Property::ORIENTATION, resultOrientation);
718   camera.SetProperty(Actor::Property::SCALE, resultScale);
719 }
720
721 void Model::NotifyImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float scaleFactor, uint32_t specularMipmapLevels)
722 {
723   if(mSceneDiffuseTexture != diffuseTexture || mSceneSpecularTexture != specularTexture)
724   {
725     mSceneDiffuseTexture       = diffuseTexture;
726     mSceneSpecularTexture      = specularTexture;
727     mSceneIblScaleFactor       = scaleFactor;
728     mSceneSpecularMipmapLevels = specularMipmapLevels;
729     // If Model IBL is not set, use SceneView's IBL.
730     if(!mDiffuseTexture || !mSpecularTexture)
731     {
732       UpdateImageBasedLightTexture();
733     }
734   }
735 }
736
737 void Model::NotifyImageBasedLightScaleFactor(float scaleFactor)
738 {
739   mSceneIblScaleFactor = scaleFactor;
740   if(mSceneDiffuseTexture && mSceneSpecularTexture)
741   {
742     UpdateImageBasedLightScaleFactor();
743   }
744 }
745
746 void Model::OnModelLoadComplete()
747 {
748   if(!mModelLoadTask->HasSucceeded())
749   {
750     ResetResourceTasks();
751
752     if(ModelCacheManager::Get() && !mModelUrl.empty())
753     {
754       ModelCacheManager::Get().UnreferenceModelCache(mModelUrl);
755     }
756
757     return;
758   }
759
760   if(!mModelRoot)
761   {
762     CreateModelRoot();
763   }
764   CreateModel();
765
766   auto& resources = mModelLoadTask->GetResources();
767   auto& scene     = mModelLoadTask->GetScene();
768   CreateAnimations(scene);
769   ResetCameraParameters();
770   if(!resources.mEnvironmentMaps.empty())
771   {
772     mDefaultDiffuseTexture  = resources.mEnvironmentMaps.front().second.mDiffuse;
773     mDefaultSpecularTexture = resources.mEnvironmentMaps.front().second.mSpecular;
774   }
775
776   UpdateImageBasedLightTexture();
777   UpdateImageBasedLightScaleFactor();
778   Self().SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3(mModelPivot.x, 1.0f - mModelPivot.y, mModelPivot.z));
779
780   mModelResourceReady = true;
781   NotifyResourceReady();
782   ResetResourceTask(mModelLoadTask);
783 }
784
785 void Model::OnIblDiffuseLoadComplete()
786 {
787   mDiffuseTexture = mIblDiffuseLoadTask->GetLoadedTexture();
788   ResetResourceTask(mIblDiffuseLoadTask);
789   mIblDiffuseResourceReady = true;
790   if(mIblDiffuseResourceReady && mIblSpecularResourceReady)
791   {
792     OnIblLoadComplete();
793   }
794 }
795
796 void Model::OnIblSpecularLoadComplete()
797 {
798   mSpecularTexture      = mIblSpecularLoadTask->GetLoadedTexture();
799   mSpecularMipmapLevels = mIblSpecularLoadTask->GetMipmapLevels();
800   ResetResourceTask(mIblSpecularLoadTask);
801   mIblSpecularResourceReady = true;
802   if(mIblDiffuseResourceReady && mIblSpecularResourceReady)
803   {
804     OnIblLoadComplete();
805   }
806 }
807
808 void Model::OnIblLoadComplete()
809 {
810   UpdateImageBasedLightTexture();
811   NotifyResourceReady();
812 }
813
814 void Model::OnSizeNotification(Dali::PropertyNotification& source)
815 {
816   ScaleModel(true);
817 }
818
819 void Model::ResetResourceTasks()
820 {
821   if(!Dali::Adaptor::IsAvailable())
822   {
823     return;
824   }
825   ResetResourceTask(mModelLoadTask);
826   ResetResourceTask(mIblDiffuseLoadTask);
827   ResetResourceTask(mIblSpecularLoadTask);
828 }
829
830 void Model::ResetResourceTask(IntrusivePtr<AsyncTask> asyncTask)
831 {
832   if(!asyncTask)
833   {
834     return;
835   }
836   Dali::AsyncTaskManager::Get().RemoveTask(asyncTask);
837   asyncTask.Reset();
838 }
839
840 void Model::NotifyResourceReady()
841 {
842   if(!IsResourceReady())
843   {
844     return;
845   }
846   Control::SetResourceReady(false);
847 }
848
849 void Model::CreateModel()
850 {
851   BoundingVolume                                      AABB;
852   auto&                                               resources       = mModelLoadTask->GetResources();
853   auto&                                               scene           = mModelLoadTask->GetScene();
854   auto&                                               resourceChoices = mModelLoadTask->GetResourceChoices();
855   Dali::Scene3D::Loader::Transforms                   xforms{Dali::Scene3D::Loader::MatrixStack{}, Dali::Scene3D::Loader::ViewProjection{}};
856   Dali::Scene3D::Loader::NodeDefinition::CreateParams nodeParams{resources, xforms, {}, {}, {}};
857
858   // Generate Dali handles from resource bundle. Note that we generate all scene's resouce immediatly.
859   resources.GenerateResources();
860   for(auto iRoot : scene.GetRoots())
861   {
862     if(auto actor = scene.CreateNodes(iRoot, resourceChoices, nodeParams))
863     {
864       scene.ConfigureSkinningShaders(resources, actor, std::move(nodeParams.mSkinnables));
865       ConfigureBlendShapeShaders(resources, scene, actor, std::move(nodeParams.mBlendshapeRequests));
866
867       scene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
868
869       mModelRoot.Add(actor);
870     }
871
872     AddModelTreeToAABB(AABB, scene, resourceChoices, iRoot, nodeParams, Matrix::IDENTITY);
873   }
874
875   mNaturalSize = AABB.CalculateSize();
876   mModelPivot  = AABB.CalculatePivot();
877   mModelRoot.SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
878   Vector3 controlSize = Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
879   if(Dali::EqualsZero(controlSize.x) || Dali::EqualsZero(controlSize.y))
880   {
881     Self().SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
882   }
883   FitModelPosition();
884   ScaleModel(false);
885 }
886
887 void Model::CreateAnimations(Dali::Scene3D::Loader::SceneDefinition& scene)
888 {
889   mAnimations.clear();
890   if(!mModelLoadTask->GetAnimations().empty())
891   {
892     auto getActor = [&](const Scene3D::Loader::AnimatedProperty& property)
893     {
894       if(property.mNodeIndex == Scene3D::Loader::INVALID_INDEX)
895       {
896         return mModelRoot.FindChildByName(property.mNodeName);
897       }
898       auto* node = scene.GetNode(property.mNodeIndex);
899       if(node == nullptr)
900       {
901         return Dali::Actor();
902       }
903       return mModelRoot.FindChildById(node->mNodeId);
904     };
905
906     for(auto&& animation : mModelLoadTask->GetAnimations())
907     {
908       Dali::Animation anim = animation.ReAnimate(getActor);
909       mAnimations.push_back({animation.GetName(), anim});
910     }
911   }
912 }
913
914 void Model::ResetCameraParameters()
915 {
916   mCameraParameters.clear();
917   if(!mModelLoadTask->GetCameras().empty())
918   {
919     // Copy camera parameters.
920     std::copy(mModelLoadTask->GetCameras().begin(), mModelLoadTask->GetCameras().end(), std::back_inserter(mCameraParameters));
921   }
922 }
923
924 } // namespace Internal
925 } // namespace Scene3D
926 } // namespace Dali