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 <dali-scene3d/internal/controls/model/model-impl.h>
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/math/math-utils.h>
28 #include <dali/public-api/object/type-registry-helper.h>
29 #include <dali/public-api/object/type-registry.h>
33 #include <dali-scene3d/internal/controls/scene-view/scene-view-impl.h>
34 #include <dali-scene3d/public-api/controls/model/model.h>
35 #include <dali-scene3d/public-api/loader/animation-definition.h>
36 #include <dali-scene3d/public-api/loader/camera-parameters.h>
37 #include <dali-scene3d/public-api/loader/cube-map-loader.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>
58 return Scene3D::Model::New(std::string());
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()
65 static constexpr uint32_t OFFSET_FOR_DIFFUSE_CUBE_TEXTURE = 2u;
66 static constexpr uint32_t OFFSET_FOR_SPECULAR_CUBE_TEXTURE = 1u;
68 static constexpr Vector3 Y_DIRECTION(1.0f, -1.0f, 1.0f);
70 static constexpr bool DEFAULT_MODEL_CHILDREN_SENSITIVE = false;
71 static constexpr bool DEFAULT_MODEL_CHILDREN_FOCUSABLE = false;
73 static constexpr std::string_view KTX_EXTENSION = ".ktx";
74 static constexpr std::string_view OBJ_EXTENSION = ".obj";
75 static constexpr std::string_view GLTF_EXTENSION = ".gltf";
76 static constexpr std::string_view DLI_EXTENSION = ".dli";
82 pointMin = Vector3(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
83 pointMax = Vector3(std::numeric_limits<float>::min(), std::numeric_limits<float>::min(), std::numeric_limits<float>::min());
86 void ConsiderNewPointInVolume(const Vector3& position)
88 pointMin.x = std::min(position.x, pointMin.x);
89 pointMin.y = std::min(position.y, pointMin.y);
90 pointMin.z = std::min(position.z, pointMin.z);
92 pointMax.x = std::max(position.x, pointMax.x);
93 pointMax.y = std::max(position.y, pointMax.y);
94 pointMax.z = std::max(position.z, pointMax.z);
97 Vector3 CalculateSize()
99 return pointMax - pointMin;
102 Vector3 CalculatePivot()
104 Vector3 pivot = pointMin / (pointMin - pointMax);
105 for(uint32_t i = 0; i < 3; ++i)
107 // To avoid divid by zero
108 if(Dali::Equals(pointMin[i], pointMax[i]))
120 void ConfigureBlendShapeShaders(
121 Dali::Scene3D::Loader::ResourceBundle& resources, const Dali::Scene3D::Loader::SceneDefinition& scene, Actor root, std::vector<Dali::Scene3D::Loader::BlendshapeShaderConfigurationRequest>&& requests)
123 std::vector<std::string> errors;
124 auto onError = [&errors](const std::string& msg) { errors.push_back(msg); };
125 if(!scene.ConfigureBlendshapeShaders(resources, root, std::move(requests), onError))
127 Dali::Scene3D::Loader::ExceptionFlinger flinger(ASSERT_LOCATION);
128 for(auto& msg : errors)
130 flinger << msg << '\n';
135 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)
137 static constexpr uint32_t BOX_POINT_COUNT = 8;
138 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}};
141 const Dali::Scene3D::Loader::NodeDefinition* node = scene.GetNode(iNode);
142 Matrix localMatrix = node->GetLocalSpace();
143 Matrix::Multiply(nodeMatrix, localMatrix, parentMatrix);
146 if(node->GetExtents(nodeParams.mResources, volume[0], volume[1]))
148 for(uint32_t i = 0; i < BOX_POINT_COUNT; ++i)
150 Vector4 position = Vector4(volume[BBIndex[i][0]].x, volume[BBIndex[i][1]].y, volume[BBIndex[i][2]].z, 1.0f);
151 Vector4 objectPosition = nodeMatrix * position;
152 objectPosition /= objectPosition.w;
154 AABB.ConsiderNewPointInVolume(Vector3(objectPosition));
158 if(node->mCustomization)
160 if(!node->mChildren.empty())
162 auto choice = choices.Get(node->mCustomization->mTag);
163 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));
165 AddModelTreeToAABB(AABB, scene, choices, node->mChildren[i], nodeParams, nodeMatrix);
170 for(auto i : node->mChildren)
172 AddModelTreeToAABB(AABB, scene, choices, i, nodeParams, nodeMatrix);
177 } // anonymous namespace
179 Model::Model(const std::string& modelUrl, const std::string& resourceDirectoryUrl)
180 : Control(ControlBehaviour(DISABLE_SIZE_NEGOTIATION | DISABLE_STYLE_CHANGE_SIGNALS)),
182 mResourceDirectoryUrl(resourceDirectoryUrl),
184 mNaturalSize(Vector3::ZERO),
185 mModelPivot(AnchorPoint::CENTER),
186 mSceneIblScaleFactor(1.0f),
187 mIblScaleFactor(1.0f),
188 mModelChildrenSensitive(DEFAULT_MODEL_CHILDREN_SENSITIVE),
189 mModelChildrenFocusable(DEFAULT_MODEL_CHILDREN_FOCUSABLE),
190 mModelResourceReady(false),
191 mIBLResourceReady(true)
199 Dali::Scene3D::Model Model::New(const std::string& modelUrl, const std::string& resourceDirectoryUrl)
201 Model* impl = new Model(modelUrl, resourceDirectoryUrl);
203 Dali::Scene3D::Model handle = Dali::Scene3D::Model(*impl);
205 // Second-phase init of the implementation
206 // This can only be done after the CustomActor connection has been made...
212 const Actor Model::GetModelRoot() const
217 void Model::SetChildrenSensitive(bool enable)
219 if(mModelChildrenSensitive != enable)
221 mModelChildrenSensitive = enable;
224 mModelRoot.SetProperty(Dali::Actor::Property::SENSITIVE, mModelChildrenSensitive);
229 bool Model::GetChildrenSensitive() const
231 return mModelChildrenSensitive;
234 void Model::SetChildrenFocusable(bool enable)
236 if(mModelChildrenFocusable != enable)
238 mModelChildrenFocusable = enable;
241 mModelRoot.SetProperty(Dali::Actor::Property::KEYBOARD_FOCUSABLE, mModelChildrenFocusable);
242 mModelRoot.SetProperty(Dali::DevelActor::Property::KEYBOARD_FOCUSABLE_CHILDREN, mModelChildrenFocusable);
247 bool Model::GetChildrenFocusable() const
249 return mModelChildrenFocusable;
252 void Model::SetImageBasedLightSource(const std::string& diffuseUrl, const std::string& specularUrl, float scaleFactor)
254 mIBLResourceReady = false;
255 Texture diffuseTexture = (!diffuseUrl.empty()) ? Dali::Scene3D::Loader::LoadCubeMap(diffuseUrl) : Texture();
256 Texture specularTexture = (!specularUrl.empty()) ? Dali::Scene3D::Loader::LoadCubeMap(specularUrl) : Texture();
257 SetImageBasedLightTexture(diffuseTexture, specularTexture, scaleFactor);
258 mIBLResourceReady = true;
260 // If Model resource is already ready, then set resource ready.
261 // If Model resource is still not ready, wait for model resource ready.
262 if(IsResourceReady())
264 SetResourceReady(false);
268 void Model::SetImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float scaleFactor)
270 // If input texture is wrong, Model is rendered with SceneView's IBL.
271 if(mDiffuseTexture != diffuseTexture || mSpecularTexture != specularTexture)
273 mDiffuseTexture = diffuseTexture;
274 mSpecularTexture = specularTexture;
275 mIblScaleFactor = scaleFactor;
276 UpdateImageBasedLightTexture();
280 void Model::SetImageBasedLightScaleFactor(float scaleFactor)
282 mIblScaleFactor = scaleFactor;
283 if(mDiffuseTexture && mSpecularTexture)
285 UpdateImageBasedLightScaleFactor();
289 float Model::GetImageBasedLightScaleFactor() const
291 return mIblScaleFactor;
294 uint32_t Model::GetAnimationCount() const
296 return mAnimations.size();
299 Dali::Animation Model::GetAnimation(uint32_t index) const
301 Dali::Animation animation;
302 if(mAnimations.size() > index)
304 animation = mAnimations[index].second;
309 Dali::Animation Model::GetAnimation(const std::string& name) const
311 Dali::Animation animation;
314 for(auto&& animationData : mAnimations)
316 if(animationData.first == name)
318 animation = animationData.second;
326 ///////////////////////////////////////////////////////////
331 void Model::OnInitialize()
333 // Make ParentOrigin as Center.
334 Self().SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
337 void Model::OnSceneConnection(int depth)
344 Actor parent = Self().GetParent();
347 Scene3D::SceneView sceneView = Scene3D::SceneView::DownCast(parent);
350 GetImpl(sceneView).RegisterSceneItem(this);
351 mParentSceneView = sceneView;
354 parent = parent.GetParent();
357 Control::OnSceneConnection(depth);
360 void Model::OnSceneDisconnection()
362 Scene3D::SceneView sceneView = mParentSceneView.GetHandle();
365 GetImpl(sceneView).UnregisterSceneItem(this);
366 mParentSceneView.Reset();
368 Control::OnSceneDisconnection();
371 Vector3 Model::GetNaturalSize()
381 float Model::GetHeightForWidth(float width)
384 padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
385 return Control::GetHeightForWidth(width) + padding.top + padding.bottom;
388 float Model::GetWidthForHeight(float height)
391 padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
392 return Control::GetWidthForHeight(height) + padding.start + padding.end;
395 void Model::OnRelayout(const Vector2& size, RelayoutContainer& container)
397 Control::OnRelayout(size, container);
401 bool Model::IsResourceReady() const
403 return mModelResourceReady && mIBLResourceReady;
406 void Model::LoadModel()
408 std::filesystem::path modelUrl(mModelUrl);
409 if(mResourceDirectoryUrl.empty())
411 mResourceDirectoryUrl = std::string(modelUrl.parent_path()) + "/";
413 std::string extension = modelUrl.extension();
414 std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
416 Dali::Scene3D::Loader::ResourceBundle::PathProvider pathProvider = [&](Dali::Scene3D::Loader::ResourceType::Value type) {
417 return mResourceDirectoryUrl;
420 Dali::Scene3D::Loader::ResourceBundle resources;
421 Dali::Scene3D::Loader::SceneDefinition scene;
422 std::vector<Dali::Scene3D::Loader::AnimationGroupDefinition> animGroups;
423 std::vector<Dali::Scene3D::Loader::CameraParameters> cameraParameters;
424 std::vector<Dali::Scene3D::Loader::LightParameters> lights;
426 std::vector<Dali::Scene3D::Loader::AnimationDefinition> animations;
429 Dali::Scene3D::Loader::SceneMetadata metaData;
431 std::filesystem::path metaDataUrl = modelUrl;
432 metaDataUrl.replace_extension("metadata");
434 Dali::Scene3D::Loader::LoadSceneMetadata(metaDataUrl.c_str(), metaData);
436 Dali::Scene3D::Loader::LoadResult output{resources, scene, metaData, animations, animGroups, cameraParameters, lights};
438 if(extension == DLI_EXTENSION)
440 Dali::Scene3D::Loader::DliLoader loader;
441 Dali::Scene3D::Loader::DliLoader::InputParams input{
442 pathProvider(Dali::Scene3D::Loader::ResourceType::Mesh),
448 Dali::Scene3D::Loader::DliLoader::LoadParams loadParams{input, output};
449 if(!loader.LoadScene(mModelUrl, loadParams))
451 Dali::Scene3D::Loader::ExceptionFlinger(ASSERT_LOCATION) << "Failed to load scene from '" << mModelUrl << "': " << loader.GetParseError();
454 else if(extension == GLTF_EXTENSION)
456 Dali::Scene3D::Loader::ShaderDefinitionFactory sdf;
457 sdf.SetResources(resources);
458 Dali::Scene3D::Loader::LoadGltfScene(mModelUrl, sdf, output);
460 resources.mEnvironmentMaps.push_back({});
464 DALI_LOG_ERROR("Unsupported model type.\n");
467 Dali::Scene3D::Loader::Transforms xforms{Dali::Scene3D::Loader::MatrixStack{}, Dali::Scene3D::Loader::ViewProjection{}};
468 Dali::Scene3D::Loader::NodeDefinition::CreateParams nodeParams{resources, xforms, {}, {}, {}};
469 Dali::Scene3D::Loader::Customization::Choices choices;
471 mModelRoot = Actor::New();
472 mModelRoot.SetProperty(Actor::Property::COLOR_MODE, ColorMode::USE_OWN_MULTIPLY_PARENT_COLOR);
475 for(auto iRoot : scene.GetRoots())
477 auto resourceRefs = resources.CreateRefCounter();
478 scene.CountResourceRefs(iRoot, choices, resourceRefs);
479 resources.CountEnvironmentReferences(resourceRefs);
481 resources.LoadResources(resourceRefs, pathProvider);
483 // glTF Mesh is defined in right hand coordinate system, with positive Y for Up direction.
484 // Because DALi uses left hand system, Y direciton will be flipped for environment map sampling.
485 for(auto&& env : resources.mEnvironmentMaps)
487 env.first.mYDirection = Y_DIRECTION;
490 if(auto actor = scene.CreateNodes(iRoot, choices, nodeParams))
492 scene.ConfigureSkeletonJoints(iRoot, resources.mSkeletons, actor);
493 scene.ConfigureSkinningShaders(resources, actor, std::move(nodeParams.mSkinnables));
494 ConfigureBlendShapeShaders(resources, scene, actor, std::move(nodeParams.mBlendshapeRequests));
496 scene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
498 mModelRoot.Add(actor);
501 AddModelTreeToAABB(AABB, scene, choices, iRoot, nodeParams, Matrix::IDENTITY);
504 if(!resources.mEnvironmentMaps.empty())
506 mDefaultDiffuseTexture = resources.mEnvironmentMaps.front().second.mDiffuse;
507 mDefaultSpecularTexture = resources.mEnvironmentMaps.front().second.mSpecular;
510 if(!animations.empty())
512 auto getActor = [&](const Scene3D::Loader::AnimatedProperty& property)
515 if(property.mNodeIndex != Scene3D::Loader::INVALID_INDEX)
517 auto* node = scene.GetNode(property.mNodeIndex);
520 actor = mModelRoot.FindChildById(node->mNodeId);
525 actor = mModelRoot.FindChildByName(property.mNodeName);
531 for(auto&& animation : animations)
533 Dali::Animation anim = animation.ReAnimate(getActor);
535 mAnimations.push_back({animation.mName, anim});
539 mRenderableActors.clear();
540 CollectRenderableActor(mModelRoot);
541 UpdateImageBasedLightTexture();
542 UpdateImageBasedLightScaleFactor();
544 mNaturalSize = AABB.CalculateSize();
545 mModelPivot = AABB.CalculatePivot();
546 mModelRoot.SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
547 Vector3 controlSize = Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
548 if(Dali::EqualsZero(controlSize.x) || Dali::EqualsZero(controlSize.y))
550 Self().SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
556 mModelRoot.SetProperty(Dali::Actor::Property::SENSITIVE, mModelChildrenSensitive);
557 mModelRoot.SetProperty(Dali::Actor::Property::KEYBOARD_FOCUSABLE, mModelChildrenFocusable);
558 mModelRoot.SetProperty(Dali::DevelActor::Property::KEYBOARD_FOCUSABLE_CHILDREN, mModelChildrenFocusable);
560 Self().Add(mModelRoot);
562 Self().SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3(mModelPivot.x, 1.0f - mModelPivot.y, mModelPivot.z));
564 mModelResourceReady = true;
566 Control::SetResourceReady(false);
569 void Model::ScaleModel()
574 Vector3 size = Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
575 if(size.x > 0.0f && size.y > 0.0f)
578 scale = std::min(size.x / mNaturalSize.x, scale);
579 scale = std::min(size.y / mNaturalSize.y, scale);
581 // Models in glTF and dli are defined as right hand coordinate system.
582 // DALi uses left hand coordinate system. Scaling negative is for change winding order.
583 mModelRoot.SetProperty(Dali::Actor::Property::SCALE, Y_DIRECTION * scale);
587 void Model::FitModelPosition()
591 // Loaded model pivot is not the model center.
592 mModelRoot.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
593 mModelRoot.SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3::ONE - mModelPivot);
597 void Model::CollectRenderableActor(Actor actor)
599 uint32_t rendererCount = actor.GetRendererCount();
602 mRenderableActors.push_back(actor);
605 uint32_t childrenCount = actor.GetChildCount();
606 for(uint32_t i = 0; i < childrenCount; ++i)
608 CollectRenderableActor(actor.GetChildAt(i));
612 void Model::UpdateImageBasedLightTexture()
614 Dali::Texture currentDiffuseTexture = (mDiffuseTexture) ? mDiffuseTexture : mSceneDiffuseTexture;
615 Dali::Texture currentSpecularTexture = (mSpecularTexture) ? mSpecularTexture : mSceneSpecularTexture;
616 float currentIBLScaleFactor = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
617 if(!currentDiffuseTexture || !currentSpecularTexture)
619 currentDiffuseTexture = mDefaultDiffuseTexture;
620 currentSpecularTexture = mDefaultSpecularTexture;
621 currentIBLScaleFactor = Dali::Scene3D::Loader::EnvironmentDefinition::GetDefaultIntensity();
624 for(auto&& actor : mRenderableActors)
626 Actor renderableActor = actor.GetHandle();
629 uint32_t rendererCount = renderableActor.GetRendererCount();
630 for(uint32_t i = 0; i < rendererCount; ++i)
632 Dali::Renderer renderer = renderableActor.GetRendererAt(i);
635 Dali::TextureSet textures = renderer.GetTextures();
638 uint32_t textureCount = textures.GetTextureCount();
639 // EnvMap requires at least 2 texture, diffuse and specular
640 if(textureCount > 2u)
642 textures.SetTexture(textureCount - OFFSET_FOR_DIFFUSE_CUBE_TEXTURE, currentDiffuseTexture);
643 textures.SetTexture(textureCount - OFFSET_FOR_SPECULAR_CUBE_TEXTURE, currentSpecularTexture);
648 renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), currentIBLScaleFactor);
653 void Model::UpdateImageBasedLightScaleFactor()
655 if((!mDiffuseTexture || !mSpecularTexture) &&
656 (!mSceneDiffuseTexture || !mSceneSpecularTexture))
661 float currentIBLScaleFactor = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
662 for(auto&& actor : mRenderableActors)
664 Actor renderableActor = actor.GetHandle();
667 renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), currentIBLScaleFactor);
672 void Model::NotifyImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float scaleFactor)
674 if(mSceneDiffuseTexture != diffuseTexture || mSceneSpecularTexture != specularTexture)
676 mSceneDiffuseTexture = diffuseTexture;
677 mSceneSpecularTexture = specularTexture;
678 mSceneIblScaleFactor = scaleFactor;
679 UpdateImageBasedLightTexture();
683 void Model::NotifyImageBasedLightScaleFactor(float scaleFactor)
685 mSceneIblScaleFactor = scaleFactor;
686 if(mSceneDiffuseTexture && mSceneSpecularTexture)
688 UpdateImageBasedLightScaleFactor();
692 } // namespace Internal
693 } // namespace Scene3D