86893a7a13c3992c8074677d111812d7222704e8
[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::LoadResult output{resources, scene, animations, animGroups, cameraParameters, lights};
429
430   if(extension == DLI_EXTENSION)
431   {
432     Dali::Scene3D::Loader::DliLoader              loader;
433     Dali::Scene3D::Loader::DliLoader::InputParams input{
434       pathProvider(Dali::Scene3D::Loader::ResourceType::Mesh),
435       nullptr,
436       {},
437       {},
438       nullptr,
439       {}};
440     Dali::Scene3D::Loader::DliLoader::LoadParams loadParams{input, output};
441     if(!loader.LoadScene(mModelUrl, loadParams))
442     {
443       Dali::Scene3D::Loader::ExceptionFlinger(ASSERT_LOCATION) << "Failed to load scene from '" << mModelUrl << "': " << loader.GetParseError();
444     }
445   }
446   else if(extension == GLTF_EXTENSION)
447   {
448     Dali::Scene3D::Loader::ShaderDefinitionFactory sdf;
449     sdf.SetResources(resources);
450     Dali::Scene3D::Loader::LoadGltfScene(mModelUrl, sdf, output);
451
452     resources.mEnvironmentMaps.push_back({});
453   }
454   else
455   {
456     DALI_LOG_ERROR("Unsupported model type.\n");
457   }
458
459   Dali::Scene3D::Loader::Transforms                   xforms{Dali::Scene3D::Loader::MatrixStack{}, Dali::Scene3D::Loader::ViewProjection{}};
460   Dali::Scene3D::Loader::NodeDefinition::CreateParams nodeParams{resources, xforms, {}, {}, {}};
461   Dali::Scene3D::Loader::Customization::Choices       choices;
462
463   mModelRoot = Actor::New();
464
465   BoundingVolume AABB;
466   for(auto iRoot : scene.GetRoots())
467   {
468     auto resourceRefs = resources.CreateRefCounter();
469     scene.CountResourceRefs(iRoot, choices, resourceRefs);
470     resources.CountEnvironmentReferences(resourceRefs);
471
472     resources.LoadResources(resourceRefs, pathProvider);
473
474     // glTF Mesh is defined in right hand coordinate system, with positive Y for Up direction.
475     // Because DALi uses left hand system, Y direciton will be flipped for environment map sampling.
476     for(auto&& env : resources.mEnvironmentMaps)
477     {
478       env.first.mYDirection = Y_DIRECTION;
479     }
480
481     if(auto actor = scene.CreateNodes(iRoot, choices, nodeParams))
482     {
483       scene.ConfigureSkeletonJoints(iRoot, resources.mSkeletons, actor);
484       scene.ConfigureSkinningShaders(resources, actor, std::move(nodeParams.mSkinnables));
485       ConfigureBlendShapeShaders(resources, scene, actor, std::move(nodeParams.mBlendshapeRequests));
486
487       scene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
488
489       mModelRoot.Add(actor);
490     }
491
492     AddModelTreeToAABB(AABB, scene, choices, iRoot, nodeParams, Matrix::IDENTITY);
493   }
494
495   if(!resources.mEnvironmentMaps.empty())
496   {
497     mDefaultDiffuseTexture  = resources.mEnvironmentMaps.front().second.mDiffuse;
498     mDefaultSpecularTexture = resources.mEnvironmentMaps.front().second.mSpecular;
499   }
500
501   if(!animations.empty())
502   {
503     auto getActor = [&](const std::string& name) {
504       return mModelRoot.FindChildByName(name);
505     };
506
507     mAnimations.clear();
508     for(auto&& animation : animations)
509     {
510       Dali::Animation anim = animation.ReAnimate(getActor);
511
512       mAnimations.push_back({animation.mName, anim});
513     }
514   }
515
516   mRenderableActors.clear();
517   CollectRenderableActor(mModelRoot);
518   UpdateImageBasedLightTexture();
519   UpdateImageBasedLightScaleFactor();
520
521   mNaturalSize = AABB.CalculateSize();
522   mModelPivot  = AABB.CalculatePivot();
523   mModelRoot.SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
524   Vector3 controlSize = Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
525   if(controlSize.x == 0.0f || controlSize.y == 0.0f)
526   {
527     Self().SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
528   }
529
530   FitModelPosition();
531   ScaleModel();
532
533   mModelRoot.SetProperty(Dali::Actor::Property::SENSITIVE, mModelChildrenSensitive);
534   mModelRoot.SetProperty(Dali::Actor::Property::KEYBOARD_FOCUSABLE, mModelChildrenFocusable);
535   mModelRoot.SetProperty(Dali::DevelActor::Property::KEYBOARD_FOCUSABLE_CHILDREN, mModelChildrenFocusable);
536
537   Self().Add(mModelRoot);
538
539   Self().SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3(mModelPivot.x, 1.0f - mModelPivot.y, mModelPivot.z));
540
541   mModelResourceReady = true;
542
543   Control::SetResourceReady(false);
544 }
545
546 void Model::ScaleModel()
547 {
548   if(mModelRoot)
549   {
550     float   scale = 1.0f;
551     Vector3 size  = Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
552     if(size.x > 0.0f && size.y > 0.0f)
553     {
554       scale = MAXFLOAT;
555       scale = std::min(size.x / mNaturalSize.x, scale);
556       scale = std::min(size.y / mNaturalSize.y, scale);
557     }
558     // Models in glTF and dli are defined as right hand coordinate system.
559     // DALi uses left hand coordinate system. Scaling negative is for change winding order.
560     mModelRoot.SetProperty(Dali::Actor::Property::SCALE, Y_DIRECTION * scale);
561   }
562 }
563
564 void Model::FitModelPosition()
565 {
566   if(mModelRoot)
567   {
568     // Loaded model pivot is not the model center.
569     mModelRoot.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
570     mModelRoot.SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3::ONE - mModelPivot);
571   }
572 }
573
574 void Model::CollectRenderableActor(Actor actor)
575 {
576   uint32_t rendererCount = actor.GetRendererCount();
577   if(rendererCount)
578   {
579     mRenderableActors.push_back(actor);
580   }
581
582   uint32_t childrenCount = actor.GetChildCount();
583   for(uint32_t i = 0; i < childrenCount; ++i)
584   {
585     CollectRenderableActor(actor.GetChildAt(i));
586   }
587 }
588
589 void Model::UpdateImageBasedLightTexture()
590 {
591   Dali::Texture currentDiffuseTexture  = (mDiffuseTexture) ? mDiffuseTexture : mSceneDiffuseTexture;
592   Dali::Texture currentSpecularTexture = (mSpecularTexture) ? mSpecularTexture : mSceneSpecularTexture;
593   float         currentIBLScaleFactor  = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
594   if(!currentDiffuseTexture || !currentSpecularTexture)
595   {
596     currentDiffuseTexture  = mDefaultDiffuseTexture;
597     currentSpecularTexture = mDefaultSpecularTexture;
598     currentIBLScaleFactor  = Dali::Scene3D::Loader::EnvironmentDefinition::GetDefaultIntensity();
599   }
600
601   for(auto&& actor : mRenderableActors)
602   {
603     Actor renderableActor = actor.GetHandle();
604     if(renderableActor)
605     {
606       uint32_t rendererCount = renderableActor.GetRendererCount();
607       for(uint32_t i = 0; i < rendererCount; ++i)
608       {
609         Dali::Renderer renderer = renderableActor.GetRendererAt(i);
610         if(renderer)
611         {
612           Dali::TextureSet textures = renderer.GetTextures();
613           if(textures)
614           {
615             uint32_t textureCount = textures.GetTextureCount();
616             // EnvMap requires at least 2 texture, diffuse and specular
617             if(textureCount > 2u)
618             {
619               textures.SetTexture(textureCount - OFFSET_FOR_DIFFUSE_CUBE_TEXTURE, currentDiffuseTexture);
620               textures.SetTexture(textureCount - OFFSET_FOR_SPECULAR_CUBE_TEXTURE, currentSpecularTexture);
621             }
622           }
623         }
624       }
625       renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), currentIBLScaleFactor);
626     }
627   }
628 }
629
630 void Model::UpdateImageBasedLightScaleFactor()
631 {
632   if((!mDiffuseTexture || !mSpecularTexture) &&
633      (!mSceneDiffuseTexture || !mSceneSpecularTexture))
634   {
635     return;
636   }
637
638   float currentIBLScaleFactor = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
639   for(auto&& actor : mRenderableActors)
640   {
641     Actor renderableActor = actor.GetHandle();
642     if(renderableActor)
643     {
644       renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), currentIBLScaleFactor);
645     }
646   }
647 }
648
649 void Model::NotifyImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float scaleFactor)
650 {
651   if(mSceneDiffuseTexture != diffuseTexture || mSceneSpecularTexture != specularTexture)
652   {
653     mSceneDiffuseTexture  = diffuseTexture;
654     mSceneSpecularTexture = specularTexture;
655     mSceneIblScaleFactor  = scaleFactor;
656     UpdateImageBasedLightTexture();
657   }
658 }
659
660 void Model::NotifyImageBasedLightScaleFactor(float scaleFactor)
661 {
662   mSceneIblScaleFactor = scaleFactor;
663   if(mSceneDiffuseTexture && mSceneSpecularTexture)
664   {
665     UpdateImageBasedLightScaleFactor();
666   }
667 }
668
669 } // namespace Internal
670 } // namespace Scene3D
671 } // namespace Dali