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