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