2 * Copyright (c) 2023 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-scene3d/public-api/loader/scene-definition.h>
22 #include <dali/devel-api/common/map-wrapper.h>
23 #include <dali/public-api/animation/constraints.h>
26 #include <dali-scene3d/internal/graphics/builtin-shader-extern-gen.h>
27 #include <dali-scene3d/internal/model-components/model-node-impl.h>
28 #include <dali-scene3d/public-api/loader/blend-shape-details.h>
29 #include <dali-scene3d/public-api/loader/skinning-details.h>
30 #include <dali-scene3d/public-api/loader/utils.h>
32 // #define DEBUG_SCENE_DEFINITION
33 // #define DEBUG_JOINTS
35 #if defined(DEBUG_SCENE_DEFINITION) || defined(DEBUG_JOINTS)
36 #define DEBUG_ONLY(x) x
41 #define LOGD(x) DEBUG_ONLY(printf x; printf("\n"); fflush(stdout))
43 namespace Dali::Scene3D::Loader
48 const std::map<Property::Type, Constraint (*)(Actor&, Property::Index)>& GetConstraintFactory()
50 static const std::map<Property::Type, Constraint (*)(Actor&, Property::Index)> sConstraintFactory = {
51 {Property::Type::BOOLEAN,
52 [](Actor& a, Property::Index i) {
53 return Constraint::New<bool>(a, i, [](bool& current, const PropertyInputContainer& inputs) {
54 current = inputs[0]->GetBoolean();
57 {Property::Type::INTEGER,
58 [](Actor& a, Property::Index i) {
59 return Constraint::New<int>(a, i, [](int& current, const PropertyInputContainer& inputs) {
60 current = inputs[0]->GetInteger();
63 {Property::Type::FLOAT,
64 [](Actor& a, Property::Index i) {
65 return Constraint::New<float>(a, i, EqualToConstraint());
67 {Property::Type::VECTOR2,
68 [](Actor& a, Property::Index i) {
69 return Constraint::New<Vector2>(a, i, EqualToConstraint());
71 {Property::Type::VECTOR3,
72 [](Actor& a, Property::Index i) {
73 return Constraint::New<Vector3>(a, i, EqualToConstraint());
75 {Property::Type::VECTOR4,
76 [](Actor& a, Property::Index i) {
77 return Constraint::New<Vector4>(a, i, EqualToConstraint());
79 {Property::Type::MATRIX,
80 [](Actor& a, Property::Index i) {
81 return Constraint::New<Matrix>(a, i, EqualToConstraint());
83 {Property::Type::MATRIX3,
84 [](Actor& a, Property::Index i) {
85 return Constraint::New<Matrix3>(a, i, EqualToConstraint());
87 {Property::Type::ROTATION,
88 [](Actor& a, Property::Index i) {
89 return Constraint::New<Quaternion>(a, i, EqualToConstraint());
92 return sConstraintFactory;
95 struct ResourceReflector : IResourceReflector
97 Index* iMesh = nullptr;
98 Index* iShader = nullptr;
100 void Reflect(ResourceType::Value type, Index& id)
104 case ResourceType::Shader:
105 DALI_ASSERT_ALWAYS(!iShader && "Shader index already assigned!");
109 case ResourceType::Mesh:
110 DALI_ASSERT_ALWAYS(!iMesh && "Mesh index already assigned!");
114 default: // Other resource types are not relevant to the problem at hand.
122 Shader sJointDebugShader;
125 void EnsureJointDebugShaderCreated()
129 sJointDebugShader = Shader::New(SHADER_SCENE3D_JOINT_DEBUG_VERT, SHADER_SCENE3D_JOINT_DEBUG_FRAG);
134 void AddJointDebugVisual(Actor aJoint)
136 Property::Map attribs;
137 attribs["aPosition"] = Property::Type::VECTOR3;
138 attribs["aColor"] = Property::Type::FLOAT;
140 PropertyBuffer vbo = PropertyBuffer::New(attribs);
147 {Vector3::ZERO, .999f + .999f * 256.f + .999f * 256.f * 256.f},
148 {Vector3::XAXIS, .999f},
149 {Vector3::YAXIS, .999f * 256.f},
150 {Vector3::ZAXIS, .999f * 256.f * 256.f},
153 vbo.SetData(&vertices, std::extent<decltype(vertices)>::value);
155 uint16_t indices[] = {0, 1, 0, 2, 0, 3};
157 Geometry geo = Geometry::New();
158 geo.AddVertexBuffer(vbo);
159 geo.SetIndexBuffer(indices, std::extent<decltype(indices)>::value);
160 geo.SetType(Geometry::LINES);
162 Renderer r = Renderer::New(geo, sJointDebugShader);
163 aJoint.AddRenderer(r);
165 aJoint.SetVisible(true);
167 #endif //DEBUG_JOINTS
169 class ActorCreatorVisitor : public NodeDefinition::IVisitor
172 ActorCreatorVisitor(NodeDefinition::CreateParams& params)
173 : mCreationContext(params)
177 void Start(NodeDefinition& n)
179 mCreationContext.mXforms.modelStack.Push(n.GetLocalSpace());
181 ModelNode a = n.CreateModelNode(mCreationContext);
182 if(!mActorStack.empty())
184 mActorStack.back().Add(a);
190 mActorStack.push_back(a);
193 void Finish(NodeDefinition& n)
195 mActorStack.pop_back();
196 mCreationContext.mXforms.modelStack.Pop();
199 ModelNode GetRoot() const
205 NodeDefinition::CreateParams& mCreationContext;
206 std::vector<ModelNode> mActorStack;
210 void SortAndDeduplicateSkinningRequests(std::vector<SkinningShaderConfigurationRequest>& requests)
212 // Sort requests by shaders.
213 std::sort(requests.begin(), requests.end());
215 // Remove duplicates.
216 auto i = requests.begin();
217 auto iEnd = requests.end();
218 Shader s = i->mShader;
219 Index skeletonIdx = i->mSkeletonIdx;
223 // Multiple identical shader instances are removed.
224 while(i != iEnd && i->mShader == s)
226 // Cannot have multiple skeletons input to the same shader.
227 // NOTE: DliModel now makes sure this doesn't happen.
228 DALI_ASSERT_ALWAYS(i->mSkeletonIdx == skeletonIdx &&
229 "Skinning shader must not be shared between different skeletons.");
231 i->mShader = Shader();
240 skeletonIdx = i->mSkeletonIdx;
244 requests.erase(std::remove_if(requests.begin(), requests.end(), [](const SkinningShaderConfigurationRequest& sscr) {
245 return !sscr.mShader;
250 void ConfigureBoneMatrix(const Matrix& ibm, ModelNode joint, ModelPrimitive primitive, Index& boneIdx)
252 // Register bone transform on shader.
253 Internal::GetImplementation(joint).SetBoneMatrix(ibm, primitive, boneIdx);
257 template<class Visitor, class SceneDefinition>
258 void VisitInternal(Index iNode, const Customization::Choices& choices, Visitor& v, SceneDefinition& sd)
260 auto& node = *sd.GetNode(iNode);
263 if(node.mCustomization)
265 if(!node.mChildren.empty())
267 auto choice = choices.Get(node.mCustomization->mTag);
268 Index i = std::min(choice != Customization::NONE ? choice : 0, static_cast<Index>(node.mChildren.size() - 1));
269 sd.Visit(node.mChildren[i], choices, v);
274 for(auto i : node.mChildren)
276 sd.Visit(i, choices, v);
285 SceneDefinition::SceneDefinition()
290 EnsureJointDebugShaderCreated();
294 SceneDefinition::SceneDefinition(SceneDefinition&& other)
295 : mNodes(std::move(other.mNodes)),
296 mRootNodeIds(std::move(other.mRootNodeIds))
299 EnsureJointDebugShaderCreated();
303 SceneDefinition::~SceneDefinition()
309 sJointDebugShader = Shader();
314 uint32_t Dali::Scene3D::Loader::SceneDefinition::AddRootNode(Index iNode)
316 if(iNode < mNodes.size())
318 uint32_t result = mRootNodeIds.size();
319 mRootNodeIds.push_back(iNode);
324 ExceptionFlinger(ASSERT_LOCATION) << "Failed to add new root with node " << iNode << " -- index out of bounds.";
329 const std::vector<Index>& SceneDefinition::GetRoots() const
334 void SceneDefinition::RemoveRootNode(Index iRoot)
336 if(iRoot < mRootNodeIds.size())
338 mRootNodeIds.erase(mRootNodeIds.begin() + iRoot);
342 ExceptionFlinger(ASSERT_LOCATION) << "Failed to remove root " << iRoot << " -- index out of bounds.";
346 uint32_t SceneDefinition::GetNodeCount() const
348 return mNodes.size();
351 const NodeDefinition* SceneDefinition::GetNode(Index iNode) const
353 return mNodes[iNode].get();
356 NodeDefinition* SceneDefinition::GetNode(Index iNode)
358 if(iNode != Scene3D::Loader::INVALID_INDEX && iNode < mNodes.size())
360 return mNodes[iNode].get();
365 void SceneDefinition::Visit(Index iNode, const Customization::Choices& choices, NodeDefinition::IVisitor& v)
367 VisitInternal(iNode, choices, v, *this);
370 void SceneDefinition::Visit(Index iNode, const Customization::Choices& choices, NodeDefinition::IConstVisitor& v) const
372 VisitInternal(iNode, choices, v, *this);
375 void SceneDefinition::CountResourceRefs(Index iNode, const Customization::Choices& choices, ResourceRefCounts& refCounts) const
377 struct RefCounter : IResourceReceiver
379 ResourceRefCounts* refCounts;
381 void Register(ResourceType::Value type, Index id)
383 ++(*refCounts)[type][id];
387 struct : NodeDefinition::IConstVisitor
391 void Start(const NodeDefinition& n)
393 for(auto& renderable : n.mRenderables)
395 renderable->RegisterResources(counter);
399 void Finish(const NodeDefinition& n)
404 refCounterVisitor.counter.refCounts = &refCounts;
406 Visit(iNode, choices, refCounterVisitor);
409 ModelNode SceneDefinition::CreateNodes(Index iNode, const Customization::Choices& choices, NodeDefinition::CreateParams& params)
411 ActorCreatorVisitor actorCreatorVisitor(params);
413 Visit(iNode, choices, actorCreatorVisitor);
415 return actorCreatorVisitor.GetRoot();
418 void SceneDefinition::GetCustomizationOptions(const Customization::Choices& choices,
419 Customization::Map& outCustomizationOptions,
420 Customization::Choices* outMissingChoices) const
422 struct : NodeDefinition::IConstVisitor
424 const Customization::Choices* choices; // choices that we know about.
425 Customization::Map* options; // tags are registered here. NO OWNERSHIP.
426 Customization::Choices* missingChoices; // tags will be registered with the default 0. NO OWNERSHIP.
428 void Start(const NodeDefinition& n)
432 const std::string& tag = n.mCustomization->mTag;
433 if(missingChoices != nullptr && choices->Get(tag) == Customization::NONE)
435 missingChoices->Set(tag, 0);
438 auto customization = options->Get(tag);
441 customization = options->Set(tag, {});
443 customization->nodes.push_back(n.mName);
444 customization->numOptions = std::max(customization->numOptions,
445 static_cast<uint32_t>(n.mChildren.size()));
449 void Finish(const NodeDefinition& n)
453 } customizationRegistrationVisitor;
454 customizationRegistrationVisitor.choices = &choices;
455 customizationRegistrationVisitor.options = &outCustomizationOptions;
456 customizationRegistrationVisitor.missingChoices = outMissingChoices;
458 for(auto i : mRootNodeIds)
460 Visit(i, choices, customizationRegistrationVisitor);
464 NodeDefinition* SceneDefinition::AddNode(std::unique_ptr<NodeDefinition>&& nodeDef)
466 // add next index (to which we're about to push) as a child to the designated parent, if any.
467 if(nodeDef->mParentIdx != INVALID_INDEX)
469 mNodes[nodeDef->mParentIdx]->mChildren.push_back(mNodes.size());
472 mNodes.push_back(std::move(nodeDef));
474 return mNodes.back().get();
477 bool SceneDefinition::ReparentNode(const std::string& name, const std::string& newParentName, Index siblingOrder)
479 LOGD(("reparenting %s to %s @ %d", name.c_str(), newParentName.c_str(), siblingOrder));
481 std::unique_ptr<NodeDefinition>* nodePtr = nullptr;
482 std::unique_ptr<NodeDefinition>* newParentPtr = nullptr;
483 if(!FindNode(name, &nodePtr) || !FindNode(newParentName, &newParentPtr))
488 auto& node = *nodePtr;
489 auto iNode = std::distance(mNodes.data(), nodePtr);
491 DEBUG_ONLY(auto dumpNode = [](NodeDefinition const& n) {
492 std::ostringstream stream;
493 stream << n.mName << " (" << n.mParentIdx << "):";
494 for(auto i : n.mChildren)
498 LOGD(("%s", stream.str().c_str()));
501 // Remove node from children of previous parent (if any).
502 if(node->mParentIdx != INVALID_INDEX)
504 LOGD(("old parent:"));
505 DEBUG_ONLY(dumpNode(*mNodes[node->mParentIdx]);)
507 auto& children = mNodes[node->mParentIdx]->mChildren;
508 children.erase(std::remove(children.begin(), children.end(), iNode), children.end());
510 DEBUG_ONLY(dumpNode(*mNodes[node->mParentIdx]);)
513 // Register node to new parent.
514 LOGD(("new parent:"));
515 DEBUG_ONLY(dumpNode(**newParentPtr);)
516 auto& children = (*newParentPtr)->mChildren;
517 if(siblingOrder > children.size())
519 siblingOrder = children.size();
521 children.insert(children.begin() + siblingOrder, 1, iNode);
522 DEBUG_ONLY(dumpNode(**newParentPtr);)
524 // Update parent index.
526 DEBUG_ONLY(dumpNode(*node);)
527 auto iParent = std::distance(mNodes.data(), newParentPtr);
528 node->mParentIdx = iParent;
529 DEBUG_ONLY(dumpNode(*node);)
533 bool SceneDefinition::RemoveNode(const std::string& name)
535 std::unique_ptr<NodeDefinition>* node = nullptr;
536 if(!FindNode(name, &node))
541 // Reset node def pointers recursively.
542 auto& thisNodes = mNodes;
543 unsigned int numReset = 0;
544 std::function<void(std::unique_ptr<NodeDefinition>&)> resetFn =
545 [&thisNodes, &resetFn, &numReset](std::unique_ptr<NodeDefinition>& nd) {
546 LOGD(("resetting %d", &nd - thisNodes.data()));
547 for(auto i : nd->mChildren)
549 resetFn(thisNodes[i]);
557 // Gather indices of dead nodes into a vector which we sort on insertion.
558 std::vector<Index> offsets;
559 offsets.reserve(numReset);
560 for(auto& n : mNodes)
564 offsets.push_back(std::distance(mNodes.data(), &n));
568 // Erase dead nodes as they don't have to be processed anymore.
569 mNodes.erase(std::remove(mNodes.begin(), mNodes.end(), decltype(mNodes)::value_type()), mNodes.end());
571 // Offset all indices (parent and child) by the index they'd sort into in offsets.
574 INDEX_FOR_REMOVAL = INVALID_INDEX
576 auto offsetter = [&offsets](Index& i) {
577 auto iFind = std::lower_bound(offsets.begin(), offsets.end(), i);
578 if(iFind != offsets.end() && *iFind == i)
580 LOGD(("marking %d for removal.", i));
581 i = INDEX_FOR_REMOVAL;
586 auto distance = std::distance(offsets.begin(), iFind);
589 LOGD(("offsetting %d by %d.", i, distance));
596 for(auto& nd : mNodes)
598 bool parentOffsetResult = offsetter(nd->mParentIdx);
599 DALI_ASSERT_ALWAYS(parentOffsetResult); // since nodes were recursively removed, we should not be finding invalid parents at this point.
601 auto& children = nd->mChildren;
602 for(auto i0 = children.begin(), i1 = children.end(); i0 != i1; ++i0)
607 children.erase(std::remove(children.begin(), children.end(), INDEX_FOR_REMOVAL), children.end());
613 void SceneDefinition::GetNodeModelStack(Index index, MatrixStack& model) const
615 auto& thisNodes = mNodes;
616 std::function<void(int)> buildStack = [&model, &thisNodes, &buildStack](int i) {
617 auto node = thisNodes[i].get();
618 if(node->mParentIdx != INVALID_INDEX)
620 buildStack(node->mParentIdx);
622 model.Push(node->GetLocalSpace());
627 NodeDefinition* SceneDefinition::FindNode(const std::string& name, Index* outIndex)
629 auto iBegin = mNodes.begin();
630 auto iEnd = mNodes.end();
631 auto iFind = std::find_if(iBegin, iEnd, [&name](const std::unique_ptr<NodeDefinition>& nd) {
632 return nd->mName == name;
635 auto result = iFind != iEnd ? iFind->get() : nullptr;
636 if(result && outIndex)
638 *outIndex = std::distance(iBegin, iFind);
643 const NodeDefinition* SceneDefinition::FindNode(const std::string& name, Index* outIndex) const
645 auto iBegin = mNodes.begin();
646 auto iEnd = mNodes.end();
647 auto iFind = std::find_if(iBegin, iEnd, [&name](const std::unique_ptr<NodeDefinition>& nd) {
648 return nd->mName == name;
651 auto result = iFind != iEnd ? iFind->get() : nullptr;
652 if(result && outIndex)
654 *outIndex = std::distance(iBegin, iFind);
659 Index SceneDefinition::FindNodeIndex(const NodeDefinition& node) const
661 auto iBegin = mNodes.begin();
662 auto iEnd = mNodes.end();
663 auto iFind = std::find_if(iBegin, iEnd, [&node](const std::unique_ptr<NodeDefinition>& n) {
664 return n.get() == &node;
666 return iFind != iEnd ? std::distance(iBegin, iFind) : INVALID_INDEX;
669 void SceneDefinition::FindNodes(NodePredicate predicate, NodeConsumer consumer, unsigned int limit)
672 for(auto& defp : mNodes)
686 void SceneDefinition::FindNodes(NodePredicate predicate, ConstNodeConsumer consumer, unsigned int limit) const
689 for(auto& defp : mNodes)
703 void SceneDefinition::ApplyConstraints(Actor& root,
704 std::vector<ConstraintRequest>&& constrainables,
705 StringCallback onError) const
707 for(auto& cr : constrainables)
709 auto& nodeDef = mNodes[cr.mConstraint->mSourceIdx];
710 auto sourceName = nodeDef->mName.c_str();
711 Property::Index iTarget = cr.mTarget.GetPropertyIndex(cr.mConstraint->mProperty);
712 if(iTarget != Property::INVALID_INDEX)
714 auto propertyType = cr.mTarget.GetPropertyType(iTarget);
715 auto iFind = GetConstraintFactory().find(propertyType);
716 if(iFind == GetConstraintFactory().end())
718 onError(FormatString("node '%s': Property '%s' has unsupported type '%s'; ignored.",
720 cr.mConstraint->mProperty.c_str(),
721 PropertyTypes::GetName(propertyType)));
725 Constraint constraint = iFind->second(cr.mTarget, iTarget);
727 Actor source = root.FindChildByName(nodeDef->mName);
730 auto targetName = cr.mTarget.GetProperty(Actor::Property::NAME).Get<std::string>();
731 onError(FormatString("node '%s': Failed to locate constraint source %s@%s; ignored.",
733 cr.mConstraint->mProperty.c_str(),
734 targetName.c_str()));
737 else if(source == cr.mTarget)
739 onError(FormatString("node '%s': Cyclic constraint definition for property '%s'; ignored.",
741 cr.mConstraint->mProperty.c_str()));
745 Property::Index iSource = source.GetPropertyIndex(cr.mConstraint->mProperty);
746 constraint.AddSource(Source{source, iSource});
751 auto targetName = cr.mTarget.GetProperty(Actor::Property::NAME).Get<std::string>();
752 onError(FormatString("node '%s': Failed to create constraint for property %s@%s; ignored.",
754 cr.mConstraint->mProperty.c_str(),
755 targetName.c_str()));
760 void SceneDefinition::EnsureUniqueSkinningShaderInstances(ResourceBundle& resources) const
762 std::map<Index, std::map<Index, std::vector<Index*>>> skinningShaderUsers;
763 for(auto& node : mNodes)
765 for(auto& renderable : node->mRenderables)
767 ResourceReflector reflector;
768 renderable->ReflectResources(reflector);
772 const auto& mesh = resources.mMeshes[*reflector.iMesh].first;
775 skinningShaderUsers[*reflector.iShader][mesh.mSkeletonIdx].push_back(reflector.iShader);
781 // For each shader, and each skeleton using the same shader as the first skeleton,
782 // update the shader references (from nodes with skinned meshes) with a new copy of
783 // the shader definition from the node using the first skeleton.
784 for(auto& users : skinningShaderUsers)
786 auto& skeletons = users.second;
787 auto iterSkeleton = skeletons.begin();
788 // skipping the first skeleton.
791 resources.mShaders.reserve(resources.mShaders.size() + std::distance(iterSkeleton, skeletons.end()));
792 const ShaderDefinition& shaderDef = resources.mShaders[users.first].first;
794 while(iterSkeleton != skeletons.end())
796 Index iShader = resources.mShaders.size();
797 resources.mShaders.push_back({shaderDef, Shader()});
799 for(auto& i : iterSkeleton->second)
808 void SceneDefinition::ConfigureSkinningShaders(const ResourceBundle& resources,
810 std::vector<SkinningShaderConfigurationRequest>&& requests) const
817 SortAndDeduplicateSkinningRequests(requests);
819 for(auto& request : requests)
821 auto& skeleton = resources.mSkeletons[request.mSkeletonIdx];
822 if(skeleton.mJoints.empty())
824 LOGD(("Skeleton %d has no joints.", request.mSkeletonIdx));
829 for(auto& joint : skeleton.mJoints)
831 auto node = GetNode(joint.mNodeIdx);
832 ModelNode modelNode = ModelNode::DownCast(rootActor.FindChildByName(node->mName));
837 ConfigureBoneMatrix(joint.mInverseBindMatrix, modelNode, request.mPrimitive, boneIdx);
842 bool SceneDefinition::ConfigureBlendshapeShaders(const ResourceBundle& resources,
844 std::vector<BlendshapeShaderConfigurationRequest>&& requests,
845 StringCallback onError) const
852 // Sort requests by shaders.
853 std::sort(requests.begin(), requests.end());
855 // Remove duplicates.
856 auto i = requests.begin();
857 auto iEnd = requests.end();
858 Shader s = i->mShader;
862 // Multiple identical shader instances are removed.
863 while(i != iEnd && i->mShader == s)
865 i->mShader = Shader();
877 // Configure the rest.
880 for(auto& i : requests)
883 if(FindNode(i.mNodeName, &iNode))
885 const auto& node = GetNode(iNode);
887 const auto& mesh = resources.mMeshes[i.mMeshIdx];
889 if(mesh.first.HasBlendShapes())
891 Actor actor = rootActor.FindChildByName(node->mName);
892 Scene3D::ModelNode node = Scene3D::ModelNode::DownCast(actor);
897 BlendShapes::BlendShapeData data;
898 data.components = 0x0;
899 for(auto&& blendShape : mesh.first.mBlendShapes)
901 data.weights.push_back(blendShape.weight);
902 data.components |= (blendShape.deltas.IsDefined() * BlendShapes::Component::POSITIONS) |
903 (blendShape.normals.IsDefined() * BlendShapes::Component::NORMALS) | (blendShape.tangents.IsDefined() * BlendShapes::Component::TANGENTS);
905 for(auto&& factor : mesh.second.blendShapeUnnormalizeFactor)
907 data.unnormalizeFactors.push_back(factor);
909 data.version = mesh.first.mBlendShapeVersion;
910 data.bufferOffset = mesh.second.blendShapeBufferOffset;
912 Internal::GetImplementation(node).SetBlendShapeData(data, i.mPrimitive);
920 void SceneDefinition::EnsureUniqueBlendShapeShaderInstances(ResourceBundle& resources) const
922 std::map<Index, std::map<std::string, std::vector<Index*>>> blendShapeShaderUsers;
923 for(auto& node : mNodes)
925 for(auto& renderable : node->mRenderables)
927 ResourceReflector reflector;
928 renderable->ReflectResources(reflector);
932 const auto& mesh = resources.mMeshes[*reflector.iMesh].first;
933 if(mesh.HasBlendShapes())
935 blendShapeShaderUsers[*reflector.iShader][node->mName].push_back(reflector.iShader);
941 for(auto& users : blendShapeShaderUsers)
943 resources.mShaders.reserve(resources.mShaders.size() + users.second.size() - 1u);
944 const ShaderDefinition& shaderDef = resources.mShaders[users.first].first;
946 auto nodesIt = users.second.begin();
947 auto nodesEndIt = users.second.end();
948 // skipping the first node.
950 while(nodesIt != nodesEndIt)
952 Index iShader = resources.mShaders.size();
953 resources.mShaders.push_back({shaderDef, Shader()});
955 auto& nodes = *nodesIt;
956 for(auto& shader : nodes.second)
965 SceneDefinition& SceneDefinition::operator=(SceneDefinition&& other)
967 SceneDefinition temp(std::move(other));
968 std::swap(mNodes, temp.mNodes);
969 std::swap(mRootNodeIds, temp.mRootNodeIds);
973 bool SceneDefinition::FindNode(const std::string& name, std::unique_ptr<NodeDefinition>** result)
975 // We're searching from the end assuming a higher probability of operations targeting
976 // recently added nodes. (conf.: root, which is immovable, cannot be removed, and was
977 // the first to be added, is index 0.)
978 auto iFind = std::find_if(mNodes.rbegin(), mNodes.rend(), [&name](const std::unique_ptr<NodeDefinition>& nd) {
979 return nd->mName == name;
982 const bool success = iFind != mNodes.begin();
983 if(success && result)
992 } // namespace Dali::Scene3D::Loader