X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dali-scene3d%2Finternal%2Fcontrols%2Fmodel%2Fmodel-impl.cpp;h=f012aa6ac9ce8668513f6801add26c9a90637705;hb=HEAD;hp=e5d5e78c1c90789fa1bc3df2a8cea9bb8cbe86d0;hpb=0e37670cba44c24d9b5775e20e36007412fbc97d;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 e5d5e78..f012aa6 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) 2023 Samsung Electronics Co., Ltd. + * Copyright (c) 2024 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. @@ -31,18 +31,22 @@ #include // INTERNAL INCLUDES +#include +#include #include +#include +#include +#include #include #include #include -#include -#include #include #include #include #include -#include - +#include +#include +#include using namespace Dali; namespace Dali @@ -53,6 +57,9 @@ namespace Internal { namespace { +/** + * Creates control through type registry + */ BaseHandle Create() { return Scene3D::Model::New(std::string()); @@ -62,10 +69,8 @@ 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 float SIZE_STEP_CONDITION = 0.1f; static constexpr bool DEFAULT_MODEL_CHILDREN_SENSITIVE = false; static constexpr bool DEFAULT_MODEL_CHILDREN_FOCUSABLE = false; @@ -169,6 +174,77 @@ void AddModelTreeToAABB(BoundingVolume& AABB, const Dali::Scene3D::Loader::Scene } } +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 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); + } + } +} + +void ResetResourceTask(IntrusivePtr&& asyncTask) +{ + if(!asyncTask) + { + return; + } + Dali::AsyncTaskManager::Get().RemoveTask(asyncTask); + asyncTask.Reset(); +} + } // anonymous namespace Model::Model(const std::string& modelUrl, const std::string& resourceDirectoryUrl) @@ -176,23 +252,33 @@ Model::Model(const std::string& modelUrl, const std::string& resourceDirectoryUr 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), mIblDiffuseResourceReady(true), mIblSpecularResourceReady(true), mIblDiffuseDirty(false), - mIblSpecularDirty(false) + mIblSpecularDirty(false), + mIsShadowCasting(true), + mIsShadowReceiving(true) { } 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) @@ -208,11 +294,88 @@ 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; + } + + UpdateShaderRecursively(modelNode, mShaderManager); + + if(mShadowMapTexture) + { + UpdateShadowMapTextureRecursively(modelNode, mShadowMapTexture); + } + + if(mIblDiffuseResourceReady && mIblSpecularResourceReady) + { + UpdateImageBasedLightTexture(); + UpdateImageBasedLightScaleFactor(); + } + + GetImplementation(modelNode).SetRootModel(this); + + // If model has a collider mesh set, add it to the container + if(modelNode.HasColliderMesh()) + { + RegisterColliderMesh(modelNode); + Scene3D::ColliderMeshProcessor::Get().ColliderMeshChanged(Scene3D::Model::DownCast(Self())); + } + + if(Self().GetProperty(Dali::Actor::Property::CONNECTED_TO_SCENE)) + { + NotifyResourceReady(); + } +} + +void Model::RegisterColliderMesh(Scene3D::ModelNode& modelNode) +{ + mColliderMeshes[modelNode.GetProperty(Actor::Property::ID)] = modelNode; + + // Add processor + Scene3D::ColliderMeshProcessor::Get().ColliderMeshChanged(Scene3D::Model::DownCast(Self())); +} + +void Model::RemoveColliderMesh(Scene3D::ModelNode& node) +{ + auto id = node.GetProperty(Actor::Property::ID); + auto iter = std::find_if(mColliderMeshes.begin(), mColliderMeshes.end(), [id](auto& item) { + return item.first == id; + }); + if(iter != mColliderMeshes.end()) + { + mColliderMeshes.erase(iter); + } +} + +void Model::RemoveModelNode(Scene3D::ModelNode modelNode) +{ + // remove collider mesh from the list if node is being removed + if(modelNode.HasColliderMesh()) + { + RemoveColliderMesh(modelNode); + GetImplementation(modelNode).SetRootModel(nullptr); + } + + if(mModelRoot) + { + UpdateShaderRecursively(modelNode, nullptr); + mModelRoot.Remove(modelNode); + } +} + void Model::SetChildrenSensitive(bool enable) { if(mModelChildrenSensitive != enable) @@ -295,6 +458,9 @@ void Model::SetImageBasedLightSource(const std::string& diffuseUrl, const std::s mDiffuseTexture.Reset(); mSpecularTexture.Reset(); UpdateImageBasedLightTexture(); + + // Request image resource GC + Dali::Scene3D::Internal::ImageResourceLoader::RequestGarbageCollect(); } else { @@ -304,6 +470,9 @@ void Model::SetImageBasedLightSource(const std::string& diffuseUrl, const std::s mIblDiffuseLoadTask = new EnvironmentMapLoadTask(mDiffuseIblUrl, Scene3D::EnvironmentMapType::CUBEMAP, MakeCallback(this, &Model::OnIblDiffuseLoadComplete)); Dali::AsyncTaskManager::Get().AddTask(mIblDiffuseLoadTask); mIblDiffuseDirty = false; + + // Request image resource GC + Dali::Scene3D::Internal::ImageResourceLoader::RequestGarbageCollect(); } if(isOnScene && mIblSpecularDirty) @@ -312,6 +481,9 @@ void Model::SetImageBasedLightSource(const std::string& diffuseUrl, const std::s mIblSpecularLoadTask = new EnvironmentMapLoadTask(mSpecularIblUrl, Scene3D::EnvironmentMapType::CUBEMAP, MakeCallback(this, &Model::OnIblSpecularLoadComplete)); Dali::AsyncTaskManager::Get().AddTask(mIblSpecularLoadTask); mIblSpecularDirty = false; + + // Request image resource GC + Dali::Scene3D::Internal::ImageResourceLoader::RequestGarbageCollect(); } } @@ -325,18 +497,6 @@ void Model::SetImageBasedLightSource(const std::string& diffuseUrl, const std::s NotifyResourceReady(); } -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(); - } -} - void Model::SetImageBasedLightScaleFactor(float scaleFactor) { mIblScaleFactor = scaleFactor; @@ -422,6 +582,259 @@ bool Model::ApplyCamera(uint32_t index, Dali::CameraActor camera) const 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& 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& 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 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 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); + } + } + } + } + } + } + } + } + } +} + +void Model::CastShadow(bool castShadow) +{ + mIsShadowCasting = castShadow; + UpdateCastShadowRecursively(mModelRoot, mIsShadowCasting); +} + +bool Model::IsShadowCasting() const +{ + return mIsShadowCasting; +} + +void Model::ReceiveShadow(bool receiveShadow) +{ + mIsShadowReceiving = receiveShadow; + UpdateReceiveShadowRecursively(mModelRoot, mIsShadowReceiving); +} + +bool Model::IsShadowReceiving() const +{ + return mIsShadowReceiving; +} + + /////////////////////////////////////////////////////////// // // Private methods @@ -431,49 +844,91 @@ void Model::OnInitialize() { // Make ParentOrigin as Center. Self().SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + mDefaultDiffuseTexture = ImageResourceLoader::GetEmptyTextureWhiteRGB(); + mDefaultSpecularTexture = ImageResourceLoader::GetEmptyTextureWhiteRGB(); } 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(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) @@ -502,7 +957,7 @@ float Model::GetWidthForHeight(float height) void Model::OnRelayout(const Vector2& size, RelayoutContainer& container) { Control::OnRelayout(size, container); - ScaleModel(); + ScaleModel(false); } bool Model::IsResourceReady() const @@ -510,7 +965,18 @@ bool Model::IsResourceReady() const return mModelResourceReady && mIblDiffuseResourceReady && mIblSpecularResourceReady; } -void Model::ScaleModel() +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::ScaleModel(bool useCurrentSize) { if(!mModelRoot) { @@ -518,7 +984,7 @@ void Model::ScaleModel() } float scale = 1.0f; - Vector3 size = Self().GetProperty(Dali::Actor::Property::SIZE); + Vector3 size = (useCurrentSize) ? Self().GetCurrentProperty(Dali::Actor::Property::SIZE) : Self().GetProperty(Dali::Actor::Property::SIZE); if(size.x > 0.0f && size.y > 0.0f) { scale = MAXFLOAT; @@ -541,65 +1007,103 @@ void Model::FitModelPosition() mModelRoot.SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3::ONE - mModelPivot); } -void Model::CollectRenderableActor(Actor actor) +void Model::UpdateCastShadowRecursively(Scene3D::ModelNode node, bool castShadow) { - uint32_t rendererCount = actor.GetRendererCount(); - if(rendererCount) + if(!node) { - mRenderableActors.push_back(actor); + return; } - uint32_t childrenCount = actor.GetChildCount(); + GetImplementation(node).CastShadow(castShadow); + 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; + } + UpdateCastShadowRecursively(childNode, castShadow); } } -void Model::UpdateImageBasedLightTexture() +void Model::UpdateReceiveShadowRecursively(Scene3D::ModelNode node, bool receiveShadow) { - Dali::Texture currentDiffuseTexture = (mDiffuseTexture && mSpecularTexture) ? mDiffuseTexture : mSceneDiffuseTexture; - Dali::Texture currentSpecularTexture = (mDiffuseTexture && mSpecularTexture) ? mSpecularTexture : mSceneSpecularTexture; - float currentIblScaleFactor = (mDiffuseTexture && mSpecularTexture) ? mIblScaleFactor : mSceneIblScaleFactor; + if(!node) + { + return; + } - if(!currentDiffuseTexture || !currentSpecularTexture) + GetImplementation(node).ReceiveShadow(receiveShadow); + 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; + } + UpdateReceiveShadowRecursively(childNode, receiveShadow); + } +} + +void Model::UpdateImageBasedLightTextureRecursively(Scene3D::ModelNode node, Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels) +{ + if(!node) { - currentDiffuseTexture = mDefaultDiffuseTexture; - currentSpecularTexture = mDefaultSpecularTexture; - currentIblScaleFactor = Dali::Scene3D::Loader::EnvironmentDefinition::GetDefaultIntensity(); + return; } - for(auto&& actor : mRenderableActors) + GetImplementation(node).SetImageBasedLightTexture(diffuseTexture, specularTexture, iblScaleFactor, specularMipmapLevels); + 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) { continue; } + UpdateImageBasedLightTextureRecursively(childNode, diffuseTexture, specularTexture, iblScaleFactor, specularMipmapLevels); + } +} + +void Model::UpdateImageBasedLightScaleFactorRecursively(Scene3D::ModelNode node, float iblScaleFactor) +{ + if(!node) + { + return; + } + + GetImplementation(node).SetImageBasedLightScaleFactor(iblScaleFactor); - uint32_t rendererCount = renderableActor.GetRendererCount(); - for(uint32_t i = 0; i < rendererCount; ++i) + uint32_t childrenCount = node.GetChildCount(); + for(uint32_t i = 0; i < childrenCount; ++i) + { + Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i)); + if(!childNode) { - Dali::Renderer renderer = renderableActor.GetRendererAt(i); - if(!renderer) - { - continue; - } - Dali::TextureSet textures = renderer.GetTextures(); - if(!textures) - { - continue; - } - 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); - } + continue; } - renderableActor.RegisterProperty(Dali::Scene3D::Loader::NodeDefinition::GetIblScaleFactorUniformName().data(), currentIblScaleFactor); + 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() @@ -611,14 +1115,7 @@ void Model::UpdateImageBasedLightScaleFactor() } 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); - } - } + UpdateImageBasedLightScaleFactorRecursively(mModelRoot, currentIblScaleFactor); } void Model::ApplyCameraTransform(Dali::CameraActor camera) const @@ -660,13 +1157,23 @@ void Model::ApplyCameraTransform(Dali::CameraActor camera) const camera.SetProperty(Actor::Property::SCALE, resultScale); } -void Model::NotifyImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float scaleFactor) +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, 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) { @@ -686,40 +1193,53 @@ void Model::NotifyImageBasedLightScaleFactor(float scaleFactor) void Model::OnModelLoadComplete() { + IntrusivePtr self = this; // Keep reference until this API finished + if(!mModelLoadTask->HasSucceeded()) { ResetResourceTasks(); + + if(ModelCacheManager::Get() && !mModelUrl.empty()) + { + ModelCacheManager::Get().UnreferenceModelCache(mModelUrl); + } + return; } + if(!mModelRoot) + { + CreateModelRoot(); + } CreateModel(); - mRenderableActors.clear(); - CollectRenderableActor(mModelRoot); - auto* resources = &(mModelLoadTask->mResources); - auto* scene = &(mModelLoadTask->mScene); - CreateAnimations(*scene); + auto& resources = mModelLoadTask->GetResources(); + auto& scene = mModelLoadTask->GetScene(); + CreateAnimations(scene); ResetCameraParameters(); - - if(!resources->mEnvironmentMaps.empty()) + if(!resources.mEnvironmentMaps.empty()) { - mDefaultDiffuseTexture = resources->mEnvironmentMaps.front().second.mDiffuse; - mDefaultSpecularTexture = resources->mEnvironmentMaps.front().second.mSpecular; + if(resources.mEnvironmentMaps.front().second.mDiffuse) + { + mDefaultDiffuseTexture = resources.mEnvironmentMaps.front().second.mDiffuse; + } + if(resources.mEnvironmentMaps.front().second.mSpecular) + { + mDefaultSpecularTexture = resources.mEnvironmentMaps.front().second.mSpecular; + } } + if(mShadowMapTexture) + { + UpdateShadowMapTextureRecursively(mModelRoot, mShadowMapTexture); + } UpdateImageBasedLightTexture(); UpdateImageBasedLightScaleFactor(); - - 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; - NotifyResourceReady(); ResetResourceTask(mModelLoadTask); + NotifyResourceReady(); } void Model::OnIblDiffuseLoadComplete() @@ -735,7 +1255,8 @@ void Model::OnIblDiffuseLoadComplete() void Model::OnIblSpecularLoadComplete() { - mSpecularTexture = mIblSpecularLoadTask->GetLoadedTexture(); + mSpecularTexture = mIblSpecularLoadTask->GetLoadedTexture(); + mSpecularMipmapLevels = mIblSpecularLoadTask->GetMipmapLevels(); ResetResourceTask(mIblSpecularLoadTask); mIblSpecularResourceReady = true; if(mIblDiffuseResourceReady && mIblSpecularResourceReady) @@ -750,6 +1271,11 @@ void Model::OnIblLoadComplete() NotifyResourceReady(); } +void Model::OnSizeNotification(Dali::PropertyNotification& source) +{ + ScaleModel(true); +} + void Model::ResetResourceTasks() { if(!Dali::Adaptor::IsAvailable()) @@ -761,55 +1287,44 @@ void Model::ResetResourceTasks() ResetResourceTask(mIblSpecularLoadTask); } -void Model::ResetResourceTask(IntrusivePtr asyncTask) -{ - if(!asyncTask) - { - return; - } - Dali::AsyncTaskManager::Get().RemoveTask(asyncTask); - asyncTask.Reset(); -} - void Model::NotifyResourceReady() { if(!IsResourceReady()) { return; } - Control::SetResourceReady(false); + Control::SetResourceReady(); } void Model::CreateModel() { - mModelRoot = Actor::New(); - mModelRoot.SetProperty(Actor::Property::COLOR_MODE, ColorMode::USE_OWN_MULTIPLY_PARENT_COLOR); + 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{}}; - 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]); + Dali::Scene3D::Loader::NodeDefinition::CreateParams nodeParams{resources, xforms, mShaderManager, {}, {}, {}}; - if(auto actor = scene->CreateNodes(iRoot, mModelLoadTask->mResourceChoices, nodeParams)) + // 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)) { - scene->ConfigureSkeletonJoints(iRoot, resources->mSkeletons, actor); - scene->ConfigureSkinningShaders(*resources, actor, std::move(nodeParams.mSkinnables)); - ConfigureBlendShapeShaders(*resources, *scene, actor, std::move(nodeParams.mBlendshapeRequests)); + scene.ConfigureSkinningShaders(resources, modelNode, std::move(nodeParams.mSkinnables)); + ConfigureBlendShapeShaders(resources, scene, modelNode, std::move(nodeParams.mBlendshapeRequests)); - scene->ApplyConstraints(actor, std::move(nodeParams.mConstrainables)); + scene.ApplyConstraints(modelNode, std::move(nodeParams.mConstrainables)); - mModelRoot.Add(actor); + mModelRoot.Add(modelNode); } - AddModelTreeToAABB(AABB, *scene, mModelLoadTask->mResourceChoices, iRoot, nodeParams, Matrix::IDENTITY); - rootCount++; + AddModelTreeToAABB(AABB, scene, resourceChoices, iRoot, nodeParams, Matrix::IDENTITY); } + UpdateBlendShapeNodeMap(); + mNaturalSize = AABB.CalculateSize(); mModelPivot = AABB.CalculatePivot(); mModelRoot.SetProperty(Dali::Actor::Property::SIZE, mNaturalSize); @@ -819,13 +1334,13 @@ void Model::CreateModel() Self().SetProperty(Dali::Actor::Property::SIZE, mNaturalSize); } FitModelPosition(); - ScaleModel(); + ScaleModel(false); } void Model::CreateAnimations(Dali::Scene3D::Loader::SceneDefinition& scene) { mAnimations.clear(); - if(!mModelLoadTask->mAnimations.empty()) + if(!mModelLoadTask->GetAnimations().empty()) { auto getActor = [&](const Scene3D::Loader::AnimatedProperty& property) { if(property.mNodeIndex == Scene3D::Loader::INVALID_INDEX) @@ -840,10 +1355,10 @@ void Model::CreateAnimations(Dali::Scene3D::Loader::SceneDefinition& scene) return mModelRoot.FindChildById(node->mNodeId); }; - for(auto&& animation : mModelLoadTask->mAnimations) + for(auto&& animation : mModelLoadTask->GetAnimations()) { Dali::Animation anim = animation.ReAnimate(getActor); - mAnimations.push_back({animation.mName, anim}); + mAnimations.push_back({animation.GetName(), anim}); } } } @@ -851,13 +1366,21 @@ void Model::CreateAnimations(Dali::Scene3D::Loader::SceneDefinition& scene) void Model::ResetCameraParameters() { mCameraParameters.clear(); - if(!mModelLoadTask->mCameraParameters.empty()) + if(!mModelLoadTask->GetCameras().empty()) { // Copy camera parameters. - std::copy(mModelLoadTask->mCameraParameters.begin(), mModelLoadTask->mCameraParameters.end(), std::back_inserter(mCameraParameters)); + 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