2 * Copyright (c) 2022 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "model-view-impl.h"
22 #include <dali-toolkit/dali-toolkit.h>
23 #include <dali-toolkit/devel-api/controls/control-devel.h>
24 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
25 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
26 #include <dali/public-api/object/type-registry-helper.h>
27 #include <dali/public-api/object/type-registry.h>
28 #include <dali/integration-api/debug.h>
32 #include <dali-scene-loader/public-api/animation-definition.h>
33 #include <dali-scene-loader/public-api/camera-parameters.h>
34 #include <dali-scene-loader/public-api/dli-loader.h>
35 #include <dali-scene-loader/public-api/gltf2-loader.h>
36 #include <dali-scene-loader/public-api/light-parameters.h>
37 #include <dali-scene-loader/public-api/load-result.h>
38 #include <dali-scene-loader/public-api/node-definition.h>
39 #include <dali-scene-loader/public-api/scene-definition.h>
40 #include <dali-scene-loader/public-api/shader-definition-factory.h>
41 #include <dali-scene-loader/public-api/controls/model-view/model-view.h>
42 #include <dali-scene-loader/public-api/cube-map-loader.h>
43 #include <dali-scene-loader/public-api/cube-data.h>
57 return Scene3D::ModelView::New(std::string());
60 // Setup properties, signals and actions using the type-registry.
61 DALI_TYPE_REGISTRATION_BEGIN(Scene3D::ModelView, Toolkit::Control, Create);
62 DALI_TYPE_REGISTRATION_END()
64 static constexpr uint32_t OFFSET_FOR_DIFFUSE_CUBE_TEXTURE = 2u;
65 static constexpr uint32_t OFFSET_FOR_SPECULAR_CUBE_TEXTURE = 1u;
67 static constexpr Vector3 Y_DIRECTION(1.0f, -1.0f, 1.0f);
69 static constexpr std::string_view KTX_EXTENSION = ".ktx";
70 static constexpr std::string_view OBJ_EXTENSION = ".obj";
71 static constexpr std::string_view GLTF_EXTENSION = ".gltf";
72 static constexpr std::string_view DLI_EXTENSION = ".dli";
78 pointMin = Vector3(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
79 pointMax = Vector3(std::numeric_limits<float>::min(), std::numeric_limits<float>::min(), std::numeric_limits<float>::min());
82 void ConsiderNewPointInVolume(const Vector3& position)
84 pointMin.x = std::min(position.x, pointMin.x);
85 pointMin.y = std::min(position.y, pointMin.y);
86 pointMin.z = std::min(position.z, pointMin.z);
88 pointMax.x = std::max(position.x, pointMax.x);
89 pointMax.y = std::max(position.y, pointMax.y);
90 pointMax.z = std::max(position.z, pointMax.z);
93 Vector3 CalculateSize()
95 return pointMax - pointMin;
98 Vector3 CalculatePivot()
100 Vector3 pivot = pointMin / (pointMin - pointMax);
101 for(uint32_t i = 0; i < 3; ++i)
103 // To avoid divid by zero
104 if(pointMin[i] == pointMax[i])
116 Texture LoadCubeMap(const std::string& cubeMapPath)
119 SceneLoader::CubeData cubeData;
120 if(SceneLoader::LoadCubeMapData(cubeMapPath, cubeData))
122 cubeTexture = cubeData.CreateTexture();
126 DALI_LOG_ERROR("Fail to load cube map, %s\n", cubeMapPath.c_str());
132 void ConfigureBlendShapeShaders(
133 SceneLoader::ResourceBundle& resources, const SceneLoader::SceneDefinition& scene, Actor root, std::vector<SceneLoader::BlendshapeShaderConfigurationRequest>&& requests)
135 std::vector<std::string> errors;
136 auto onError = [&errors](const std::string& msg) { errors.push_back(msg); };
137 if(!scene.ConfigureBlendshapeShaders(resources, root, std::move(requests), onError))
139 SceneLoader::ExceptionFlinger flinger(ASSERT_LOCATION);
140 for(auto& msg : errors)
142 flinger << msg << '\n';
147 void AddModelTreeToAABB(BoundingVolume& AABB, const SceneLoader::SceneDefinition& scene, const Dali::SceneLoader::Customization::Choices& choices, Dali::SceneLoader::Index iNode, Dali::SceneLoader::NodeDefinition::CreateParams& nodeParams, Matrix parentMatrix)
149 static constexpr uint32_t BOX_POINT_COUNT = 8;
150 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}};
153 const SceneLoader::NodeDefinition* node = scene.GetNode(iNode);
154 Matrix localMatrix = node->GetLocalSpace();
155 Matrix::Multiply(nodeMatrix, localMatrix, parentMatrix);
158 if(node->GetExtents(nodeParams.mResources, volume[0], volume[1]))
160 for(uint32_t i = 0; i < BOX_POINT_COUNT; ++i)
162 Vector4 position = Vector4(volume[BBIndex[i][0]].x, volume[BBIndex[i][1]].y, volume[BBIndex[i][2]].z, 1.0f);
163 Vector4 objectPosition = nodeMatrix * position;
164 objectPosition /= objectPosition.w;
166 AABB.ConsiderNewPointInVolume(Vector3(objectPosition));
170 if(node->mCustomization)
172 if(!node->mChildren.empty())
174 auto choice = choices.Get(node->mCustomization->mTag);
175 Dali::SceneLoader::Index i = std::min(choice != Dali::SceneLoader::Customization::NONE ? choice : 0, static_cast<Dali::SceneLoader::Index>(node->mChildren.size() - 1));
177 AddModelTreeToAABB(AABB, scene, choices, node->mChildren[i], nodeParams, nodeMatrix);
182 for(auto i : node->mChildren)
184 AddModelTreeToAABB(AABB, scene, choices, i, nodeParams, nodeMatrix);
189 } // anonymous namespace
191 ModelView::ModelView(const std::string& modelPath, const std::string& resourcePath)
192 : Control(ControlBehaviour(CONTROL_BEHAVIOUR_DEFAULT)),
193 mModelPath(modelPath),
194 mResourcePath(resourcePath),
197 mNaturalSize(Vector3::ZERO),
198 mModelPivot(AnchorPoint::CENTER),
199 mIblScaleFactor(1.0f),
205 ModelView::~ModelView()
209 Dali::Scene3D::ModelView ModelView::New(const std::string& modelPath, const std::string& resourcePath)
211 ModelView* impl = new ModelView(modelPath, resourcePath);
213 Dali::Scene3D::ModelView handle = Dali::Scene3D::ModelView(*impl);
215 // Second-phase init of the implementation
216 // This can only be done after the CustomActor connection has been made...
222 const Actor ModelView::GetModelRoot()
227 void ModelView::FitSize(bool fit)
233 void ModelView::FitCenter(bool fit)
239 void ModelView::SetImageBasedLightSource(const std::string& diffuse, const std::string& specular, float scaleFactor)
241 Texture diffuseTexture = LoadCubeMap(diffuse);
244 Texture specularTexture = LoadCubeMap(specular);
247 mDiffuseTexture = diffuseTexture;
248 mSpecularTexture = specularTexture;
249 mIblScaleFactor = scaleFactor;
251 SetImageBasedLight(mModelRoot);
256 uint32_t ModelView::GetAnimationCount()
258 return mAnimations.size();
261 Dali::Animation ModelView::GetAnimation(uint32_t index)
263 Dali::Animation animation;
264 if(mAnimations.size() > index)
266 animation = mAnimations[index].second;
271 Dali::Animation ModelView::GetAnimation(const std::string& name)
273 Dali::Animation animation;
276 for(auto&& animationData : mAnimations)
278 if(animationData.first == name)
280 animation = animationData.second;
288 ///////////////////////////////////////////////////////////
293 void ModelView::OnSceneConnection(int depth)
300 Control::OnSceneConnection(depth);
303 void ModelView::OnInitialize()
306 mModelLayer = Layer::New();
307 mModelLayer.SetProperty(Layer::Property::BEHAVIOR, Layer::LAYER_3D);
308 mModelLayer.SetProperty(Layer::Property::DEPTH_TEST, true);
309 mModelLayer.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
310 mModelLayer.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
311 mModelLayer.SetResizePolicy(ResizePolicy::FILL_TO_PARENT,
312 Dimension::ALL_DIMENSIONS);
314 // Models in glTF and dli are defined as right hand coordinate system.
315 // DALi uses left hand coordinate system. Scaling negative is for change winding order.
316 mModelLayer.SetProperty(Dali::Actor::Property::SCALE_Y, -1.0f);
317 self.Add(mModelLayer);
320 Vector3 ModelView::GetNaturalSize()
330 float ModelView::GetHeightForWidth(float width)
333 padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
334 return Control::GetHeightForWidth(width) + padding.top + padding.bottom;
337 float ModelView::GetWidthForHeight(float height)
340 padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
341 return Control::GetWidthForHeight(height) + padding.start + padding.end;
344 void ModelView::OnRelayout(const Vector2& size, RelayoutContainer& container)
346 Control::OnRelayout(size, container);
350 void ModelView::LoadModel()
352 std::filesystem::path modelPath(mModelPath);
353 if(mResourcePath.empty())
355 mResourcePath = std::string(modelPath.parent_path()) + "/";
357 std::string extension = modelPath.extension();
358 std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
360 SceneLoader::ResourceBundle::PathProvider pathProvider = [&](SceneLoader::ResourceType::Value type) {
361 return mResourcePath;
364 SceneLoader::ResourceBundle resources;
365 SceneLoader::SceneDefinition scene;
366 std::vector<SceneLoader::AnimationGroupDefinition> animGroups;
367 std::vector<SceneLoader::CameraParameters> cameraParameters;
368 std::vector<SceneLoader::LightParameters> lights;
370 std::vector<Dali::SceneLoader::AnimationDefinition> animations;
373 SceneLoader::LoadResult output{resources, scene, animations, animGroups, cameraParameters, lights};
375 if(extension == DLI_EXTENSION)
377 SceneLoader::DliLoader loader;
378 SceneLoader::DliLoader::InputParams input{
379 pathProvider(SceneLoader::ResourceType::Mesh),
385 SceneLoader::DliLoader::LoadParams loadParams{input, output};
386 if(!loader.LoadScene(mModelPath, loadParams))
388 SceneLoader::ExceptionFlinger(ASSERT_LOCATION) << "Failed to load scene from '" << mModelPath << "': " << loader.GetParseError();
391 else if(extension == GLTF_EXTENSION)
393 SceneLoader::ShaderDefinitionFactory sdf;
394 sdf.SetResources(resources);
395 SceneLoader::LoadGltfScene(mModelPath, sdf, output);
397 resources.mEnvironmentMaps.push_back({});
401 DALI_LOG_ERROR("Unsupported model type.\n");
404 SceneLoader::Transforms xforms{SceneLoader::MatrixStack{}, SceneLoader::ViewProjection{}};
405 SceneLoader::NodeDefinition::CreateParams nodeParams{resources, xforms, {}, {}, {}};
406 SceneLoader::Customization::Choices choices;
408 mModelRoot = Actor::New();
411 for(auto iRoot : scene.GetRoots())
413 auto resourceRefs = resources.CreateRefCounter();
414 scene.CountResourceRefs(iRoot, choices, resourceRefs);
415 resources.CountEnvironmentReferences(resourceRefs);
417 resources.LoadResources(resourceRefs, pathProvider);
419 // glTF Mesh is defined in right hand coordinate system, with positive Y for Up direction.
420 // Because DALi uses left hand system, Y direciton will be flipped for environment map sampling.
421 for(auto&& env : resources.mEnvironmentMaps)
423 env.first.mYDirection = Y_DIRECTION;
426 if(auto actor = scene.CreateNodes(iRoot, choices, nodeParams))
428 scene.ConfigureSkeletonJoints(iRoot, resources.mSkeletons, actor);
429 scene.ConfigureSkinningShaders(resources, actor, std::move(nodeParams.mSkinnables));
430 ConfigureBlendShapeShaders(resources, scene, actor, std::move(nodeParams.mBlendshapeRequests));
432 scene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
434 mModelRoot.Add(actor);
437 AddModelTreeToAABB(AABB, scene, choices, iRoot, nodeParams, Matrix::IDENTITY);
440 if(!animations.empty())
442 auto getActor = [&](const std::string& name) {
443 return mModelRoot.FindChildByName(name);
447 for(auto&& animation : animations)
449 Dali::Animation anim = animation.ReAnimate(getActor);
451 mAnimations.push_back({animation.mName, anim});
455 SetImageBasedLight(mModelRoot);
457 mNaturalSize = AABB.CalculateSize();
458 mModelPivot = AABB.CalculatePivot();
459 mModelRoot.SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
464 mModelLayer.Add(mModelRoot);
467 void ModelView::ScaleModel()
473 Vector3 size = Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
474 if(size.x > 0.0f && size.y > 0.0f)
476 float scaleFactor = MAXFLOAT;
477 scaleFactor = std::min(size.x / mNaturalSize.x, scaleFactor);
478 scaleFactor = std::min(size.y / mNaturalSize.y, scaleFactor);
479 mModelRoot.SetProperty(Dali::Actor::Property::SCALE, scaleFactor);
483 DALI_LOG_ERROR("ModelView size is wrong.");
488 mModelRoot.SetProperty(Dali::Actor::Property::SCALE, 1.0f);
493 void ModelView::FitModelPosition()
499 // Loaded model pivot is not the model center.
500 mModelRoot.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
501 mModelRoot.SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3::ONE - mModelPivot);
505 mModelRoot.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
506 mModelRoot.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
511 void ModelView::SetImageBasedLight(Actor node)
513 if(!mDiffuseTexture || !mSpecularTexture || !node)
518 uint32_t rendererCount = node.GetRendererCount();
521 node.RegisterProperty(SceneLoader::NodeDefinition::GetIblScaleFactorUniformName().data(), mIblScaleFactor);
524 for(uint32_t i = 0; i < rendererCount; ++i)
526 Dali::Renderer renderer = node.GetRendererAt(i);
529 Dali::TextureSet textures = renderer.GetTextures();
532 uint32_t textureCount = textures.GetTextureCount();
533 // EnvMap requires at least 2 texture, diffuse and specular
534 if(textureCount > 2u)
536 textures.SetTexture(textureCount - OFFSET_FOR_DIFFUSE_CUBE_TEXTURE, mDiffuseTexture);
537 textures.SetTexture(textureCount - OFFSET_FOR_SPECULAR_CUBE_TEXTURE, mSpecularTexture);
543 uint32_t childrenCount = node.GetChildCount();
544 for(uint32_t i = 0; i < childrenCount; ++i)
546 SetImageBasedLight(node.GetChildAt(i));
550 } // namespace Internal
551 } // namespace Scene3D