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