bed5f3462ad7ca3db4ce08c022b4b05ddf68a39d
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / internal / controls / model / model-impl.cpp
1 /*
2  * Copyright (c) 2022 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/debug.h>
27 #include <dali/public-api/math/math-utils.h>
28 #include <dali/public-api/object/type-registry-helper.h>
29 #include <dali/public-api/object/type-registry.h>
30 #include <filesystem>
31
32 // INTERNAL INCLUDES
33 #include <dali-scene3d/internal/controls/scene-view/scene-view-impl.h>
34 #include <dali-scene3d/public-api/controls/model/model.h>
35 #include <dali-scene3d/public-api/loader/animation-definition.h>
36 #include <dali-scene3d/public-api/loader/camera-parameters.h>
37 #include <dali-scene3d/public-api/loader/cube-map-loader.h>
38 #include <dali-scene3d/public-api/loader/dli-loader.h>
39 #include <dali-scene3d/public-api/loader/gltf2-loader.h>
40 #include <dali-scene3d/public-api/loader/light-parameters.h>
41 #include <dali-scene3d/public-api/loader/load-result.h>
42 #include <dali-scene3d/public-api/loader/node-definition.h>
43 #include <dali-scene3d/public-api/loader/scene-definition.h>
44 #include <dali-scene3d/public-api/loader/shader-definition-factory.h>
45
46 using namespace Dali;
47
48 namespace Dali
49 {
50 namespace Scene3D
51 {
52 namespace Internal
53 {
54 namespace
55 {
56 BaseHandle Create()
57 {
58   return Scene3D::Model::New(std::string());
59 }
60
61 // Setup properties, signals and actions using the type-registry.
62 DALI_TYPE_REGISTRATION_BEGIN(Scene3D::Model, Toolkit::Control, Create);
63 DALI_TYPE_REGISTRATION_END()
64
65 static constexpr uint32_t OFFSET_FOR_DIFFUSE_CUBE_TEXTURE  = 2u;
66 static constexpr uint32_t OFFSET_FOR_SPECULAR_CUBE_TEXTURE = 1u;
67
68 static constexpr Vector3 Y_DIRECTION(1.0f, -1.0f, 1.0f);
69
70 static constexpr bool DEFAULT_MODEL_CHILDREN_SENSITIVE = false;
71 static constexpr bool DEFAULT_MODEL_CHILDREN_FOCUSABLE = false;
72
73 static constexpr std::string_view KTX_EXTENSION  = ".ktx";
74 static constexpr std::string_view OBJ_EXTENSION  = ".obj";
75 static constexpr std::string_view GLTF_EXTENSION = ".gltf";
76 static constexpr std::string_view DLI_EXTENSION  = ".dli";
77
78 struct BoundingVolume
79 {
80   void Init()
81   {
82     pointMin = Vector3(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
83     pointMax = Vector3(std::numeric_limits<float>::min(), std::numeric_limits<float>::min(), std::numeric_limits<float>::min());
84   }
85
86   void ConsiderNewPointInVolume(const Vector3& position)
87   {
88     pointMin.x = std::min(position.x, pointMin.x);
89     pointMin.y = std::min(position.y, pointMin.y);
90     pointMin.z = std::min(position.z, pointMin.z);
91
92     pointMax.x = std::max(position.x, pointMax.x);
93     pointMax.y = std::max(position.y, pointMax.y);
94     pointMax.z = std::max(position.z, pointMax.z);
95   }
96
97   Vector3 CalculateSize()
98   {
99     return pointMax - pointMin;
100   }
101
102   Vector3 CalculatePivot()
103   {
104     Vector3 pivot = pointMin / (pointMin - pointMax);
105     for(uint32_t i = 0; i < 3; ++i)
106     {
107       // To avoid divid by zero
108       if(Dali::Equals(pointMin[i], pointMax[i]))
109       {
110         pivot[i] = 0.5f;
111       }
112     }
113     return pivot;
114   }
115
116   Vector3 pointMin;
117   Vector3 pointMax;
118 };
119
120 void ConfigureBlendShapeShaders(
121   Dali::Scene3D::Loader::ResourceBundle& resources, const Dali::Scene3D::Loader::SceneDefinition& scene, Actor root, std::vector<Dali::Scene3D::Loader::BlendshapeShaderConfigurationRequest>&& requests)
122 {
123   std::vector<std::string> errors;
124   auto                     onError = [&errors](const std::string& msg) { errors.push_back(msg); };
125   if(!scene.ConfigureBlendshapeShaders(resources, root, std::move(requests), onError))
126   {
127     Dali::Scene3D::Loader::ExceptionFlinger flinger(ASSERT_LOCATION);
128     for(auto& msg : errors)
129     {
130       flinger << msg << '\n';
131     }
132   }
133 }
134
135 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)
136 {
137   static constexpr uint32_t BOX_POINT_COUNT             = 8;
138   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}};
139
140   Matrix                                       nodeMatrix;
141   const Dali::Scene3D::Loader::NodeDefinition* node        = scene.GetNode(iNode);
142   Matrix                                       localMatrix = node->GetLocalSpace();
143   Matrix::Multiply(nodeMatrix, localMatrix, parentMatrix);
144
145   Vector3 volume[2];
146   if(node->GetExtents(nodeParams.mResources, volume[0], volume[1]))
147   {
148     for(uint32_t i = 0; i < BOX_POINT_COUNT; ++i)
149     {
150       Vector4 position       = Vector4(volume[BBIndex[i][0]].x, volume[BBIndex[i][1]].y, volume[BBIndex[i][2]].z, 1.0f);
151       Vector4 objectPosition = nodeMatrix * position;
152       objectPosition /= objectPosition.w;
153
154       AABB.ConsiderNewPointInVolume(Vector3(objectPosition));
155     }
156   }
157
158   if(node->mCustomization)
159   {
160     if(!node->mChildren.empty())
161     {
162       auto                         choice = choices.Get(node->mCustomization->mTag);
163       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));
164
165       AddModelTreeToAABB(AABB, scene, choices, node->mChildren[i], nodeParams, nodeMatrix);
166     }
167   }
168   else
169   {
170     for(auto i : node->mChildren)
171     {
172       AddModelTreeToAABB(AABB, scene, choices, i, nodeParams, nodeMatrix);
173     }
174   }
175 }
176
177 } // anonymous namespace
178
179 Model::Model(const std::string& modelUrl, const std::string& resourceDirectoryUrl)
180 : Control(ControlBehaviour(DISABLE_SIZE_NEGOTIATION | DISABLE_STYLE_CHANGE_SIGNALS)),
181   mModelUrl(modelUrl),
182   mResourceDirectoryUrl(resourceDirectoryUrl),
183   mModelRoot(),
184   mNaturalSize(Vector3::ZERO),
185   mModelPivot(AnchorPoint::CENTER),
186   mSceneIblScaleFactor(1.0f),
187   mIblScaleFactor(1.0f),
188   mModelChildrenSensitive(DEFAULT_MODEL_CHILDREN_SENSITIVE),
189   mModelChildrenFocusable(DEFAULT_MODEL_CHILDREN_FOCUSABLE),
190   mModelResourceReady(false),
191   mIBLResourceReady(true)
192 {
193 }
194
195 Model::~Model()
196 {
197 }
198
199 Dali::Scene3D::Model Model::New(const std::string& modelUrl, const std::string& resourceDirectoryUrl)
200 {
201   Model* impl = new Model(modelUrl, resourceDirectoryUrl);
202
203   Dali::Scene3D::Model handle = Dali::Scene3D::Model(*impl);
204
205   // Second-phase init of the implementation
206   // This can only be done after the CustomActor connection has been made...
207   impl->Initialize();
208
209   return handle;
210 }
211
212 const Actor Model::GetModelRoot() const
213 {
214   return mModelRoot;
215 }
216
217 void Model::SetChildrenSensitive(bool enable)
218 {
219   if(mModelChildrenSensitive != enable)
220   {
221     mModelChildrenSensitive = enable;
222     if(mModelRoot)
223     {
224       mModelRoot.SetProperty(Dali::Actor::Property::SENSITIVE, mModelChildrenSensitive);
225     }
226   }
227 }
228
229 bool Model::GetChildrenSensitive() const
230 {
231   return mModelChildrenSensitive;
232 }
233
234 void Model::SetChildrenFocusable(bool enable)
235 {
236   if(mModelChildrenFocusable != enable)
237   {
238     mModelChildrenFocusable = enable;
239     if(mModelRoot)
240     {
241       mModelRoot.SetProperty(Dali::Actor::Property::KEYBOARD_FOCUSABLE, mModelChildrenFocusable);
242       mModelRoot.SetProperty(Dali::DevelActor::Property::KEYBOARD_FOCUSABLE_CHILDREN, mModelChildrenFocusable);
243     }
244   }
245 }
246
247 bool Model::GetChildrenFocusable() const
248 {
249   return mModelChildrenFocusable;
250 }
251
252 void Model::SetImageBasedLightSource(const std::string& diffuseUrl, const std::string& specularUrl, float scaleFactor)
253 {
254   mIBLResourceReady       = false;
255   Texture diffuseTexture  = (!diffuseUrl.empty()) ? Dali::Scene3D::Loader::LoadCubeMap(diffuseUrl) : Texture();
256   Texture specularTexture = (!specularUrl.empty()) ? Dali::Scene3D::Loader::LoadCubeMap(specularUrl) : Texture();
257   SetImageBasedLightTexture(diffuseTexture, specularTexture, scaleFactor);
258   mIBLResourceReady = true;
259
260   // If Model resource is already ready, then set resource ready.
261   // If Model resource is still not ready, wait for model resource ready.
262   if(IsResourceReady())
263   {
264     SetResourceReady(false);
265   }
266 }
267
268 void Model::SetImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float scaleFactor)
269 {
270   // If input texture is wrong, Model is rendered with SceneView's IBL.
271   if(mDiffuseTexture != diffuseTexture || mSpecularTexture != specularTexture)
272   {
273     mDiffuseTexture  = diffuseTexture;
274     mSpecularTexture = specularTexture;
275     mIblScaleFactor  = scaleFactor;
276     UpdateImageBasedLightTexture();
277   }
278 }
279
280 void Model::SetImageBasedLightScaleFactor(float scaleFactor)
281 {
282   mIblScaleFactor = scaleFactor;
283   if(mDiffuseTexture && mSpecularTexture)
284   {
285     UpdateImageBasedLightScaleFactor();
286   }
287 }
288
289 float Model::GetImageBasedLightScaleFactor() const
290 {
291   return mIblScaleFactor;
292 }
293
294 uint32_t Model::GetAnimationCount() const
295 {
296   return mAnimations.size();
297 }
298
299 Dali::Animation Model::GetAnimation(uint32_t index) const
300 {
301   Dali::Animation animation;
302   if(mAnimations.size() > index)
303   {
304     animation = mAnimations[index].second;
305   }
306   return animation;
307 }
308
309 Dali::Animation Model::GetAnimation(const std::string& name) const
310 {
311   Dali::Animation animation;
312   if(!name.empty())
313   {
314     for(auto&& animationData : mAnimations)
315     {
316       if(animationData.first == name)
317       {
318         animation = animationData.second;
319         break;
320       }
321     }
322   }
323   return animation;
324 }
325
326 ///////////////////////////////////////////////////////////
327 //
328 // Private methods
329 //
330
331 void Model::OnInitialize()
332 {
333   // Make ParentOrigin as Center.
334   Self().SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
335 }
336
337 void Model::OnSceneConnection(int depth)
338 {
339   if(!mModelRoot)
340   {
341     LoadModel();
342   }
343
344   Actor parent = Self().GetParent();
345   while(parent)
346   {
347     Scene3D::SceneView sceneView = Scene3D::SceneView::DownCast(parent);
348     if(sceneView)
349     {
350       GetImpl(sceneView).RegisterSceneItem(this);
351       mParentSceneView = sceneView;
352       break;
353     }
354     parent = parent.GetParent();
355   }
356
357   Control::OnSceneConnection(depth);
358 }
359
360 void Model::OnSceneDisconnection()
361 {
362   Scene3D::SceneView sceneView = mParentSceneView.GetHandle();
363   if(sceneView)
364   {
365     GetImpl(sceneView).UnregisterSceneItem(this);
366     mParentSceneView.Reset();
367   }
368   Control::OnSceneDisconnection();
369 }
370
371 Vector3 Model::GetNaturalSize()
372 {
373   if(!mModelRoot)
374   {
375     LoadModel();
376   }
377
378   return mNaturalSize;
379 }
380
381 float Model::GetHeightForWidth(float width)
382 {
383   Extents padding;
384   padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
385   return Control::GetHeightForWidth(width) + padding.top + padding.bottom;
386 }
387
388 float Model::GetWidthForHeight(float height)
389 {
390   Extents padding;
391   padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
392   return Control::GetWidthForHeight(height) + padding.start + padding.end;
393 }
394
395 void Model::OnRelayout(const Vector2& size, RelayoutContainer& container)
396 {
397   Control::OnRelayout(size, container);
398   ScaleModel();
399 }
400
401 bool Model::IsResourceReady() const
402 {
403   return mModelResourceReady && mIBLResourceReady;
404 }
405
406 void Model::LoadModel()
407 {
408   std::filesystem::path modelUrl(mModelUrl);
409   if(mResourceDirectoryUrl.empty())
410   {
411     mResourceDirectoryUrl = std::string(modelUrl.parent_path()) + "/";
412   }
413   std::string extension = modelUrl.extension();
414   std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
415
416   Dali::Scene3D::Loader::ResourceBundle::PathProvider pathProvider = [&](Dali::Scene3D::Loader::ResourceType::Value type) {
417     return mResourceDirectoryUrl;
418   };
419
420   Dali::Scene3D::Loader::ResourceBundle                        resources;
421   Dali::Scene3D::Loader::SceneDefinition                       scene;
422   std::vector<Dali::Scene3D::Loader::AnimationGroupDefinition> animGroups;
423   std::vector<Dali::Scene3D::Loader::CameraParameters>         cameraParameters;
424   std::vector<Dali::Scene3D::Loader::LightParameters>          lights;
425
426   std::vector<Dali::Scene3D::Loader::AnimationDefinition> animations;
427   animations.clear();
428
429   Dali::Scene3D::Loader::SceneMetadata metaData;
430
431   std::filesystem::path metaDataUrl = modelUrl;
432   metaDataUrl.replace_extension("metadata");
433
434   Dali::Scene3D::Loader::LoadSceneMetadata(metaDataUrl.c_str(), metaData);
435
436   Dali::Scene3D::Loader::LoadResult output{resources, scene, metaData, animations, animGroups, cameraParameters, lights};
437
438   if(extension == DLI_EXTENSION)
439   {
440     Dali::Scene3D::Loader::DliLoader              loader;
441     Dali::Scene3D::Loader::DliLoader::InputParams input{
442       pathProvider(Dali::Scene3D::Loader::ResourceType::Mesh),
443       nullptr,
444       {},
445       {},
446       nullptr,
447       {}};
448     Dali::Scene3D::Loader::DliLoader::LoadParams loadParams{input, output};
449     if(!loader.LoadScene(mModelUrl, loadParams))
450     {
451       Dali::Scene3D::Loader::ExceptionFlinger(ASSERT_LOCATION) << "Failed to load scene from '" << mModelUrl << "': " << loader.GetParseError();
452     }
453   }
454   else if(extension == GLTF_EXTENSION)
455   {
456     Dali::Scene3D::Loader::ShaderDefinitionFactory sdf;
457     sdf.SetResources(resources);
458     Dali::Scene3D::Loader::LoadGltfScene(mModelUrl, sdf, output);
459
460     resources.mEnvironmentMaps.push_back({});
461   }
462   else
463   {
464     DALI_LOG_ERROR("Unsupported model type.\n");
465   }
466
467   Dali::Scene3D::Loader::Transforms                   xforms{Dali::Scene3D::Loader::MatrixStack{}, Dali::Scene3D::Loader::ViewProjection{}};
468   Dali::Scene3D::Loader::NodeDefinition::CreateParams nodeParams{resources, xforms, {}, {}, {}};
469   Dali::Scene3D::Loader::Customization::Choices       choices;
470
471   mModelRoot = Actor::New();
472   mModelRoot.SetProperty(Actor::Property::COLOR_MODE, ColorMode::USE_OWN_MULTIPLY_PARENT_COLOR);
473
474   BoundingVolume AABB;
475   for(auto iRoot : scene.GetRoots())
476   {
477     auto resourceRefs = resources.CreateRefCounter();
478     scene.CountResourceRefs(iRoot, choices, resourceRefs);
479     resources.CountEnvironmentReferences(resourceRefs);
480
481     resources.LoadResources(resourceRefs, pathProvider);
482
483     // glTF Mesh is defined in right hand coordinate system, with positive Y for Up direction.
484     // Because DALi uses left hand system, Y direciton will be flipped for environment map sampling.
485     for(auto&& env : resources.mEnvironmentMaps)
486     {
487       env.first.mYDirection = Y_DIRECTION;
488     }
489
490     if(auto actor = scene.CreateNodes(iRoot, choices, nodeParams))
491     {
492       scene.ConfigureSkeletonJoints(iRoot, resources.mSkeletons, actor);
493       scene.ConfigureSkinningShaders(resources, actor, std::move(nodeParams.mSkinnables));
494       ConfigureBlendShapeShaders(resources, scene, actor, std::move(nodeParams.mBlendshapeRequests));
495
496       scene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
497
498       mModelRoot.Add(actor);
499     }
500
501     AddModelTreeToAABB(AABB, scene, choices, iRoot, nodeParams, Matrix::IDENTITY);
502   }
503
504   if(!resources.mEnvironmentMaps.empty())
505   {
506     mDefaultDiffuseTexture  = resources.mEnvironmentMaps.front().second.mDiffuse;
507     mDefaultSpecularTexture = resources.mEnvironmentMaps.front().second.mSpecular;
508   }
509
510   if(!animations.empty())
511   {
512     auto getActor = [&](const Scene3D::Loader::AnimatedProperty& property)
513     {
514       Dali::Actor actor;
515       if(property.mNodeIndex != Scene3D::Loader::INVALID_INDEX)
516       {
517         auto* node = scene.GetNode(property.mNodeIndex);
518         if(node != nullptr)
519         {
520           actor = mModelRoot.FindChildById(node->mNodeId);
521         }
522       }
523       else
524       {
525         actor = mModelRoot.FindChildByName(property.mNodeName);
526       }
527       return actor;
528     };
529
530     mAnimations.clear();
531     for(auto&& animation : animations)
532     {
533       Dali::Animation anim = animation.ReAnimate(getActor);
534
535       mAnimations.push_back({animation.mName, anim});
536     }
537   }
538
539   mRenderableActors.clear();
540   CollectRenderableActor(mModelRoot);
541   UpdateImageBasedLightTexture();
542   UpdateImageBasedLightScaleFactor();
543
544   mNaturalSize = AABB.CalculateSize();
545   mModelPivot  = AABB.CalculatePivot();
546   mModelRoot.SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
547   Vector3 controlSize = Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
548   if(Dali::EqualsZero(controlSize.x) || Dali::EqualsZero(controlSize.y))
549   {
550     Self().SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
551   }
552
553   FitModelPosition();
554   ScaleModel();
555
556   mModelRoot.SetProperty(Dali::Actor::Property::SENSITIVE, mModelChildrenSensitive);
557   mModelRoot.SetProperty(Dali::Actor::Property::KEYBOARD_FOCUSABLE, mModelChildrenFocusable);
558   mModelRoot.SetProperty(Dali::DevelActor::Property::KEYBOARD_FOCUSABLE_CHILDREN, mModelChildrenFocusable);
559
560   Self().Add(mModelRoot);
561
562   Self().SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3(mModelPivot.x, 1.0f - mModelPivot.y, mModelPivot.z));
563
564   mModelResourceReady = true;
565
566   Control::SetResourceReady(false);
567 }
568
569 void Model::ScaleModel()
570 {
571   if(mModelRoot)
572   {
573     float   scale = 1.0f;
574     Vector3 size  = Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
575     if(size.x > 0.0f && size.y > 0.0f)
576     {
577       scale = MAXFLOAT;
578       scale = std::min(size.x / mNaturalSize.x, scale);
579       scale = std::min(size.y / mNaturalSize.y, scale);
580     }
581     // Models in glTF and dli are defined as right hand coordinate system.
582     // DALi uses left hand coordinate system. Scaling negative is for change winding order.
583     mModelRoot.SetProperty(Dali::Actor::Property::SCALE, Y_DIRECTION * scale);
584   }
585 }
586
587 void Model::FitModelPosition()
588 {
589   if(mModelRoot)
590   {
591     // Loaded model pivot is not the model center.
592     mModelRoot.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
593     mModelRoot.SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3::ONE - mModelPivot);
594   }
595 }
596
597 void Model::CollectRenderableActor(Actor actor)
598 {
599   uint32_t rendererCount = actor.GetRendererCount();
600   if(rendererCount)
601   {
602     mRenderableActors.push_back(actor);
603   }
604
605   uint32_t childrenCount = actor.GetChildCount();
606   for(uint32_t i = 0; i < childrenCount; ++i)
607   {
608     CollectRenderableActor(actor.GetChildAt(i));
609   }
610 }
611
612 void Model::UpdateImageBasedLightTexture()
613 {
614   Dali::Texture currentDiffuseTexture  = (mDiffuseTexture) ? mDiffuseTexture : mSceneDiffuseTexture;
615   Dali::Texture currentSpecularTexture = (mSpecularTexture) ? mSpecularTexture : mSceneSpecularTexture;
616   float         currentIBLScaleFactor  = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
617   if(!currentDiffuseTexture || !currentSpecularTexture)
618   {
619     currentDiffuseTexture  = mDefaultDiffuseTexture;
620     currentSpecularTexture = mDefaultSpecularTexture;
621     currentIBLScaleFactor  = Dali::Scene3D::Loader::EnvironmentDefinition::GetDefaultIntensity();
622   }
623
624   for(auto&& actor : mRenderableActors)
625   {
626     Actor renderableActor = actor.GetHandle();
627     if(renderableActor)
628     {
629       uint32_t rendererCount = renderableActor.GetRendererCount();
630       for(uint32_t i = 0; i < rendererCount; ++i)
631       {
632         Dali::Renderer renderer = renderableActor.GetRendererAt(i);
633         if(renderer)
634         {
635           Dali::TextureSet textures = renderer.GetTextures();
636           if(textures)
637           {
638             uint32_t textureCount = textures.GetTextureCount();
639             // EnvMap requires at least 2 texture, diffuse and specular
640             if(textureCount > 2u)
641             {
642               textures.SetTexture(textureCount - OFFSET_FOR_DIFFUSE_CUBE_TEXTURE, currentDiffuseTexture);
643               textures.SetTexture(textureCount - OFFSET_FOR_SPECULAR_CUBE_TEXTURE, currentSpecularTexture);
644             }
645           }
646         }
647       }
648       renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), currentIBLScaleFactor);
649     }
650   }
651 }
652
653 void Model::UpdateImageBasedLightScaleFactor()
654 {
655   if((!mDiffuseTexture || !mSpecularTexture) &&
656      (!mSceneDiffuseTexture || !mSceneSpecularTexture))
657   {
658     return;
659   }
660
661   float currentIBLScaleFactor = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
662   for(auto&& actor : mRenderableActors)
663   {
664     Actor renderableActor = actor.GetHandle();
665     if(renderableActor)
666     {
667       renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), currentIBLScaleFactor);
668     }
669   }
670 }
671
672 void Model::NotifyImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float scaleFactor)
673 {
674   if(mSceneDiffuseTexture != diffuseTexture || mSceneSpecularTexture != specularTexture)
675   {
676     mSceneDiffuseTexture  = diffuseTexture;
677     mSceneSpecularTexture = specularTexture;
678     mSceneIblScaleFactor  = scaleFactor;
679     UpdateImageBasedLightTexture();
680   }
681 }
682
683 void Model::NotifyImageBasedLightScaleFactor(float scaleFactor)
684 {
685   mSceneIblScaleFactor = scaleFactor;
686   if(mSceneDiffuseTexture && mSceneSpecularTexture)
687   {
688     UpdateImageBasedLightScaleFactor();
689   }
690 }
691
692 } // namespace Internal
693 } // namespace Scene3D
694 } // namespace Dali