Adjust text fit condition
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / internal / controls / model-view / model-view-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 "model-view-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/integration-api/debug.h>
26 #include <dali/public-api/object/type-registry-helper.h>
27 #include <dali/public-api/object/type-registry.h>
28 #include <filesystem>
29
30 // INTERNAL INCLUDES
31 #include <dali-scene3d/internal/controls/scene-view/scene-view-impl.h>
32 #include <dali-scene3d/public-api/controls/model-view/model-view.h>
33 #include <dali-scene3d/public-api/loader/animation-definition.h>
34 #include <dali-scene3d/public-api/loader/camera-parameters.h>
35 #include <dali-scene3d/public-api/loader/cube-map-loader.h>
36 #include <dali-scene3d/public-api/loader/dli-loader.h>
37 #include <dali-scene3d/public-api/loader/gltf2-loader.h>
38 #include <dali-scene3d/public-api/loader/light-parameters.h>
39 #include <dali-scene3d/public-api/loader/load-result.h>
40 #include <dali-scene3d/public-api/loader/node-definition.h>
41 #include <dali-scene3d/public-api/loader/scene-definition.h>
42 #include <dali-scene3d/public-api/loader/shader-definition-factory.h>
43
44 using namespace Dali;
45
46 namespace Dali
47 {
48 namespace Scene3D
49 {
50 namespace Internal
51 {
52 namespace
53 {
54 BaseHandle Create()
55 {
56   return Scene3D::ModelView::New(std::string());
57 }
58
59 // Setup properties, signals and actions using the type-registry.
60 DALI_TYPE_REGISTRATION_BEGIN(Scene3D::ModelView, Toolkit::Control, Create);
61 DALI_TYPE_REGISTRATION_END()
62
63 static constexpr uint32_t OFFSET_FOR_DIFFUSE_CUBE_TEXTURE  = 2u;
64 static constexpr uint32_t OFFSET_FOR_SPECULAR_CUBE_TEXTURE = 1u;
65
66 static constexpr Vector3 Y_DIRECTION(1.0f, -1.0f, 1.0f);
67
68 static constexpr std::string_view KTX_EXTENSION  = ".ktx";
69 static constexpr std::string_view OBJ_EXTENSION  = ".obj";
70 static constexpr std::string_view GLTF_EXTENSION = ".gltf";
71 static constexpr std::string_view DLI_EXTENSION  = ".dli";
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(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 ModelView::ModelView(const std::string& modelPath, const std::string& resourcePath)
175 : Control(ControlBehaviour(DISABLE_SIZE_NEGOTIATION | DISABLE_STYLE_CHANGE_SIGNALS)),
176   mModelPath(modelPath),
177   mResourcePath(resourcePath),
178   mModelRoot(),
179   mNaturalSize(Vector3::ZERO),
180   mModelPivot(AnchorPoint::CENTER),
181   mIblScaleFactor(1.0f),
182   mFitSize(true),
183   mFitCenter(true),
184   mModelResourceReady(false),
185   mIBLResourceReady(true)
186 {
187 }
188
189 ModelView::~ModelView()
190 {
191 }
192
193 Dali::Scene3D::ModelView ModelView::New(const std::string& modelPath, const std::string& resourcePath)
194 {
195   ModelView* impl = new ModelView(modelPath, resourcePath);
196
197   Dali::Scene3D::ModelView handle = Dali::Scene3D::ModelView(*impl);
198
199   // Second-phase init of the implementation
200   // This can only be done after the CustomActor connection has been made...
201   impl->Initialize();
202
203   return handle;
204 }
205
206 const Actor ModelView::GetModelRoot() const
207 {
208   return mModelRoot;
209 }
210
211 void ModelView::FitSize(bool fit)
212 {
213   mFitSize = fit;
214   ScaleModel();
215 }
216
217 void ModelView::FitCenter(bool fit)
218 {
219   mFitCenter = fit;
220   FitModelPosition();
221 }
222
223 void ModelView::SetImageBasedLightSource(const std::string& diffuse, const std::string& specular, float scaleFactor)
224 {
225   mIBLResourceReady = false;
226   Texture diffuseTexture  = Dali::Scene3D::Loader::LoadCubeMap(diffuse);
227   Texture specularTexture = Dali::Scene3D::Loader::LoadCubeMap(specular);
228   SetImageBasedLightTexture(diffuseTexture, specularTexture, scaleFactor);
229   mIBLResourceReady = true;
230
231   // If Model resource is already ready, then set resource ready.
232   // If Model resource is still not ready, wait for model resource ready.
233   if(IsResourceReady())
234   {
235     SetResourceReady(false);
236   }
237 }
238
239 void ModelView::SetImageBasedLightTexture(Dali::Texture diffuse, Dali::Texture specular, float scaleFactor)
240 {
241   if(diffuse && specular)
242   {
243     if(mDiffuseTexture != diffuse || mSpecularTexture != specular)
244     {
245       mDiffuseTexture  = diffuse;
246       mSpecularTexture = specular;
247       UpdateImageBasedLightTexture();
248     }
249     if(mIblScaleFactor != scaleFactor)
250     {
251       mIblScaleFactor = scaleFactor;
252       UpdateImageBasedLightScaleFactor();
253     }
254
255   }
256 }
257
258 void ModelView::SetImageBasedLightScaleFactor(float scaleFactor)
259 {
260   mIblScaleFactor = scaleFactor;
261   if(mDiffuseTexture && mSpecularTexture)
262   {
263     UpdateImageBasedLightScaleFactor();
264   }
265 }
266
267 float ModelView::GetImageBasedLightScaleFactor() const
268 {
269   return mIblScaleFactor;
270 }
271
272 uint32_t ModelView::GetAnimationCount() const
273 {
274   return mAnimations.size();
275 }
276
277 Dali::Animation ModelView::GetAnimation(uint32_t index) const
278 {
279   Dali::Animation animation;
280   if(mAnimations.size() > index)
281   {
282     animation = mAnimations[index].second;
283   }
284   return animation;
285 }
286
287 Dali::Animation ModelView::GetAnimation(const std::string& name) const
288 {
289   Dali::Animation animation;
290   if(!name.empty())
291   {
292     for(auto&& animationData : mAnimations)
293     {
294       if(animationData.first == name)
295       {
296         animation = animationData.second;
297         break;
298       }
299     }
300   }
301   return animation;
302 }
303
304 ///////////////////////////////////////////////////////////
305 //
306 // Private methods
307 //
308
309 void ModelView::OnSceneConnection(int depth)
310 {
311   if(!mModelRoot)
312   {
313     LoadModel();
314   }
315
316   Actor parent = Self().GetParent();
317   while(parent)
318   {
319     Scene3D::SceneView sceneView = Scene3D::SceneView::DownCast(parent);
320     if(sceneView)
321     {
322       GetImpl(sceneView).RegisterModelView(Scene3D::ModelView::DownCast(Self()));
323       mParentSceneView = sceneView;
324       break;
325     }
326     parent = parent.GetParent();
327   }
328
329   Control::OnSceneConnection(depth);
330 }
331
332 void ModelView::OnSceneDisconnection()
333 {
334   Scene3D::SceneView sceneView = mParentSceneView.GetHandle();
335   if(sceneView)
336   {
337     GetImpl(sceneView).UnregisterModelView(Scene3D::ModelView::DownCast(Self()));
338     mParentSceneView.Reset();
339   }
340   Control::OnSceneDisconnection();
341 }
342
343 Vector3 ModelView::GetNaturalSize()
344 {
345   if(!mModelRoot)
346   {
347     LoadModel();
348   }
349
350   return mNaturalSize;
351 }
352
353 float ModelView::GetHeightForWidth(float width)
354 {
355   Extents padding;
356   padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
357   return Control::GetHeightForWidth(width) + padding.top + padding.bottom;
358 }
359
360 float ModelView::GetWidthForHeight(float height)
361 {
362   Extents padding;
363   padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
364   return Control::GetWidthForHeight(height) + padding.start + padding.end;
365 }
366
367 void ModelView::OnRelayout(const Vector2& size, RelayoutContainer& container)
368 {
369   Control::OnRelayout(size, container);
370   ScaleModel();
371 }
372
373 bool ModelView::IsResourceReady() const
374 {
375   return mModelResourceReady && mIBLResourceReady;
376 }
377
378 void ModelView::LoadModel()
379 {
380   std::filesystem::path modelPath(mModelPath);
381   if(mResourcePath.empty())
382   {
383     mResourcePath = std::string(modelPath.parent_path()) + "/";
384   }
385   std::string extension = modelPath.extension();
386   std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
387
388   Dali::Scene3D::Loader::ResourceBundle::PathProvider pathProvider = [&](Dali::Scene3D::Loader::ResourceType::Value type) {
389     return mResourcePath;
390   };
391
392   Dali::Scene3D::Loader::ResourceBundle                        resources;
393   Dali::Scene3D::Loader::SceneDefinition                       scene;
394   std::vector<Dali::Scene3D::Loader::AnimationGroupDefinition> animGroups;
395   std::vector<Dali::Scene3D::Loader::CameraParameters>         cameraParameters;
396   std::vector<Dali::Scene3D::Loader::LightParameters>          lights;
397
398   std::vector<Dali::Scene3D::Loader::AnimationDefinition> animations;
399   animations.clear();
400
401   Dali::Scene3D::Loader::LoadResult output{resources, scene, animations, animGroups, cameraParameters, lights};
402
403   if(extension == DLI_EXTENSION)
404   {
405     Dali::Scene3D::Loader::DliLoader              loader;
406     Dali::Scene3D::Loader::DliLoader::InputParams input{
407       pathProvider(Dali::Scene3D::Loader::ResourceType::Mesh),
408       nullptr,
409       {},
410       {},
411       nullptr,
412       {}};
413     Dali::Scene3D::Loader::DliLoader::LoadParams loadParams{input, output};
414     if(!loader.LoadScene(mModelPath, loadParams))
415     {
416       Dali::Scene3D::Loader::ExceptionFlinger(ASSERT_LOCATION) << "Failed to load scene from '" << mModelPath << "': " << loader.GetParseError();
417     }
418   }
419   else if(extension == GLTF_EXTENSION)
420   {
421     Dali::Scene3D::Loader::ShaderDefinitionFactory sdf;
422     sdf.SetResources(resources);
423     Dali::Scene3D::Loader::LoadGltfScene(mModelPath, sdf, output);
424
425     resources.mEnvironmentMaps.push_back({});
426   }
427   else
428   {
429     DALI_LOG_ERROR("Unsupported model type.\n");
430   }
431
432   Dali::Scene3D::Loader::Transforms                   xforms{Dali::Scene3D::Loader::MatrixStack{}, Dali::Scene3D::Loader::ViewProjection{}};
433   Dali::Scene3D::Loader::NodeDefinition::CreateParams nodeParams{resources, xforms, {}, {}, {}};
434   Dali::Scene3D::Loader::Customization::Choices       choices;
435
436   mModelRoot = Actor::New();
437
438   BoundingVolume AABB;
439   for(auto iRoot : scene.GetRoots())
440   {
441     auto resourceRefs = resources.CreateRefCounter();
442     scene.CountResourceRefs(iRoot, choices, resourceRefs);
443     resources.CountEnvironmentReferences(resourceRefs);
444
445     resources.LoadResources(resourceRefs, pathProvider);
446
447     // glTF Mesh is defined in right hand coordinate system, with positive Y for Up direction.
448     // Because DALi uses left hand system, Y direciton will be flipped for environment map sampling.
449     for(auto&& env : resources.mEnvironmentMaps)
450     {
451       env.first.mYDirection = Y_DIRECTION;
452     }
453
454     if(auto actor = scene.CreateNodes(iRoot, choices, nodeParams))
455     {
456       scene.ConfigureSkeletonJoints(iRoot, resources.mSkeletons, actor);
457       scene.ConfigureSkinningShaders(resources, actor, std::move(nodeParams.mSkinnables));
458       ConfigureBlendShapeShaders(resources, scene, actor, std::move(nodeParams.mBlendshapeRequests));
459
460       scene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
461
462       mModelRoot.Add(actor);
463     }
464
465     AddModelTreeToAABB(AABB, scene, choices, iRoot, nodeParams, Matrix::IDENTITY);
466   }
467
468   if(!animations.empty())
469   {
470     auto getActor = [&](const std::string& name) {
471       return mModelRoot.FindChildByName(name);
472     };
473
474     mAnimations.clear();
475     for(auto&& animation : animations)
476     {
477       Dali::Animation anim = animation.ReAnimate(getActor);
478
479       mAnimations.push_back({animation.mName, anim});
480     }
481   }
482
483   mRenderableActors.clear();
484   CollectRenderableActor(mModelRoot);
485   UpdateImageBasedLightTexture();
486   UpdateImageBasedLightScaleFactor();
487
488   mNaturalSize = AABB.CalculateSize();
489   mModelPivot  = AABB.CalculatePivot();
490   mModelRoot.SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
491   Vector3 controlSize = Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
492   if(controlSize.x == 0.0f || controlSize.y == 0.0f)
493   {
494     Self().SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
495   }
496
497   FitModelPosition();
498   ScaleModel();
499
500   Self().Add(mModelRoot);
501
502   Self().SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
503   Self().SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3(mModelPivot.x, 1.0f - mModelPivot.y, mModelPivot.z));
504
505   mModelResourceReady = true;
506
507   Control::SetResourceReady(false);
508 }
509
510 void ModelView::ScaleModel()
511 {
512   if(mModelRoot)
513   {
514     Vector3 size = Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
515     if(mFitSize && size.x > 0.0f && size.y > 0.0f)
516     {
517       float scaleFactor = MAXFLOAT;
518       scaleFactor       = std::min(size.x / mNaturalSize.x, scaleFactor);
519       scaleFactor       = std::min(size.y / mNaturalSize.y, scaleFactor);
520       // Models in glTF and dli are defined as right hand coordinate system.
521       // DALi uses left hand coordinate system. Scaling negative is for change winding order.
522       mModelRoot.SetProperty(Dali::Actor::Property::SCALE, Y_DIRECTION * scaleFactor);
523     }
524     else
525     {
526       // Models in glTF and dli are defined as right hand coordinate system.
527       // DALi uses left hand coordinate system. Scaling negative is for change winding order.
528       mModelRoot.SetProperty(Dali::Actor::Property::SCALE, Y_DIRECTION);
529     }
530   }
531 }
532
533 void ModelView::FitModelPosition()
534 {
535   if(mModelRoot)
536   {
537     if(mFitCenter)
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     else
544     {
545       mModelRoot.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
546       mModelRoot.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
547     }
548   }
549 }
550
551 void ModelView::CollectRenderableActor(Actor actor)
552 {
553   uint32_t rendererCount = actor.GetRendererCount();
554   if(rendererCount)
555   {
556     mRenderableActors.push_back(actor);
557   }
558
559   uint32_t childrenCount = actor.GetChildCount();
560   for(uint32_t i = 0; i < childrenCount; ++i)
561   {
562     CollectRenderableActor(actor.GetChildAt(i));
563   }
564 }
565
566 void ModelView::UpdateImageBasedLightTexture()
567 {
568   if(!mDiffuseTexture || !mSpecularTexture)
569   {
570     return;
571   }
572
573   for(auto&& actor : mRenderableActors)
574   {
575     Actor renderableActor = actor.GetHandle();
576     if(renderableActor)
577     {
578       uint32_t rendererCount = renderableActor.GetRendererCount();
579       for(uint32_t i = 0; i < rendererCount; ++i)
580       {
581         Dali::Renderer renderer = renderableActor.GetRendererAt(i);
582         if(renderer)
583         {
584           Dali::TextureSet textures = renderer.GetTextures();
585           if(textures)
586           {
587             uint32_t textureCount = textures.GetTextureCount();
588             // EnvMap requires at least 2 texture, diffuse and specular
589             if(textureCount > 2u)
590             {
591               textures.SetTexture(textureCount - OFFSET_FOR_DIFFUSE_CUBE_TEXTURE, mDiffuseTexture);
592               textures.SetTexture(textureCount - OFFSET_FOR_SPECULAR_CUBE_TEXTURE, mSpecularTexture);
593             }
594           }
595         }
596       }
597     }
598   }
599 }
600
601 void ModelView::UpdateImageBasedLightScaleFactor()
602 {
603   if(!mDiffuseTexture || !mSpecularTexture)
604   {
605     return;
606   }
607   for(auto&& actor : mRenderableActors)
608   {
609     Actor renderableActor = actor.GetHandle();
610     if(renderableActor)
611     {
612       renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), mIblScaleFactor);
613     }
614   }
615 }
616
617 } // namespace Internal
618 } // namespace Scene3D
619 } // namespace Dali