#include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
#include <dali/devel-api/actors/actor-devel.h>
#include <dali/integration-api/debug.h>
+#include <dali/public-api/math/math-utils.h>
#include <dali/public-api/object/type-registry-helper.h>
#include <dali/public-api/object/type-registry.h>
+#include <dali/integration-api/adaptor-framework/adaptor.h>
#include <filesystem>
// INTERNAL INCLUDES
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()
for(uint32_t i = 0; i < 3; ++i)
{
// To avoid divid by zero
- if(pointMin[i] == pointMax[i])
+ if(Dali::Equals(pointMin[i], pointMax[i]))
{
pivot[i] = 0.5f;
}
mModelChildrenSensitive(DEFAULT_MODEL_CHILDREN_SENSITIVE),
mModelChildrenFocusable(DEFAULT_MODEL_CHILDREN_FOCUSABLE),
mModelResourceReady(false),
- mIBLResourceReady(true)
+ mIblDiffuseResourceReady(true),
+ mIblSpecularResourceReady(true),
+ mIblDiffuseDirty(false),
+ mIblSpecularDirty(false)
{
}
Model::~Model()
{
+ ResetResourceTasks();
}
Dali::Scene3D::Model Model::New(const std::string& modelUrl, const std::string& resourceDirectoryUrl)
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<bool>(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(mSpecularIblUrl != specularUrl)
+ {
+ mSpecularIblUrl = specularUrl;
+ if(mSpecularIblUrl.empty())
+ {
+ needIblReset = true;
+ }
+ else
+ {
+ mIblSpecularDirty = true;
+ mIblSpecularResourceReady = false;
+ }
+ }
+
+ // If one or both of diffuse url and specular url are empty,
+ // we don't need to request to load texture.
+ if(needIblReset)
+ {
+ if(mIblDiffuseLoadTask)
+ {
+ Dali::AsyncTaskManager::Get().RemoveTask(mIblDiffuseLoadTask);
+ mIblDiffuseLoadTask.Reset();
+ }
+
+ if(mIblSpecularLoadTask)
+ {
+ Dali::AsyncTaskManager::Get().RemoveTask(mIblSpecularLoadTask);
+ mIblSpecularLoadTask.Reset();
+ }
+
+ mIblDiffuseDirty = false;
+ mIblSpecularDirty = false;
+ mIblDiffuseResourceReady = true;
+ mIblSpecularResourceReady = true;
+
+ mDiffuseTexture.Reset();
+ mSpecularTexture.Reset();
+ UpdateImageBasedLightTexture();
+ }
+ else
+ {
+ if(isOnScene && mIblDiffuseDirty)
+ {
+ if(mIblDiffuseLoadTask)
+ {
+ Dali::AsyncTaskManager::Get().RemoveTask(mIblDiffuseLoadTask);
+ mIblDiffuseLoadTask.Reset();
+ }
+ mIblDiffuseLoadTask = new EnvironmentMapLoadTask(mDiffuseIblUrl, MakeCallback(this, &Model::OnIblDiffuseLoadComplete));
+ Dali::AsyncTaskManager::Get().AddTask(mIblDiffuseLoadTask);
+ mIblDiffuseDirty = false;
+ }
+
+ if(isOnScene && mIblSpecularDirty)
+ {
+ if(mIblSpecularLoadTask)
+ {
+ Dali::AsyncTaskManager::Get().RemoveTask(mIblSpecularLoadTask);
+ mIblSpecularLoadTask.Reset();
+ }
+ mIblSpecularLoadTask = new EnvironmentMapLoadTask(mSpecularIblUrl, 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.
if(IsResourceReady())
{
- SetResourceReady(false);
+ Control::SetResourceReady(false);
}
}
void Model::OnSceneConnection(int depth)
{
- if(!mModelRoot)
+ if(!mModelLoadTask && !mModelRoot)
+ {
+ Scene3D::Loader::InitializeGltfLoader();
+ 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();
{
if(!mModelRoot)
{
- LoadModel();
+ DALI_LOG_ERROR("Model is still not loaded.\n");
+ return Vector3::ZERO;
}
return mNaturalSize;
bool Model::IsResourceReady() const
{
- return mModelResourceReady && mIBLResourceReady;
-}
-
-void Model::LoadModel()
-{
- std::filesystem::path modelUrl(mModelUrl);
- if(mResourceDirectoryUrl.empty())
- {
- mResourceDirectoryUrl = std::string(modelUrl.parent_path()) + "/";
- }
- 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<Dali::Scene3D::Loader::AnimationGroupDefinition> animGroups;
- std::vector<Dali::Scene3D::Loader::CameraParameters> cameraParameters;
- std::vector<Dali::Scene3D::Loader::LightParameters> lights;
-
- std::vector<Dali::Scene3D::Loader::AnimationDefinition> animations;
- animations.clear();
-
- Dali::Scene3D::Loader::LoadResult output{resources, scene, animations, animGroups, cameraParameters, lights};
-
- if(extension == DLI_EXTENSION)
- {
- 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))
- {
- Dali::Scene3D::Loader::ExceptionFlinger(ASSERT_LOCATION) << "Failed to load scene from '" << mModelUrl << "': " << loader.GetParseError();
- }
- }
- else if(extension == GLTF_EXTENSION)
- {
- Dali::Scene3D::Loader::ShaderDefinitionFactory sdf;
- sdf.SetResources(resources);
- Dali::Scene3D::Loader::LoadGltfScene(mModelUrl, sdf, output);
-
- resources.mEnvironmentMaps.push_back({});
- }
- else
- {
- DALI_LOG_ERROR("Unsupported model type.\n");
- }
-
- 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;
-
- mModelRoot = Actor::New();
-
- BoundingVolume AABB;
- for(auto iRoot : scene.GetRoots())
- {
- auto resourceRefs = resources.CreateRefCounter();
- scene.CountResourceRefs(iRoot, choices, resourceRefs);
- resources.CountEnvironmentReferences(resourceRefs);
-
- resources.LoadResources(resourceRefs, pathProvider);
-
- // 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;
- }
-
- 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));
-
- scene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
-
- mModelRoot.Add(actor);
- }
-
- AddModelTreeToAABB(AABB, scene, choices, iRoot, nodeParams, Matrix::IDENTITY);
- }
-
- if(!resources.mEnvironmentMaps.empty())
- {
- mDefaultDiffuseTexture = resources.mEnvironmentMaps.front().second.mDiffuse;
- mDefaultSpecularTexture = resources.mEnvironmentMaps.front().second.mSpecular;
- }
-
- if(!animations.empty())
- {
- auto getActor = [&](const std::string& name) {
- return mModelRoot.FindChildByName(name);
- };
-
- mAnimations.clear();
- for(auto&& animation : animations)
- {
- Dali::Animation anim = animation.ReAnimate(getActor);
-
- mAnimations.push_back({animation.mName, anim});
- }
- }
-
- mRenderableActors.clear();
- CollectRenderableActor(mModelRoot);
- UpdateImageBasedLightTexture();
- UpdateImageBasedLightScaleFactor();
-
- mNaturalSize = AABB.CalculateSize();
- mModelPivot = AABB.CalculatePivot();
- mModelRoot.SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
- Vector3 controlSize = Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
- if(controlSize.x == 0.0f || controlSize.y == 0.0f)
- {
- Self().SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
- }
-
- 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);
-
- Self().SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3(mModelPivot.x, 1.0f - mModelPivot.y, mModelPivot.z));
-
- mModelResourceReady = true;
-
- Control::SetResourceReady(false);
+ return mModelResourceReady && mIblDiffuseResourceReady && mIblSpecularResourceReady;
}
void Model::ScaleModel()
void Model::UpdateImageBasedLightTexture()
{
- Dali::Texture currentDiffuseTexture = (mDiffuseTexture) ? mDiffuseTexture : mSceneDiffuseTexture;
- Dali::Texture currentSpecularTexture = (mSpecularTexture) ? mSpecularTexture : mSceneSpecularTexture;
- float currentIBLScaleFactor = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
+ Dali::Texture currentDiffuseTexture = (mDiffuseTexture && mSpecularTexture) ? mDiffuseTexture : mSceneDiffuseTexture;
+ Dali::Texture currentSpecularTexture = (mDiffuseTexture && mSpecularTexture) ? mSpecularTexture : mSceneSpecularTexture;
+ float currentIblScaleFactor = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
+
if(!currentDiffuseTexture || !currentSpecularTexture)
{
currentDiffuseTexture = mDefaultDiffuseTexture;
currentSpecularTexture = mDefaultSpecularTexture;
- currentIBLScaleFactor = Dali::Scene3D::Loader::EnvironmentDefinition::GetDefaultIntensity();
+ currentIblScaleFactor = Dali::Scene3D::Loader::EnvironmentDefinition::GetDefaultIntensity();
}
for(auto&& actor : mRenderableActors)
}
}
}
- renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), currentIBLScaleFactor);
+ renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), currentIblScaleFactor);
}
}
}
return;
}
- float currentIBLScaleFactor = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
+ float currentIblScaleFactor = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
for(auto&& actor : mRenderableActors)
{
Actor renderableActor = actor.GetHandle();
if(renderableActor)
{
- renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), currentIBLScaleFactor);
+ renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), currentIblScaleFactor);
}
}
}
mSceneDiffuseTexture = diffuseTexture;
mSceneSpecularTexture = specularTexture;
mSceneIblScaleFactor = scaleFactor;
- UpdateImageBasedLightTexture();
+ // If Model IBL is not set, use SceneView's IBL.
+ if(!mDiffuseTexture || !mSpecularTexture)
+ {
+ UpdateImageBasedLightTexture();
+ }
}
}
}
}
+void Model::OnModelLoadComplete()
+{
+ if(!mModelLoadTask->HasSucceeded())
+ {
+ ResetResourceTasks();
+ return;
+ }
+
+ mModelRoot = Actor::New();
+ mModelRoot.SetProperty(Actor::Property::COLOR_MODE, ColorMode::USE_OWN_MULTIPLY_PARENT_COLOR);
+
+ BoundingVolume AABB;
+ auto* resources = &(mModelLoadTask->mResources);
+ auto* scene = &(mModelLoadTask->mScene);
+ Dali::Scene3D::Loader::Transforms xforms{Dali::Scene3D::Loader::MatrixStack{}, Dali::Scene3D::Loader::ViewProjection{}};
+ Dali::Scene3D::Loader::NodeDefinition::CreateParams nodeParams{*resources, xforms, {}, {}, {}};
+ uint32_t rootCount = 0u;
+ for(auto iRoot : scene->GetRoots())
+ {
+ resources->GenerateResources(mModelLoadTask->mResourceRefCounts[rootCount]);
+
+ if(auto actor = scene->CreateNodes(iRoot, mModelLoadTask->mResourceChoices, nodeParams))
+ {
+ 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, mModelLoadTask->mResourceChoices, iRoot, nodeParams, Matrix::IDENTITY);
+ rootCount++;
+ }
+
+ if(!resources->mEnvironmentMaps.empty())
+ {
+ mDefaultDiffuseTexture = resources->mEnvironmentMaps.front().second.mDiffuse;
+ mDefaultSpecularTexture = resources->mEnvironmentMaps.front().second.mSpecular;
+ }
+
+ if(!mModelLoadTask->mAnimations.empty())
+ {
+ auto getActor = [&](const Scene3D::Loader::AnimatedProperty& property)
+ {
+ Dali::Actor actor;
+ if(property.mNodeIndex != Scene3D::Loader::INVALID_INDEX)
+ {
+ auto* node = scene->GetNode(property.mNodeIndex);
+ if(node != nullptr)
+ {
+ actor = mModelRoot.FindChildById(node->mNodeId);
+ }
+ }
+ else
+ {
+ actor = mModelRoot.FindChildByName(property.mNodeName);
+ }
+ return actor;
+ };
+
+ mAnimations.clear();
+ for(auto&& animation : mModelLoadTask->mAnimations)
+ {
+ Dali::Animation anim = animation.ReAnimate(getActor);
+
+ mAnimations.push_back({animation.mName, anim});
+ }
+ }
+
+ mRenderableActors.clear();
+ CollectRenderableActor(mModelRoot);
+
+ UpdateImageBasedLightTexture();
+ UpdateImageBasedLightScaleFactor();
+
+ mNaturalSize = AABB.CalculateSize();
+ mModelPivot = AABB.CalculatePivot();
+ mModelRoot.SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
+ Vector3 controlSize = Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
+ if(Dali::EqualsZero(controlSize.x) || Dali::EqualsZero(controlSize.y))
+ {
+ Self().SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
+ }
+
+ 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);
+
+ Self().SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3(mModelPivot.x, 1.0f - mModelPivot.y, mModelPivot.z));
+
+ mModelResourceReady = true;
+
+ if(IsResourceReady())
+ {
+ Control::SetResourceReady(false);
+ }
+ mModelLoadTask.Reset();
+}
+
+void Model::OnIblDiffuseLoadComplete()
+{
+ mDiffuseTexture = (mIblDiffuseLoadTask->HasSucceeded()) ? mIblDiffuseLoadTask->GetEnvironmentMap().CreateTexture() : Texture();
+ mIblDiffuseResourceReady = true;
+ if(mIblDiffuseResourceReady && mIblSpecularResourceReady)
+ {
+ OnIblLoadComplete();
+ }
+ mIblDiffuseLoadTask.Reset();
+}
+
+void Model::OnIblSpecularLoadComplete()
+{
+ mSpecularTexture = (mIblSpecularLoadTask->HasSucceeded()) ? mIblSpecularLoadTask->GetEnvironmentMap().CreateTexture() : Texture();
+ mIblSpecularResourceReady = true;
+ if(mIblDiffuseResourceReady && mIblSpecularResourceReady)
+ {
+ OnIblLoadComplete();
+ }
+ mIblSpecularLoadTask.Reset();
+}
+
+void Model::OnIblLoadComplete()
+{
+ UpdateImageBasedLightTexture();
+
+ if(IsResourceReady())
+ {
+ Control::SetResourceReady(false);
+ }
+}
+
+void Model::ResetResourceTasks()
+{
+ if(Dali::Adaptor::IsAvailable())
+ {
+ if(mModelLoadTask)
+ {
+ Dali::AsyncTaskManager::Get().RemoveTask(mModelLoadTask);
+ mModelLoadTask.Reset();
+ }
+ if(mIblDiffuseLoadTask)
+ {
+ Dali::AsyncTaskManager::Get().RemoveTask(mIblDiffuseLoadTask);
+ mIblDiffuseLoadTask.Reset();
+ }
+ if(mIblSpecularLoadTask)
+ {
+ Dali::AsyncTaskManager::Get().RemoveTask(mIblSpecularLoadTask);
+ mIblSpecularLoadTask.Reset();
+ }
+ }
+}
+
} // namespace Internal
} // namespace Scene3D
} // namespace Dali