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