X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dali-scene3d%2Finternal%2Fcontrols%2Fmodel%2Fmodel-impl.cpp;h=2f29b255210b187779abee1bbc8b24f3f9ab4ec2;hb=679f02413b55445b39148f4b102c153b1b5fae83;hp=b552489848ab8622f9b130f3c3a123a1cb0e182a;hpb=c2045685c3bec7f4ad450605afdd938bf9c94d9a;p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git diff --git a/dali-scene3d/internal/controls/model/model-impl.cpp b/dali-scene3d/internal/controls/model/model-impl.cpp index b552489..2f29b25 100644 --- a/dali-scene3d/internal/controls/model/model-impl.cpp +++ b/dali-scene3d/internal/controls/model/model-impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. + * Copyright (c) 2023 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -30,13 +31,12 @@ #include // INTERNAL INCLUDES +#include #include +#include #include #include #include -#include -#include -#include #include #include #include @@ -53,6 +53,9 @@ namespace Internal { namespace { +/** + * Creates control through type registry + */ BaseHandle Create() { return Scene3D::Model::New(std::string()); @@ -62,19 +65,11 @@ BaseHandle Create() DALI_TYPE_REGISTRATION_BEGIN(Scene3D::Model, Toolkit::Control, Create); DALI_TYPE_REGISTRATION_END() -static constexpr uint32_t OFFSET_FOR_DIFFUSE_CUBE_TEXTURE = 2u; -static constexpr uint32_t OFFSET_FOR_SPECULAR_CUBE_TEXTURE = 1u; - static constexpr Vector3 Y_DIRECTION(1.0f, -1.0f, 1.0f); static constexpr bool DEFAULT_MODEL_CHILDREN_SENSITIVE = false; static constexpr bool DEFAULT_MODEL_CHILDREN_FOCUSABLE = false; -static constexpr std::string_view KTX_EXTENSION = ".ktx"; -static constexpr std::string_view OBJ_EXTENSION = ".obj"; -static constexpr std::string_view GLTF_EXTENSION = ".gltf"; -static constexpr std::string_view DLI_EXTENSION = ".dli"; - struct BoundingVolume { void Init() @@ -121,7 +116,8 @@ void ConfigureBlendShapeShaders( Dali::Scene3D::Loader::ResourceBundle& resources, const Dali::Scene3D::Loader::SceneDefinition& scene, Actor root, std::vector&& requests) { std::vector errors; - auto onError = [&errors](const std::string& msg) { errors.push_back(msg); }; + auto onError = [&errors](const std::string& msg) + { errors.push_back(msg); }; if(!scene.ConfigureBlendshapeShaders(resources, root, std::move(requests), onError)) { Dali::Scene3D::Loader::ExceptionFlinger flinger(ASSERT_LOCATION); @@ -185,15 +181,26 @@ Model::Model(const std::string& modelUrl, const std::string& resourceDirectoryUr mModelPivot(AnchorPoint::CENTER), mSceneIblScaleFactor(1.0f), mIblScaleFactor(1.0f), + mSceneSpecularMipmapLevels(1u), + mSpecularMipmapLevels(1u), mModelChildrenSensitive(DEFAULT_MODEL_CHILDREN_SENSITIVE), mModelChildrenFocusable(DEFAULT_MODEL_CHILDREN_FOCUSABLE), mModelResourceReady(false), - mIBLResourceReady(true) + mIblDiffuseResourceReady(true), + mIblSpecularResourceReady(true), + mIblDiffuseDirty(false), + mIblSpecularDirty(false) { } Model::~Model() { + if(ModelCacheManager::Get() && !mModelUrl.empty()) + { + ModelCacheManager::Get().UnreferenceModelCache(mModelUrl); + } + + ResetResourceTasks(); } Dali::Scene3D::Model Model::New(const std::string& modelUrl, const std::string& resourceDirectoryUrl) @@ -209,11 +216,44 @@ Dali::Scene3D::Model Model::New(const std::string& modelUrl, const std::string& return handle; } -const Actor Model::GetModelRoot() const +const Scene3D::ModelNode Model::GetModelRoot() const { return mModelRoot; } +void Model::AddModelNode(Scene3D::ModelNode modelNode) +{ + if(!mModelRoot) + { + CreateModelRoot(); + } + + mModelRoot.Add(modelNode); + if(mModelUrl.empty()) + { + mModelResourceReady = true; + } + + if(mIblDiffuseResourceReady && mIblSpecularResourceReady) + { + UpdateImageBasedLightTexture(); + UpdateImageBasedLightScaleFactor(); + } + + if(Self().GetProperty(Dali::Actor::Property::CONNECTED_TO_SCENE)) + { + NotifyResourceReady(); + } +} + +void Model::RemoveModelNode(Scene3D::ModelNode modelNode) +{ + if(mModelRoot) + { + mModelRoot.Remove(modelNode); + } +} + void Model::SetChildrenSensitive(bool enable) { if(mModelChildrenSensitive != enable) @@ -251,30 +291,79 @@ bool Model::GetChildrenFocusable() const void Model::SetImageBasedLightSource(const std::string& diffuseUrl, const std::string& specularUrl, float scaleFactor) { - mIBLResourceReady = false; - Texture diffuseTexture = (!diffuseUrl.empty()) ? Dali::Scene3D::Loader::LoadCubeMap(diffuseUrl) : Texture(); - Texture specularTexture = (!specularUrl.empty()) ? Dali::Scene3D::Loader::LoadCubeMap(specularUrl) : Texture(); - SetImageBasedLightTexture(diffuseTexture, specularTexture, scaleFactor); - mIBLResourceReady = true; + bool needIblReset = false; + bool isOnScene = Self().GetProperty(Dali::Actor::Property::CONNECTED_TO_SCENE); + if(mDiffuseIblUrl != diffuseUrl) + { + mDiffuseIblUrl = diffuseUrl; + if(mDiffuseIblUrl.empty()) + { + needIblReset = true; + } + else + { + mIblDiffuseDirty = true; + mIblDiffuseResourceReady = false; + } + } - // If Model resource is already ready, then set resource ready. - // If Model resource is still not ready, wait for model resource ready. - if(IsResourceReady()) + if(mSpecularIblUrl != specularUrl) { - SetResourceReady(false); + mSpecularIblUrl = specularUrl; + if(mSpecularIblUrl.empty()) + { + needIblReset = true; + } + else + { + mIblSpecularDirty = true; + mIblSpecularResourceReady = false; + } } -} -void Model::SetImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float scaleFactor) -{ - // If input texture is wrong, Model is rendered with SceneView's IBL. - if(mDiffuseTexture != diffuseTexture || mSpecularTexture != specularTexture) + // If one or both of diffuse url and specular url are empty, + // we don't need to request to load texture. + if(needIblReset) { - mDiffuseTexture = diffuseTexture; - mSpecularTexture = specularTexture; - mIblScaleFactor = scaleFactor; + ResetResourceTask(mIblDiffuseLoadTask); + ResetResourceTask(mIblSpecularLoadTask); + + mIblDiffuseDirty = false; + mIblSpecularDirty = false; + mIblDiffuseResourceReady = true; + mIblSpecularResourceReady = true; + + mDiffuseTexture.Reset(); + mSpecularTexture.Reset(); UpdateImageBasedLightTexture(); } + else + { + if(isOnScene && mIblDiffuseDirty) + { + ResetResourceTask(mIblDiffuseLoadTask); + mIblDiffuseLoadTask = new EnvironmentMapLoadTask(mDiffuseIblUrl, Scene3D::EnvironmentMapType::CUBEMAP, MakeCallback(this, &Model::OnIblDiffuseLoadComplete)); + Dali::AsyncTaskManager::Get().AddTask(mIblDiffuseLoadTask); + mIblDiffuseDirty = false; + } + + if(isOnScene && mIblSpecularDirty) + { + ResetResourceTask(mIblSpecularLoadTask); + mIblSpecularLoadTask = new EnvironmentMapLoadTask(mSpecularIblUrl, Scene3D::EnvironmentMapType::CUBEMAP, MakeCallback(this, &Model::OnIblSpecularLoadComplete)); + Dali::AsyncTaskManager::Get().AddTask(mIblSpecularLoadTask); + mIblSpecularDirty = false; + } + } + + if(!Dali::Equals(mIblScaleFactor, scaleFactor)) + { + mIblScaleFactor = scaleFactor; + UpdateImageBasedLightScaleFactor(); + } + + // If diffuse and specular textures are already loaded, emits resource ready signal here. + NotifyResourceReady(); } void Model::SetImageBasedLightScaleFactor(float scaleFactor) @@ -323,6 +412,51 @@ Dali::Animation Model::GetAnimation(const std::string& name) const return animation; } +uint32_t Model::GetCameraCount() const +{ + return mCameraParameters.size(); +} + +Dali::CameraActor Model::GenerateCamera(uint32_t index) const +{ + Dali::CameraActor camera; + if(mCameraParameters.size() > index) + { + camera = Dali::CameraActor::New3DCamera(); + if(!mCameraParameters[index].ConfigureCamera(camera, false)) + { + DALI_LOG_ERROR("Fail to generate %u's camera actor : Some property was not defined. Please check model file.\n", index); + camera.Reset(); + return camera; + } + + ApplyCameraTransform(camera); + } + return camera; +} + +bool Model::ApplyCamera(uint32_t index, Dali::CameraActor camera) const +{ + if(camera && mCameraParameters.size() > index) + { + if(!mCameraParameters[index].ConfigureCamera(camera, false)) + { + DALI_LOG_ERROR("Fail to apply %u's camera actor : Some property was not defined. Please check model file.\n", index); + return false; + } + + ApplyCameraTransform(camera); + return true; + } + return false; +} + +Scene3D::ModelNode Model::FindChildModelNodeByName(std::string_view nodeName) +{ + Actor childActor = Self().FindChildByName(nodeName); + return Scene3D::ModelNode::DownCast(childActor); +} + /////////////////////////////////////////////////////////// // // Private methods @@ -336,9 +470,21 @@ void Model::OnInitialize() void Model::OnSceneConnection(int depth) { - if(!mModelRoot) + if(!mModelLoadTask && !mModelResourceReady && !mModelUrl.empty()) + { + // Request model load only if we setup url. + if(ModelCacheManager::Get()) + { + ModelCacheManager::Get().ReferenceModelCache(mModelUrl); + } + mModelLoadTask = new ModelLoadTask(mModelUrl, mResourceDirectoryUrl, MakeCallback(this, &Model::OnModelLoadComplete)); + Dali::AsyncTaskManager::Get().AddTask(mModelLoadTask); + } + + // If diffuse and specular url is not valid, IBL does not need to be loaded. + if(!mDiffuseIblUrl.empty() && !mSpecularIblUrl.empty()) { - LoadModel(); + SetImageBasedLightSource(mDiffuseIblUrl, mSpecularIblUrl, mIblScaleFactor); } Actor parent = Self().GetParent(); @@ -354,6 +500,7 @@ void Model::OnSceneConnection(int depth) parent = parent.GetParent(); } + NotifyResourceReady(); Control::OnSceneConnection(depth); } @@ -372,7 +519,8 @@ Vector3 Model::GetNaturalSize() { if(!mModelRoot) { - LoadModel(); + DALI_LOG_ERROR("Model is still not loaded.\n"); + return Vector3::ZERO; } return mNaturalSize; @@ -400,278 +548,358 @@ void Model::OnRelayout(const Vector2& size, RelayoutContainer& container) bool Model::IsResourceReady() const { - return mModelResourceReady && mIBLResourceReady; + return mModelResourceReady && mIblDiffuseResourceReady && mIblSpecularResourceReady; +} + +void Model::CreateModelRoot() +{ + mModelRoot = Scene3D::ModelNode::New(); + mModelRoot.SetProperty(Actor::Property::COLOR_MODE, ColorMode::USE_OWN_MULTIPLY_PARENT_COLOR); + mModelRoot.SetProperty(Dali::Actor::Property::SCALE, Y_DIRECTION); + mModelRoot.SetProperty(Dali::Actor::Property::SENSITIVE, mModelChildrenSensitive); + mModelRoot.SetProperty(Dali::Actor::Property::KEYBOARD_FOCUSABLE, mModelChildrenFocusable); + mModelRoot.SetProperty(Dali::DevelActor::Property::KEYBOARD_FOCUSABLE_CHILDREN, mModelChildrenFocusable); + Self().Add(mModelRoot); } -void Model::LoadModel() +void Model::ScaleModel() { - std::filesystem::path modelUrl(mModelUrl); - if(mResourceDirectoryUrl.empty()) + if(!mModelRoot) { - mResourceDirectoryUrl = std::string(modelUrl.parent_path()) + "/"; + return; } - std::string extension = modelUrl.extension(); - std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower); - - Dali::Scene3D::Loader::ResourceBundle::PathProvider pathProvider = [&](Dali::Scene3D::Loader::ResourceType::Value type) { - return mResourceDirectoryUrl; - }; - Dali::Scene3D::Loader::ResourceBundle resources; - Dali::Scene3D::Loader::SceneDefinition scene; - std::vector animGroups; - std::vector cameraParameters; - std::vector lights; + float scale = 1.0f; + Vector3 size = Self().GetProperty(Dali::Actor::Property::SIZE); + if(size.x > 0.0f && size.y > 0.0f) + { + scale = MAXFLOAT; + scale = std::min(size.x / mNaturalSize.x, scale); + scale = std::min(size.y / mNaturalSize.y, scale); + } + // Models in glTF and dli are defined as right hand coordinate system. + // DALi uses left hand coordinate system. Scaling negative is for change winding order. + mModelRoot.SetProperty(Dali::Actor::Property::SCALE, Y_DIRECTION * scale); +} - std::vector animations; - animations.clear(); +void Model::FitModelPosition() +{ + if(!mModelRoot) + { + return; + } + // Loaded model pivot is not the model center. + mModelRoot.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + mModelRoot.SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3::ONE - mModelPivot); +} - Dali::Scene3D::Loader::SceneMetadata metaData; +void Model::UpdateImageBasedLightTextureRecursively(Scene3D::ModelNode node, Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels) +{ + if(!node) + { + return; + } - std::filesystem::path metaDataUrl = modelUrl; - metaDataUrl.replace_extension("metadata"); + GetImplementation(node).SetImageBasedLightTexture(diffuseTexture, specularTexture, iblScaleFactor, specularMipmapLevels); + uint32_t childrenCount = node.GetChildCount(); + for(uint32_t i = 0; i < childrenCount; ++i) + { + Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i)); + if(!childNode) + { + continue; + } + UpdateImageBasedLightTextureRecursively(childNode, diffuseTexture, specularTexture, iblScaleFactor, specularMipmapLevels); + } +} - Dali::Scene3D::Loader::LoadSceneMetadata(metaDataUrl.c_str(), metaData); +void Model::UpdateImageBasedLightScaleFactorRecursively(Scene3D::ModelNode node, float iblScaleFactor) +{ + if(!node) + { + return; + } - Dali::Scene3D::Loader::LoadResult output{resources, scene, metaData, animations, animGroups, cameraParameters, lights}; + node.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), iblScaleFactor); + GetImplementation(node).SetImageBasedLightScaleFactor(iblScaleFactor); - if(extension == DLI_EXTENSION) + uint32_t childrenCount = node.GetChildCount(); + for(uint32_t i = 0; i < childrenCount; ++i) { - Dali::Scene3D::Loader::DliLoader loader; - Dali::Scene3D::Loader::DliLoader::InputParams input{ - pathProvider(Dali::Scene3D::Loader::ResourceType::Mesh), - nullptr, - {}, - {}, - nullptr, - {}}; - Dali::Scene3D::Loader::DliLoader::LoadParams loadParams{input, output}; - if(!loader.LoadScene(mModelUrl, loadParams)) + Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i)); + if(!childNode) { - Dali::Scene3D::Loader::ExceptionFlinger(ASSERT_LOCATION) << "Failed to load scene from '" << mModelUrl << "': " << loader.GetParseError(); + continue; } + UpdateImageBasedLightScaleFactorRecursively(childNode, iblScaleFactor); } - else if(extension == GLTF_EXTENSION) - { - Dali::Scene3D::Loader::ShaderDefinitionFactory sdf; - sdf.SetResources(resources); - Dali::Scene3D::Loader::LoadGltfScene(mModelUrl, sdf, output); +} - resources.mEnvironmentMaps.push_back({}); +void Model::UpdateImageBasedLightTexture() +{ + Dali::Texture currentDiffuseTexture = (mDiffuseTexture && mSpecularTexture) ? mDiffuseTexture : mSceneDiffuseTexture; + Dali::Texture currentSpecularTexture = (mDiffuseTexture && mSpecularTexture) ? mSpecularTexture : mSceneSpecularTexture; + float currentIblScaleFactor = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor; + uint32_t currentIblSpecularMipmapLevels = (mDiffuseTexture && mSpecularTexture) ? mSpecularMipmapLevels : mSceneSpecularMipmapLevels; + + if(!currentDiffuseTexture || !currentSpecularTexture) + { + currentDiffuseTexture = mDefaultDiffuseTexture; + currentSpecularTexture = mDefaultSpecularTexture; + currentIblScaleFactor = Dali::Scene3D::Loader::EnvironmentDefinition::GetDefaultIntensity(); + currentIblSpecularMipmapLevels = 1u; } - else + + UpdateImageBasedLightTextureRecursively(mModelRoot, currentDiffuseTexture, currentSpecularTexture, currentIblScaleFactor, currentIblSpecularMipmapLevels); +} + +void Model::UpdateImageBasedLightScaleFactor() +{ + if((!mDiffuseTexture || !mSpecularTexture) && + (!mSceneDiffuseTexture || !mSceneSpecularTexture)) { - DALI_LOG_ERROR("Unsupported model type.\n"); + return; } - Dali::Scene3D::Loader::Transforms xforms{Dali::Scene3D::Loader::MatrixStack{}, Dali::Scene3D::Loader::ViewProjection{}}; - Dali::Scene3D::Loader::NodeDefinition::CreateParams nodeParams{resources, xforms, {}, {}, {}}; - Dali::Scene3D::Loader::Customization::Choices choices; + float currentIblScaleFactor = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor; + UpdateImageBasedLightScaleFactorRecursively(mModelRoot, currentIblScaleFactor); +} - mModelRoot = Actor::New(); - mModelRoot.SetProperty(Actor::Property::COLOR_MODE, ColorMode::USE_OWN_MULTIPLY_PARENT_COLOR); +void Model::ApplyCameraTransform(Dali::CameraActor camera) const +{ + Vector3 selfPosition = Self().GetProperty(Actor::Property::POSITION); + Quaternion selfOrientation = Self().GetProperty(Actor::Property::ORIENTATION); + Vector3 selfScale = Self().GetProperty(Actor::Property::SCALE); - BoundingVolume AABB; - for(auto iRoot : scene.GetRoots()) - { - auto resourceRefs = resources.CreateRefCounter(); - scene.CountResourceRefs(iRoot, choices, resourceRefs); - resources.CountEnvironmentReferences(resourceRefs); + Vector3 cameraPosition = camera.GetProperty(Actor::Property::POSITION); + Quaternion cameraOrientation = camera.GetProperty(Actor::Property::ORIENTATION); + Vector3 cameraScale = camera.GetProperty(Actor::Property::SCALE); - resources.LoadResources(resourceRefs, pathProvider); + // Models in glTF and dli are defined as right hand coordinate system. + // DALi uses left hand coordinate system. Scaling negative is for change winding order. + if(!Dali::Equals(Y_DIRECTION.Dot(Vector3::YAXIS), 1.0f)) + { + // Reflect by XZ plane + cameraPosition.y = -cameraPosition.y; + Quaternion yDirectionQuaternion; + yDirectionQuaternion.mVector = Vector3::YAXIS; + // Reflect orientation + cameraOrientation = yDirectionQuaternion * cameraOrientation * yDirectionQuaternion; + } - // glTF Mesh is defined in right hand coordinate system, with positive Y for Up direction. - // Because DALi uses left hand system, Y direciton will be flipped for environment map sampling. - for(auto&& env : resources.mEnvironmentMaps) - { - env.first.mYDirection = Y_DIRECTION; - } + Vector3 resultPosition; + Quaternion resultOrientation; + Vector3 resultScale; - if(auto actor = scene.CreateNodes(iRoot, choices, nodeParams)) - { - scene.ConfigureSkeletonJoints(iRoot, resources.mSkeletons, actor); - scene.ConfigureSkinningShaders(resources, actor, std::move(nodeParams.mSkinnables)); - ConfigureBlendShapeShaders(resources, scene, actor, std::move(nodeParams.mBlendshapeRequests)); + Matrix selfMatrix(false); + Matrix cameraMatrix(false); + Matrix resultMatrix(false); + selfMatrix.SetTransformComponents(selfScale, selfOrientation, selfPosition); + cameraMatrix.SetTransformComponents(cameraScale, cameraOrientation, cameraPosition); + Matrix::Multiply(resultMatrix, cameraMatrix, selfMatrix); + resultMatrix.GetTransformComponents(resultPosition, resultOrientation, resultScale); - scene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables)); + camera.SetProperty(Actor::Property::POSITION, resultPosition); + camera.SetProperty(Actor::Property::ORIENTATION, resultOrientation); + camera.SetProperty(Actor::Property::SCALE, resultScale); +} - mModelRoot.Add(actor); +void Model::NotifyImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float scaleFactor, uint32_t specularMipmapLevels) +{ + if(mSceneDiffuseTexture != diffuseTexture || mSceneSpecularTexture != specularTexture) + { + mSceneDiffuseTexture = diffuseTexture; + mSceneSpecularTexture = specularTexture; + mSceneIblScaleFactor = scaleFactor; + mSceneSpecularMipmapLevels = specularMipmapLevels; + // If Model IBL is not set, use SceneView's IBL. + if(!mDiffuseTexture || !mSpecularTexture) + { + UpdateImageBasedLightTexture(); } - - AddModelTreeToAABB(AABB, scene, choices, iRoot, nodeParams, Matrix::IDENTITY); } +} - if(!resources.mEnvironmentMaps.empty()) +void Model::NotifyImageBasedLightScaleFactor(float scaleFactor) +{ + mSceneIblScaleFactor = scaleFactor; + if(mSceneDiffuseTexture && mSceneSpecularTexture) { - mDefaultDiffuseTexture = resources.mEnvironmentMaps.front().second.mDiffuse; - mDefaultSpecularTexture = resources.mEnvironmentMaps.front().second.mSpecular; + UpdateImageBasedLightScaleFactor(); } +} - if(!animations.empty()) +void Model::OnModelLoadComplete() +{ + if(!mModelLoadTask->HasSucceeded()) { - auto getActor = [&](const Scene3D::Loader::AnimatedProperty& property) { - return mModelRoot.FindChildById(scene.GetNode(property.mNodeIndex)->mNodeId); - }; + ResetResourceTasks(); - mAnimations.clear(); - for(auto&& animation : animations) + if(ModelCacheManager::Get() && !mModelUrl.empty()) { - Dali::Animation anim = animation.ReAnimate(getActor); - - mAnimations.push_back({animation.mName, anim}); + ModelCacheManager::Get().UnreferenceModelCache(mModelUrl); } - } - mRenderableActors.clear(); - CollectRenderableActor(mModelRoot); - UpdateImageBasedLightTexture(); - UpdateImageBasedLightScaleFactor(); + return; + } - mNaturalSize = AABB.CalculateSize(); - mModelPivot = AABB.CalculatePivot(); - mModelRoot.SetProperty(Dali::Actor::Property::SIZE, mNaturalSize); - Vector3 controlSize = Self().GetProperty(Dali::Actor::Property::SIZE); - if(Dali::EqualsZero(controlSize.x) || Dali::EqualsZero(controlSize.y)) + if(!mModelRoot) { - Self().SetProperty(Dali::Actor::Property::SIZE, mNaturalSize); + CreateModelRoot(); } + CreateModel(); - FitModelPosition(); - ScaleModel(); - - mModelRoot.SetProperty(Dali::Actor::Property::SENSITIVE, mModelChildrenSensitive); - mModelRoot.SetProperty(Dali::Actor::Property::KEYBOARD_FOCUSABLE, mModelChildrenFocusable); - mModelRoot.SetProperty(Dali::DevelActor::Property::KEYBOARD_FOCUSABLE_CHILDREN, mModelChildrenFocusable); - - Self().Add(mModelRoot); + auto& resources = mModelLoadTask->GetResources(); + auto& scene = mModelLoadTask->GetScene(); + CreateAnimations(scene); + ResetCameraParameters(); + if(!resources.mEnvironmentMaps.empty()) + { + mDefaultDiffuseTexture = resources.mEnvironmentMaps.front().second.mDiffuse; + mDefaultSpecularTexture = resources.mEnvironmentMaps.front().second.mSpecular; + } + UpdateImageBasedLightTexture(); + UpdateImageBasedLightScaleFactor(); Self().SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3(mModelPivot.x, 1.0f - mModelPivot.y, mModelPivot.z)); mModelResourceReady = true; - - Control::SetResourceReady(false); + NotifyResourceReady(); + ResetResourceTask(mModelLoadTask); } -void Model::ScaleModel() +void Model::OnIblDiffuseLoadComplete() { - if(mModelRoot) + mDiffuseTexture = mIblDiffuseLoadTask->GetLoadedTexture(); + ResetResourceTask(mIblDiffuseLoadTask); + mIblDiffuseResourceReady = true; + if(mIblDiffuseResourceReady && mIblSpecularResourceReady) { - float scale = 1.0f; - Vector3 size = Self().GetProperty(Dali::Actor::Property::SIZE); - if(size.x > 0.0f && size.y > 0.0f) - { - scale = MAXFLOAT; - scale = std::min(size.x / mNaturalSize.x, scale); - scale = std::min(size.y / mNaturalSize.y, scale); - } - // Models in glTF and dli are defined as right hand coordinate system. - // DALi uses left hand coordinate system. Scaling negative is for change winding order. - mModelRoot.SetProperty(Dali::Actor::Property::SCALE, Y_DIRECTION * scale); + OnIblLoadComplete(); } } -void Model::FitModelPosition() +void Model::OnIblSpecularLoadComplete() { - if(mModelRoot) + mSpecularTexture = mIblSpecularLoadTask->GetLoadedTexture(); + mSpecularMipmapLevels = mIblSpecularLoadTask->GetMipmapLevels(); + ResetResourceTask(mIblSpecularLoadTask); + mIblSpecularResourceReady = true; + if(mIblDiffuseResourceReady && mIblSpecularResourceReady) { - // Loaded model pivot is not the model center. - mModelRoot.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); - mModelRoot.SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3::ONE - mModelPivot); + OnIblLoadComplete(); } } -void Model::CollectRenderableActor(Actor actor) +void Model::OnIblLoadComplete() { - uint32_t rendererCount = actor.GetRendererCount(); - if(rendererCount) - { - mRenderableActors.push_back(actor); - } - - uint32_t childrenCount = actor.GetChildCount(); - for(uint32_t i = 0; i < childrenCount; ++i) - { - CollectRenderableActor(actor.GetChildAt(i)); - } + UpdateImageBasedLightTexture(); + NotifyResourceReady(); } -void Model::UpdateImageBasedLightTexture() +void Model::ResetResourceTasks() { - Dali::Texture currentDiffuseTexture = (mDiffuseTexture) ? mDiffuseTexture : mSceneDiffuseTexture; - Dali::Texture currentSpecularTexture = (mSpecularTexture) ? mSpecularTexture : mSceneSpecularTexture; - float currentIBLScaleFactor = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor; - if(!currentDiffuseTexture || !currentSpecularTexture) + if(!Dali::Adaptor::IsAvailable()) { - currentDiffuseTexture = mDefaultDiffuseTexture; - currentSpecularTexture = mDefaultSpecularTexture; - currentIBLScaleFactor = Dali::Scene3D::Loader::EnvironmentDefinition::GetDefaultIntensity(); + return; } + ResetResourceTask(mModelLoadTask); + ResetResourceTask(mIblDiffuseLoadTask); + ResetResourceTask(mIblSpecularLoadTask); +} - for(auto&& actor : mRenderableActors) +void Model::ResetResourceTask(IntrusivePtr asyncTask) +{ + if(!asyncTask) { - Actor renderableActor = actor.GetHandle(); - if(renderableActor) - { - uint32_t rendererCount = renderableActor.GetRendererCount(); - for(uint32_t i = 0; i < rendererCount; ++i) - { - Dali::Renderer renderer = renderableActor.GetRendererAt(i); - if(renderer) - { - Dali::TextureSet textures = renderer.GetTextures(); - if(textures) - { - uint32_t textureCount = textures.GetTextureCount(); - // EnvMap requires at least 2 texture, diffuse and specular - if(textureCount > 2u) - { - textures.SetTexture(textureCount - OFFSET_FOR_DIFFUSE_CUBE_TEXTURE, currentDiffuseTexture); - textures.SetTexture(textureCount - OFFSET_FOR_SPECULAR_CUBE_TEXTURE, currentSpecularTexture); - } - } - } - } - renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), currentIBLScaleFactor); - } + return; } + Dali::AsyncTaskManager::Get().RemoveTask(asyncTask); + asyncTask.Reset(); } -void Model::UpdateImageBasedLightScaleFactor() +void Model::NotifyResourceReady() { - if((!mDiffuseTexture || !mSpecularTexture) && - (!mSceneDiffuseTexture || !mSceneSpecularTexture)) + if(!IsResourceReady()) { return; } + Control::SetResourceReady(false); +} + +void Model::CreateModel() +{ + BoundingVolume AABB; + auto& resources = mModelLoadTask->GetResources(); + auto& scene = mModelLoadTask->GetScene(); + auto& resourceChoices = mModelLoadTask->GetResourceChoices(); + Dali::Scene3D::Loader::Transforms xforms{Dali::Scene3D::Loader::MatrixStack{}, Dali::Scene3D::Loader::ViewProjection{}}; + Dali::Scene3D::Loader::NodeDefinition::CreateParams nodeParams{resources, xforms, {}, {}, {}}; - float currentIBLScaleFactor = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor; - for(auto&& actor : mRenderableActors) + // Generate Dali handles from resource bundle. Note that we generate all scene's resouce immediatly. + resources.GenerateResources(); + for(auto iRoot : scene.GetRoots()) { - Actor renderableActor = actor.GetHandle(); - if(renderableActor) + if(auto actor = scene.CreateNodes(iRoot, resourceChoices, nodeParams)) { - renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), currentIBLScaleFactor); + scene.ConfigureSkeletonJoints(iRoot, resources.mSkeletons, actor); + scene.ConfigureSkinningShaders(resources, actor, std::move(nodeParams.mSkinnables)); + ConfigureBlendShapeShaders(resources, scene, actor, std::move(nodeParams.mBlendshapeRequests)); + + scene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables)); + + mModelRoot.Add(actor); } + + AddModelTreeToAABB(AABB, scene, resourceChoices, iRoot, nodeParams, Matrix::IDENTITY); } + + mNaturalSize = AABB.CalculateSize(); + mModelPivot = AABB.CalculatePivot(); + mModelRoot.SetProperty(Dali::Actor::Property::SIZE, mNaturalSize); + Vector3 controlSize = Self().GetProperty(Dali::Actor::Property::SIZE); + if(Dali::EqualsZero(controlSize.x) || Dali::EqualsZero(controlSize.y)) + { + Self().SetProperty(Dali::Actor::Property::SIZE, mNaturalSize); + } + FitModelPosition(); + ScaleModel(); } -void Model::NotifyImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float scaleFactor) +void Model::CreateAnimations(Dali::Scene3D::Loader::SceneDefinition& scene) { - if(mSceneDiffuseTexture != diffuseTexture || mSceneSpecularTexture != specularTexture) + mAnimations.clear(); + if(!mModelLoadTask->GetAnimations().empty()) { - mSceneDiffuseTexture = diffuseTexture; - mSceneSpecularTexture = specularTexture; - mSceneIblScaleFactor = scaleFactor; - UpdateImageBasedLightTexture(); + auto getActor = [&](const Scene3D::Loader::AnimatedProperty& property) + { + if(property.mNodeIndex == Scene3D::Loader::INVALID_INDEX) + { + return mModelRoot.FindChildByName(property.mNodeName); + } + auto* node = scene.GetNode(property.mNodeIndex); + if(node == nullptr) + { + return Dali::Actor(); + } + return mModelRoot.FindChildById(node->mNodeId); + }; + + for(auto&& animation : mModelLoadTask->GetAnimations()) + { + Dali::Animation anim = animation.ReAnimate(getActor); + mAnimations.push_back({animation.GetName(), anim}); + } } } -void Model::NotifyImageBasedLightScaleFactor(float scaleFactor) +void Model::ResetCameraParameters() { - mSceneIblScaleFactor = scaleFactor; - if(mSceneDiffuseTexture && mSceneSpecularTexture) + mCameraParameters.clear(); + if(!mModelLoadTask->GetCameras().empty()) { - UpdateImageBasedLightScaleFactor(); + // Copy camera parameters. + std::copy(mModelLoadTask->GetCameras().begin(), mModelLoadTask->GetCameras().end(), std::back_inserter(mCameraParameters)); } }