/*
- * 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.
#include <dali-toolkit/internal/controls/control/control-data-impl.h>
#include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
#include <dali/devel-api/actors/actor-devel.h>
+#include <dali/integration-api/adaptor-framework/adaptor.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
+#include <dali-scene3d/internal/common/model-cache-manager.h>
#include <dali-scene3d/internal/controls/scene-view/scene-view-impl.h>
+#include <dali-scene3d/internal/light/light-impl.h>
+#include <dali-scene3d/internal/model-components/model-node-impl.h>
#include <dali-scene3d/public-api/controls/model/model.h>
#include <dali-scene3d/public-api/loader/animation-definition.h>
#include <dali-scene3d/public-api/loader/camera-parameters.h>
-#include <dali-scene3d/public-api/loader/cube-map-loader.h>
-#include <dali-scene3d/public-api/loader/dli-loader.h>
-#include <dali-scene3d/public-api/loader/gltf2-loader.h>
#include <dali-scene3d/public-api/loader/light-parameters.h>
#include <dali-scene3d/public-api/loader/load-result.h>
#include <dali-scene3d/public-api/loader/node-definition.h>
#include <dali-scene3d/public-api/loader/scene-definition.h>
-#include <dali-scene3d/public-api/loader/shader-definition-factory.h>
+#include <dali-scene3d/public-api/loader/shader-manager.h>
+#include <dali-scene3d/public-api/model-motion/motion-index/blend-shape-index.h>
using namespace Dali;
{
namespace
{
+/**
+ * Creates control through type registry
+ */
BaseHandle Create()
{
return Scene3D::Model::New(std::string());
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 float SIZE_STEP_CONDITION = 0.1f;
static constexpr bool DEFAULT_MODEL_CHILDREN_SENSITIVE = false;
static constexpr bool DEFAULT_MODEL_CHILDREN_FOCUSABLE = false;
}
}
+void UpdateBlendShapeNodeMapRecursively(Model::BlendShapeModelNodeMap& resultMap, const Scene3D::ModelNode& node)
+{
+ if(!node)
+ {
+ return;
+ }
+ const auto childCount = node.GetChildCount();
+ for(auto i = 0u; i < childCount; ++i)
+ {
+ UpdateBlendShapeNodeMapRecursively(resultMap, Scene3D::ModelNode::DownCast(node.GetChildAt(i)));
+ }
+
+ std::vector<std::string> blendShapeNames;
+ node.RetrieveBlendShapeNames(blendShapeNames);
+ for(const auto& iter : blendShapeNames)
+ {
+ // Append or create new list.
+ resultMap[iter].push_back(node);
+ }
+}
+
+void UpdateShaderRecursively(Scene3D::ModelNode node, Scene3D::Loader::ShaderManagerPtr shaderManager)
+{
+ if(!node)
+ {
+ return;
+ }
+
+ GetImplementation(node).UpdateShader(shaderManager);
+
+ uint32_t childrenCount = node.GetChildCount();
+ for(uint32_t i = 0; i < childrenCount; ++i)
+ {
+ Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i));
+ if(childNode)
+ {
+ UpdateShaderRecursively(childNode, shaderManager);
+ }
+ }
+}
+
+void UpdateShadowMapTextureRecursively(Scene3D::ModelNode node, Dali::Texture shadowMapTexture)
+{
+ if(!node)
+ {
+ return;
+ }
+
+ GetImplementation(node).SetShadowMapTexture(shadowMapTexture);
+
+ uint32_t childrenCount = node.GetChildCount();
+ for(uint32_t i = 0; i < childrenCount; ++i)
+ {
+ Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i));
+ if(childNode)
+ {
+ UpdateShadowMapTextureRecursively(childNode, shadowMapTexture);
+ }
+ }
+}
+
} // anonymous namespace
Model::Model(const std::string& modelUrl, const std::string& resourceDirectoryUrl)
mModelUrl(modelUrl),
mResourceDirectoryUrl(resourceDirectoryUrl),
mModelRoot(),
+ mShaderManager(new Scene3D::Loader::ShaderManager()),
mNaturalSize(Vector3::ZERO),
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),
Model::~Model()
{
ResetResourceTasks();
+
+ if(ModelCacheManager::Get() && !mModelUrl.empty())
+ {
+ ModelCacheManager::Get().UnreferenceModelCache(mModelUrl);
+ }
}
Dali::Scene3D::Model Model::New(const std::string& modelUrl, const std::string& resourceDirectoryUrl)
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;
+ }
+
+ UpdateShaderRecursively(modelNode, mShaderManager);
+
+ if(mShadowMapTexture)
+ {
+ UpdateShadowMapTextureRecursively(modelNode, mShadowMapTexture);
+ }
+
+ if(mIblDiffuseResourceReady && mIblSpecularResourceReady)
+ {
+ UpdateImageBasedLightTexture();
+ UpdateImageBasedLightScaleFactor();
+ }
+
+ if(Self().GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE))
+ {
+ NotifyResourceReady();
+ }
+}
+
+void Model::RemoveModelNode(Scene3D::ModelNode modelNode)
+{
+ if(mModelRoot)
+ {
+ UpdateShaderRecursively(modelNode, nullptr);
+ mModelRoot.Remove(modelNode);
+ }
+}
+
void Model::SetChildrenSensitive(bool enable)
{
if(mModelChildrenSensitive != enable)
void Model::SetImageBasedLightSource(const std::string& diffuseUrl, const std::string& specularUrl, float scaleFactor)
{
bool needIblReset = false;
- bool isOnScene = Self().GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE);
+ bool isOnScene = Self().GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE);
if(mDiffuseIblUrl != diffuseUrl)
{
mDiffuseIblUrl = diffuseUrl;
if(mDiffuseIblUrl.empty())
{
- needIblReset = true;
+ needIblReset = true;
}
else
{
mSpecularIblUrl = specularUrl;
if(mSpecularIblUrl.empty())
{
- needIblReset = true;
+ needIblReset = true;
}
else
{
// 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();
- }
+ ResetResourceTask(mIblDiffuseLoadTask);
+ ResetResourceTask(mIblSpecularLoadTask);
mIblDiffuseDirty = false;
mIblSpecularDirty = false;
{
if(isOnScene && mIblDiffuseDirty)
{
- if(mIblDiffuseLoadTask)
- {
- Dali::AsyncTaskManager::Get().RemoveTask(mIblDiffuseLoadTask);
- mIblDiffuseLoadTask.Reset();
- }
- mIblDiffuseLoadTask = new EnvironmentMapLoadTask(mDiffuseIblUrl, MakeCallback(this, &Model::OnIblDiffuseLoadComplete));
+ ResetResourceTask(mIblDiffuseLoadTask);
+ mIblDiffuseLoadTask = new EnvironmentMapLoadTask(mDiffuseIblUrl, Scene3D::EnvironmentMapType::CUBEMAP, 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));
+ ResetResourceTask(mIblSpecularLoadTask);
+ mIblSpecularLoadTask = new EnvironmentMapLoadTask(mSpecularIblUrl, Scene3D::EnvironmentMapType::CUBEMAP, MakeCallback(this, &Model::OnIblSpecularLoadComplete));
Dali::AsyncTaskManager::Get().AddTask(mIblSpecularLoadTask);
mIblSpecularDirty = false;
}
}
// If diffuse and specular textures are already loaded, emits resource ready signal here.
- if(IsResourceReady())
- {
- Control::SetResourceReady(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)
- {
- mDiffuseTexture = diffuseTexture;
- mSpecularTexture = specularTexture;
- mIblScaleFactor = scaleFactor;
- UpdateImageBasedLightTexture();
- }
+ NotifyResourceReady();
}
void Model::SetImageBasedLightScaleFactor(float scaleFactor)
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);
+}
+
+void Model::RetrieveBlendShapeNames(std::vector<std::string>& blendShapeNames) const
+{
+ blendShapeNames.reserve(blendShapeNames.size() + mBlendShapeModelNodeMap.size());
+ for(const auto& iter : mBlendShapeModelNodeMap)
+ {
+ blendShapeNames.push_back(iter.first);
+ }
+}
+
+void Model::RetrieveModelNodesByBlendShapeName(std::string_view blendShapeName, std::vector<Scene3D::ModelNode>& modelNodes) const
+{
+ auto iter = mBlendShapeModelNodeMap.find(std::string(blendShapeName));
+ if(iter != mBlendShapeModelNodeMap.end())
+ {
+ const auto& modelNodeList = iter->second;
+ modelNodes.reserve(modelNodes.size() + modelNodeList.size());
+ for(const auto& nodeIter : modelNodeList)
+ {
+ modelNodes.push_back(nodeIter);
+ }
+ }
+}
+
+Dali::Animation Model::GenerateMotionDataAnimation(Scene3D::MotionData motionData)
+{
+ Dali::Animation animation;
+
+ // TODO : Need to collect duplicated codes with SetMotionData()
+
+ if(motionData)
+ {
+ const uint32_t motionCount = motionData.GetMotionCount();
+ for(uint32_t i = 0u; i < motionCount; ++i)
+ {
+ auto motionIndex = motionData.GetIndex(i);
+ auto motionValue = motionData.GetValue(i);
+ if(motionIndex && motionValue)
+ {
+ if(motionIndex.GetModelNodeId() != Property::INVALID_KEY)
+ {
+ Scene3D::ModelNode modelNode;
+ if(motionIndex.GetModelNodeId().type == Property::Key::Type::STRING)
+ {
+ modelNode = FindChildModelNodeByName(motionIndex.GetModelNodeId().stringKey);
+ }
+ else if(motionIndex.GetModelNodeId().type == Property::Key::Type::INDEX)
+ {
+ // TODO : Not implement yet.
+ }
+
+ if(modelNode)
+ {
+ KeyFrames keyFrames = motionValue.GetKeyFrames();
+
+ if(keyFrames)
+ {
+ // Try to use index first. If failed, try to use name
+ Property::Index animatedPropertyIndex = motionIndex.GetPropertyIndex(modelNode);
+ if(animatedPropertyIndex != Property::INVALID_INDEX)
+ {
+ if(DALI_UNLIKELY(!animation))
+ {
+ animation = Animation::New(motionData.GetDuration());
+ }
+ animation.AnimateBetween(Dali::Property(modelNode, animatedPropertyIndex), keyFrames);
+ }
+ else
+ {
+ std::string animatedPropertyName = motionIndex.GetPropertyName(modelNode);
+ Dali::Property property(modelNode, animatedPropertyName);
+ if(property.propertyIndex != Property::INVALID_INDEX)
+ {
+ if(DALI_UNLIKELY(!animation))
+ {
+ animation = Animation::New(motionData.GetDuration());
+ }
+ animation.AnimateBetween(property, keyFrames);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ Scene3D::BlendShapeIndex blendShapeIndex = Scene3D::BlendShapeIndex::DownCast(motionIndex);
+ if(blendShapeIndex && blendShapeIndex.GetBlendShapeId().type == Property::Key::Type::STRING)
+ {
+ // Special case : For BlendShapeIndex that doesn't have ModelNodeId and has string BlendShapeId,
+ // we need to animate all kind of blendshapes
+
+ KeyFrames keyFrames = motionValue.GetKeyFrames();
+
+ if(keyFrames)
+ {
+ std::vector<Scene3D::ModelNode> modelNodes;
+ RetrieveModelNodesByBlendShapeName(blendShapeIndex.GetBlendShapeId().stringKey, modelNodes);
+
+ for(auto& modelNode : modelNodes)
+ {
+ // Try to use index first. If failed, try to use name
+ Property::Index animatedPropertyIndex = motionIndex.GetPropertyIndex(modelNode);
+ if(animatedPropertyIndex != Property::INVALID_INDEX)
+ {
+ if(DALI_UNLIKELY(!animation))
+ {
+ animation = Animation::New(motionData.GetDuration());
+ }
+ animation.AnimateBetween(Dali::Property(modelNode, animatedPropertyIndex), keyFrames);
+ }
+ else
+ {
+ std::string animatedPropertyName = motionIndex.GetPropertyName(modelNode);
+ Dali::Property property(modelNode, animatedPropertyName);
+
+ if(property.propertyIndex != Property::INVALID_INDEX)
+ {
+ if(DALI_UNLIKELY(!animation))
+ {
+ animation = Animation::New(motionData.GetDuration());
+ }
+ animation.AnimateBetween(property, keyFrames);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return animation;
+}
+
+void Model::SetMotionData(Scene3D::MotionData motionData)
+{
+ // TODO : Need to collect duplicated codes with GenerateMotionDataAnimation()
+
+ if(motionData)
+ {
+ const uint32_t motionCount = motionData.GetMotionCount();
+ for(uint32_t i = 0u; i < motionCount; ++i)
+ {
+ auto motionIndex = motionData.GetIndex(i);
+ auto motionValue = motionData.GetValue(i);
+ if(motionIndex && motionValue)
+ {
+ if(motionIndex.GetModelNodeId() != Property::INVALID_KEY)
+ {
+ Scene3D::ModelNode modelNode;
+ if(motionIndex.GetModelNodeId().type == Property::Key::Type::STRING)
+ {
+ modelNode = FindChildModelNodeByName(motionIndex.GetModelNodeId().stringKey);
+ }
+ else if(motionIndex.GetModelNodeId().type == Property::Key::Type::INDEX)
+ {
+ // TODO : Not implement yet.
+ }
+
+ if(modelNode)
+ {
+ Property::Value value = motionValue.GetPropertyValue();
+
+ if(value.GetType() != Property::Type::NONE)
+ {
+ // Try to use index first. If failed, try to use name
+ Property::Index propertyIndex = motionIndex.GetPropertyIndex(modelNode);
+ if(propertyIndex != Property::INVALID_INDEX)
+ {
+ modelNode.SetProperty(propertyIndex, value);
+ }
+ else
+ {
+ std::string propertyName = motionIndex.GetPropertyName(modelNode);
+ Dali::Property property(modelNode, propertyName);
+ if(property.propertyIndex != Property::INVALID_INDEX)
+ {
+ modelNode.SetProperty(property.propertyIndex, value);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ Scene3D::BlendShapeIndex blendShapeIndex = Scene3D::BlendShapeIndex::DownCast(motionIndex);
+ if(blendShapeIndex && blendShapeIndex.GetBlendShapeId().type == Property::Key::Type::STRING)
+ {
+ // Special case : For BlendShapeIndex that doesn't have ModelNodeId and has string BlendShapeId,
+ // we need to animate all kind of blendshapes
+
+ Property::Value value = motionValue.GetPropertyValue();
+
+ if(value.GetType() != Property::Type::NONE)
+ {
+ std::vector<Scene3D::ModelNode> modelNodes;
+ RetrieveModelNodesByBlendShapeName(blendShapeIndex.GetBlendShapeId().stringKey, modelNodes);
+
+ for(auto& modelNode : modelNodes)
+ {
+ // Try to use index first. If failed, try to use name
+ Property::Index propertyIndex = motionIndex.GetPropertyIndex(modelNode);
+ if(propertyIndex != Property::INVALID_INDEX)
+ {
+ modelNode.SetProperty(propertyIndex, value);
+ }
+ else
+ {
+ std::string propertyName = motionIndex.GetPropertyName(modelNode);
+ Dali::Property property(modelNode, propertyName);
+ if(property.propertyIndex != Property::INVALID_INDEX)
+ {
+ modelNode.SetProperty(property.propertyIndex, value);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
///////////////////////////////////////////////////////////
//
// Private methods
void Model::OnSceneConnection(int depth)
{
- 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())
- {
- SetImageBasedLightSource(mDiffuseIblUrl, mSpecularIblUrl, mIblScaleFactor);
- }
-
Actor parent = Self().GetParent();
while(parent)
{
+ // If this Model has parent SceneView and the its ShaderManager is same with privious ShaderManager,
+ // this Model don't need to update shader.
Scene3D::SceneView sceneView = Scene3D::SceneView::DownCast(parent);
if(sceneView)
{
- GetImpl(sceneView).RegisterSceneItem(this);
mParentSceneView = sceneView;
+ GetImpl(sceneView).RegisterSceneItem(this);
+ Scene3D::Loader::ShaderManagerPtr shaderManager = GetImpl(sceneView).GetShaderManager();
+ if(mShaderManager != shaderManager)
+ {
+ mShaderManager = shaderManager;
+ UpdateShaderRecursively(mModelRoot, mShaderManager);
+ }
break;
}
parent = parent.GetParent();
}
+ // Model can be added on Dali::Scene directly without SceneView.
+ // So, Model's mShaderManager and shaders of child ModelNodes are needed to be reset when this Model has not parent SceneView.
+ Scene3D::SceneView parentSceneView = mParentSceneView.GetHandle();
+ if(!parentSceneView)
+ {
+ mShaderManager = new Dali::Scene3D::Loader::ShaderManager();
+ UpdateShaderRecursively(mModelRoot, mShaderManager);
+ }
+
+ 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())
+ {
+ SetImageBasedLightSource(mDiffuseIblUrl, mSpecularIblUrl, mIblScaleFactor);
+ }
+
+ NotifyResourceReady();
+
+ mSizeNotification = Self().AddPropertyNotification(Actor::Property::SIZE, StepCondition(SIZE_STEP_CONDITION));
+ mSizeNotification.NotifySignal().Connect(this, &Model::OnSizeNotification);
Control::OnSceneConnection(depth);
}
void Model::OnSceneDisconnection()
{
+ // If mParentSceneView is still onScene, that means this model
+ // is disconnected from mParentSceneView's sub tree.
+ // So, Unregister this Model from SceneView.
Scene3D::SceneView sceneView = mParentSceneView.GetHandle();
- if(sceneView)
+ if(sceneView && sceneView.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE))
{
GetImpl(sceneView).UnregisterSceneItem(this);
mParentSceneView.Reset();
}
+
+ mSizeNotification.NotifySignal().Disconnect(this, &Model::OnSizeNotification);
+ Self().RemovePropertyNotification(mSizeNotification);
+ mSizeNotification.Reset();
+
Control::OnSceneDisconnection();
}
+void Model::OnSizeSet(const Vector3& size)
+{
+ ScaleModel(false);
+}
+
Vector3 Model::GetNaturalSize()
{
if(!mModelRoot)
void Model::OnRelayout(const Vector2& size, RelayoutContainer& container)
{
Control::OnRelayout(size, container);
- ScaleModel();
+ ScaleModel(false);
}
bool Model::IsResourceReady() const
return mModelResourceReady && mIblDiffuseResourceReady && mIblSpecularResourceReady;
}
-void Model::ScaleModel()
+void Model::CreateModelRoot()
{
- if(mModelRoot)
+ 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::ScaleModel(bool useCurrentSize)
+{
+ if(!mModelRoot)
{
- float scale = 1.0f;
- Vector3 size = Self().GetProperty<Vector3>(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);
+ return;
+ }
+
+ float scale = 1.0f;
+ Vector3 size = (useCurrentSize) ? Self().GetCurrentProperty<Vector3>(Dali::Actor::Property::SIZE) : Self().GetProperty<Vector3>(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);
}
void Model::FitModelPosition()
{
- if(mModelRoot)
+ if(!mModelRoot)
{
- // 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);
+ 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);
}
-void Model::CollectRenderableActor(Actor actor)
+void Model::UpdateImageBasedLightTextureRecursively(Scene3D::ModelNode node, Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels)
{
- uint32_t rendererCount = actor.GetRendererCount();
- if(rendererCount)
+ if(!node)
{
- mRenderableActors.push_back(actor);
+ return;
}
- uint32_t childrenCount = actor.GetChildCount();
+ GetImplementation(node).SetImageBasedLightTexture(diffuseTexture, specularTexture, iblScaleFactor, specularMipmapLevels);
+ uint32_t childrenCount = node.GetChildCount();
for(uint32_t i = 0; i < childrenCount; ++i)
{
- CollectRenderableActor(actor.GetChildAt(i));
+ Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i));
+ if(!childNode)
+ {
+ continue;
+ }
+ UpdateImageBasedLightTextureRecursively(childNode, diffuseTexture, specularTexture, iblScaleFactor, specularMipmapLevels);
}
}
-void Model::UpdateImageBasedLightTexture()
+void Model::UpdateImageBasedLightScaleFactorRecursively(Scene3D::ModelNode node, float iblScaleFactor)
{
- Dali::Texture currentDiffuseTexture = (mDiffuseTexture && mSpecularTexture) ? mDiffuseTexture : mSceneDiffuseTexture;
- Dali::Texture currentSpecularTexture = (mDiffuseTexture && mSpecularTexture) ? mSpecularTexture : mSceneSpecularTexture;
- float currentIblScaleFactor = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
-
- if(!currentDiffuseTexture || !currentSpecularTexture)
+ if(!node)
{
- currentDiffuseTexture = mDefaultDiffuseTexture;
- currentSpecularTexture = mDefaultSpecularTexture;
- currentIblScaleFactor = Dali::Scene3D::Loader::EnvironmentDefinition::GetDefaultIntensity();
+ return;
}
- for(auto&& actor : mRenderableActors)
+ GetImplementation(node).SetImageBasedLightScaleFactor(iblScaleFactor);
+
+ uint32_t childrenCount = node.GetChildCount();
+ for(uint32_t i = 0; i < childrenCount; ++i)
{
- Actor renderableActor = actor.GetHandle();
- if(renderableActor)
+ Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i));
+ if(!childNode)
{
- 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);
+ continue;
}
+ UpdateImageBasedLightScaleFactorRecursively(childNode, iblScaleFactor);
+ }
+}
+
+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;
}
+
+ UpdateImageBasedLightTextureRecursively(mModelRoot, currentDiffuseTexture, currentSpecularTexture, currentIblScaleFactor, currentIblSpecularMipmapLevels);
}
void Model::UpdateImageBasedLightScaleFactor()
}
float currentIblScaleFactor = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor;
- for(auto&& actor : mRenderableActors)
+ UpdateImageBasedLightScaleFactorRecursively(mModelRoot, currentIblScaleFactor);
+}
+
+void Model::ApplyCameraTransform(Dali::CameraActor camera) const
+{
+ Vector3 selfPosition = Self().GetProperty<Vector3>(Actor::Property::POSITION);
+ Quaternion selfOrientation = Self().GetProperty<Quaternion>(Actor::Property::ORIENTATION);
+ Vector3 selfScale = Self().GetProperty<Vector3>(Actor::Property::SCALE);
+
+ Vector3 cameraPosition = camera.GetProperty<Vector3>(Actor::Property::POSITION);
+ Quaternion cameraOrientation = camera.GetProperty<Quaternion>(Actor::Property::ORIENTATION);
+ Vector3 cameraScale = camera.GetProperty<Vector3>(Actor::Property::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.
+ if(!Dali::Equals(Y_DIRECTION.Dot(Vector3::YAXIS), 1.0f))
{
- Actor renderableActor = actor.GetHandle();
- if(renderableActor)
- {
- renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), currentIblScaleFactor);
- }
+ // Reflect by XZ plane
+ cameraPosition.y = -cameraPosition.y;
+ Quaternion yDirectionQuaternion;
+ yDirectionQuaternion.mVector = Vector3::YAXIS;
+ // Reflect orientation
+ cameraOrientation = yDirectionQuaternion * cameraOrientation * yDirectionQuaternion;
+ }
+
+ Vector3 resultPosition;
+ Quaternion resultOrientation;
+ Vector3 resultScale;
+
+ 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);
+
+ camera.SetProperty(Actor::Property::POSITION, resultPosition);
+ camera.SetProperty(Actor::Property::ORIENTATION, resultOrientation);
+ camera.SetProperty(Actor::Property::SCALE, resultScale);
+}
+
+void Model::NotifyShadowMapTexture(Dali::Texture shadowMapTexture)
+{
+ if(mShadowMapTexture != shadowMapTexture)
+ {
+ mShadowMapTexture = shadowMapTexture;
+ UpdateShadowMapTextureRecursively(mModelRoot, mShadowMapTexture);
}
}
-void Model::NotifyImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float scaleFactor)
+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;
+ mSceneDiffuseTexture = diffuseTexture;
+ mSceneSpecularTexture = specularTexture;
+ mSceneIblScaleFactor = scaleFactor;
+ mSceneSpecularMipmapLevels = specularMipmapLevels;
// If Model IBL is not set, use SceneView's IBL.
if(!mDiffuseTexture || !mSpecularTexture)
{
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))
+ if(ModelCacheManager::Get() && !mModelUrl.empty())
{
- 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);
+ ModelCacheManager::Get().UnreferenceModelCache(mModelUrl);
}
- AddModelTreeToAABB(AABB, *scene, mModelLoadTask->mResourceChoices, iRoot, nodeParams, Matrix::IDENTITY);
- rootCount++;
+ return;
}
- if(!resources->mEnvironmentMaps.empty())
+ if(!mModelRoot)
{
- mDefaultDiffuseTexture = resources->mEnvironmentMaps.front().second.mDiffuse;
- mDefaultSpecularTexture = resources->mEnvironmentMaps.front().second.mSpecular;
+ CreateModelRoot();
}
+ CreateModel();
- if(!mModelLoadTask->mAnimations.empty())
+ auto& resources = mModelLoadTask->GetResources();
+ auto& scene = mModelLoadTask->GetScene();
+ CreateAnimations(scene);
+ ResetCameraParameters();
+ if(!resources.mEnvironmentMaps.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});
- }
+ mDefaultDiffuseTexture = resources.mEnvironmentMaps.front().second.mDiffuse;
+ mDefaultSpecularTexture = resources.mEnvironmentMaps.front().second.mSpecular;
}
- 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))
+ if(mShadowMapTexture)
{
- Self().SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
+ UpdateShadowMapTextureRecursively(mModelRoot, mShadowMapTexture);
}
-
- 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);
-
+ UpdateImageBasedLightTexture();
+ UpdateImageBasedLightScaleFactor();
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();
+ NotifyResourceReady();
+ ResetResourceTask(mModelLoadTask);
}
void Model::OnIblDiffuseLoadComplete()
{
- mDiffuseTexture = (mIblDiffuseLoadTask->HasSucceeded()) ? mIblDiffuseLoadTask->GetEnvironmentMap().CreateTexture() : Texture();
+ mDiffuseTexture = mIblDiffuseLoadTask->GetLoadedTexture();
+ ResetResourceTask(mIblDiffuseLoadTask);
mIblDiffuseResourceReady = true;
if(mIblDiffuseResourceReady && mIblSpecularResourceReady)
{
OnIblLoadComplete();
}
- mIblDiffuseLoadTask.Reset();
}
void Model::OnIblSpecularLoadComplete()
{
- mSpecularTexture = (mIblSpecularLoadTask->HasSucceeded()) ? mIblSpecularLoadTask->GetEnvironmentMap().CreateTexture() : Texture();
+ mSpecularTexture = mIblSpecularLoadTask->GetLoadedTexture();
+ mSpecularMipmapLevels = mIblSpecularLoadTask->GetMipmapLevels();
+ ResetResourceTask(mIblSpecularLoadTask);
mIblSpecularResourceReady = true;
if(mIblDiffuseResourceReady && mIblSpecularResourceReady)
{
OnIblLoadComplete();
}
- mIblSpecularLoadTask.Reset();
}
void Model::OnIblLoadComplete()
{
UpdateImageBasedLightTexture();
+ NotifyResourceReady();
+}
+
+void Model::OnSizeNotification(Dali::PropertyNotification& source)
+{
+ ScaleModel(true);
+}
- if(IsResourceReady())
+void Model::ResetResourceTasks()
+{
+ if(!Dali::Adaptor::IsAvailable())
{
- Control::SetResourceReady(false);
+ return;
}
+ ResetResourceTask(mModelLoadTask);
+ ResetResourceTask(mIblDiffuseLoadTask);
+ ResetResourceTask(mIblSpecularLoadTask);
}
-void Model::ResetResourceTasks()
+void Model::ResetResourceTask(IntrusivePtr<AsyncTask> asyncTask)
{
- if(Dali::Adaptor::IsAvailable())
+ if(!asyncTask)
{
- if(mModelLoadTask)
- {
- Dali::AsyncTaskManager::Get().RemoveTask(mModelLoadTask);
- mModelLoadTask.Reset();
- }
- if(mIblDiffuseLoadTask)
+ return;
+ }
+ Dali::AsyncTaskManager::Get().RemoveTask(asyncTask);
+ asyncTask.Reset();
+}
+
+void Model::NotifyResourceReady()
+{
+ if(!IsResourceReady())
+ {
+ return;
+ }
+ Control::SetResourceReady();
+}
+
+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, mShaderManager, {}, {}, {}};
+
+ // Generate Dali handles from resource bundle. Note that we generate all scene's resouce immediatly.
+ resources.GenerateResources();
+ for(auto iRoot : scene.GetRoots())
+ {
+ if(auto modelNode = scene.CreateNodes(iRoot, resourceChoices, nodeParams))
{
- Dali::AsyncTaskManager::Get().RemoveTask(mIblDiffuseLoadTask);
- mIblDiffuseLoadTask.Reset();
+ scene.ConfigureSkinningShaders(resources, modelNode, std::move(nodeParams.mSkinnables));
+ ConfigureBlendShapeShaders(resources, scene, modelNode, std::move(nodeParams.mBlendshapeRequests));
+
+ scene.ApplyConstraints(modelNode, std::move(nodeParams.mConstrainables));
+
+ mModelRoot.Add(modelNode);
}
- if(mIblSpecularLoadTask)
+
+ AddModelTreeToAABB(AABB, scene, resourceChoices, iRoot, nodeParams, Matrix::IDENTITY);
+ }
+
+ UpdateBlendShapeNodeMap();
+
+ 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(false);
+}
+
+void Model::CreateAnimations(Dali::Scene3D::Loader::SceneDefinition& scene)
+{
+ mAnimations.clear();
+ if(!mModelLoadTask->GetAnimations().empty())
+ {
+ 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::AsyncTaskManager::Get().RemoveTask(mIblSpecularLoadTask);
- mIblSpecularLoadTask.Reset();
+ Dali::Animation anim = animation.ReAnimate(getActor);
+ mAnimations.push_back({animation.GetName(), anim});
}
}
}
+void Model::ResetCameraParameters()
+{
+ mCameraParameters.clear();
+ if(!mModelLoadTask->GetCameras().empty())
+ {
+ // Copy camera parameters.
+ std::copy(mModelLoadTask->GetCameras().begin(), mModelLoadTask->GetCameras().end(), std::back_inserter(mCameraParameters));
+ }
+}
+
+void Model::UpdateBlendShapeNodeMap()
+{
+ // Remove privous node map
+ mBlendShapeModelNodeMap.clear();
+
+ UpdateBlendShapeNodeMapRecursively(mBlendShapeModelNodeMap, mModelRoot);
+}
+
} // namespace Internal
} // namespace Scene3D
} // namespace Dali