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