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/public-api/loader/blend-shape-details.h>
28 #include <dali-scene3d/public-api/loader/skinning-details.h>
29 #include <dali-scene3d/public-api/loader/utils.h>
31 //#define DEBUG_SCENE_DEFINITION
32 //#define DEBUG_JOINTS
34 #if defined(DEBUG_SCENE_DEFINITION) || defined(DEBUG_JOINTS)
35 #define DEBUG_ONLY(x) x
40 #define LOGD(x) DEBUG_ONLY(printf x; printf("\n"); fflush(stdout))
42 namespace Dali::Scene3D::Loader
46 const char* JOINT_MATRIX{"jointMatrix"};
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 Actor a = n.CreateActor(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 Actor GetRoot() const
205 NodeDefinition::CreateParams& mCreationContext;
206 std::vector<Actor> mActorStack;
210 bool IsAncestor(const SceneDefinition& scene, Index ancestor, Index node, Index rootHint = INVALID_INDEX)
212 bool isAncestor = false;
213 while(node != rootHint && !isAncestor)
215 node = scene.GetNode(node)->mParentIdx;
216 isAncestor = ancestor == node;
221 void InsertUniqueSorted(std::vector<Index>& data, Index value)
223 auto iInsert = std::lower_bound(data.begin(), data.end(), value);
224 if(iInsert == data.end() || *iInsert != value)
226 data.insert(iInsert, value);
230 void RemoveFromSorted(std::vector<Index>& data, Index value)
232 auto iRemove = std::lower_bound(data.begin(), data.end(), value);
233 if(iRemove != data.end() && *iRemove == value)
239 Property::Index ConfigureJointMatrix(Actor actor, Actor ancestor, Property::Index propJointMatrix)
241 Actor parent = actor.GetParent();
242 if(parent != ancestor)
244 propJointMatrix = ConfigureJointMatrix(parent, ancestor, propJointMatrix);
247 auto myPropJointMatrix = actor.GetPropertyIndex(JOINT_MATRIX);
248 if(myPropJointMatrix == Property::INVALID_INDEX)
250 myPropJointMatrix = actor.RegisterProperty(JOINT_MATRIX, Matrix{false});
251 Constraint constraint = Constraint::New<Matrix>(actor, propJointMatrix, [](Matrix& output, const PropertyInputContainer& inputs) {
252 Matrix jointMatrix{false};
253 jointMatrix.SetTransformComponents(Vector3::ONE, inputs[0]->GetQuaternion(), inputs[1]->GetVector3());
255 Matrix::Multiply(output, jointMatrix, inputs[2]->GetMatrix());
257 constraint.AddSource(Source{actor, Actor::Property::ORIENTATION});
258 constraint.AddSource(Source{actor, Actor::Property::POSITION});
259 constraint.AddSource(Source{parent, propJointMatrix});
263 return myPropJointMatrix;
266 void SortAndDeduplicateSkinningRequests(std::vector<SkinningShaderConfigurationRequest>& requests)
268 // Sort requests by shaders.
269 std::sort(requests.begin(), requests.end());
271 // Remove duplicates.
272 auto i = requests.begin();
273 auto iEnd = requests.end();
274 Shader s = i->mShader;
275 Index skeletonIdx = i->mSkeletonIdx;
279 // Multiple identical shader instances are removed.
280 while(i != iEnd && i->mShader == s)
282 // Cannot have multiple skeletons input to the same shader.
283 // NOTE: DliModel now makes sure this doesn't happen.
284 DALI_ASSERT_ALWAYS(i->mSkeletonIdx == skeletonIdx &&
285 "Skinning shader must not be shared between different skeletons.");
287 i->mShader = Shader();
296 skeletonIdx = i->mSkeletonIdx;
300 requests.erase(std::remove_if(requests.begin(), requests.end(), [](const SkinningShaderConfigurationRequest& sscr) {
301 return !sscr.mShader;
306 void ConfigureBoneMatrix(const Matrix& ibm, Actor joint, Shader& shader, Index& boneIdx)
308 // Register bone transform on shader.
309 char propertyNameBuffer[32];
310 snprintf(propertyNameBuffer, sizeof(propertyNameBuffer), "%s[%d]", Skinning::BONE_UNIFORM_NAME, boneIdx);
311 DALI_ASSERT_DEBUG(shader.GetPropertyIndex(propertyNameBuffer) == Property::INVALID_INDEX);
312 auto propBoneXform = shader.RegisterProperty(propertyNameBuffer, Matrix{false});
314 // Constrain bone matrix to joint transform.
315 Constraint constraint = Constraint::New<Matrix>(shader, propBoneXform, [ibm](Matrix& output, const PropertyInputContainer& inputs) {
316 Matrix::Multiply(output, ibm, inputs[0]->GetMatrix());
319 auto propJointMatrix = joint.GetPropertyIndex(JOINT_MATRIX);
320 constraint.AddSource(Source{joint, propJointMatrix});
326 template<class Visitor, class SceneDefinition>
327 void VisitInternal(Index iNode, const Customization::Choices& choices, Visitor& v, SceneDefinition& sd)
329 auto& node = *sd.GetNode(iNode);
332 if(node.mCustomization)
334 if(!node.mChildren.empty())
336 auto choice = choices.Get(node.mCustomization->mTag);
337 Index i = std::min(choice != Customization::NONE ? choice : 0, static_cast<Index>(node.mChildren.size() - 1));
338 sd.Visit(node.mChildren[i], choices, v);
343 for(auto i : node.mChildren)
345 sd.Visit(i, choices, v);
354 SceneDefinition::SceneDefinition()
359 EnsureJointDebugShaderCreated();
363 SceneDefinition::SceneDefinition(SceneDefinition&& other)
364 : mNodes(std::move(other.mNodes)),
365 mRootNodeIds(std::move(other.mRootNodeIds))
368 EnsureJointDebugShaderCreated();
372 SceneDefinition::~SceneDefinition()
378 sJointDebugShader = Shader();
383 uint32_t Dali::Scene3D::Loader::SceneDefinition::AddRootNode(Index iNode)
385 if(iNode < mNodes.size())
387 uint32_t result = mRootNodeIds.size();
388 mRootNodeIds.push_back(iNode);
393 ExceptionFlinger(ASSERT_LOCATION) << "Failed to add new root with node " << iNode << " -- index out of bounds.";
398 const std::vector<Index>& SceneDefinition::GetRoots() const
403 void SceneDefinition::RemoveRootNode(Index iRoot)
405 if(iRoot < mRootNodeIds.size())
407 mRootNodeIds.erase(mRootNodeIds.begin() + iRoot);
411 ExceptionFlinger(ASSERT_LOCATION) << "Failed to remove root " << iRoot << " -- index out of bounds.";
415 uint32_t SceneDefinition::GetNodeCount() const
417 return mNodes.size();
420 const NodeDefinition* SceneDefinition::GetNode(Index iNode) const
422 return mNodes[iNode].get();
425 NodeDefinition* SceneDefinition::GetNode(Index iNode)
427 if(iNode != Scene3D::Loader::INVALID_INDEX && iNode < mNodes.size())
429 return mNodes[iNode].get();
434 void SceneDefinition::Visit(Index iNode, const Customization::Choices& choices, NodeDefinition::IVisitor& v)
436 VisitInternal(iNode, choices, v, *this);
439 void SceneDefinition::Visit(Index iNode, const Customization::Choices& choices, NodeDefinition::IConstVisitor& v) const
441 VisitInternal(iNode, choices, v, *this);
444 void SceneDefinition::CountResourceRefs(Index iNode, const Customization::Choices& choices, ResourceRefCounts& refCounts) const
446 struct RefCounter : IResourceReceiver
448 ResourceRefCounts* refCounts;
450 void Register(ResourceType::Value type, Index id)
452 ++(*refCounts)[type][id];
456 struct : NodeDefinition::IConstVisitor
460 void Start(const NodeDefinition& n)
462 for(auto& renderable : n.mRenderables)
464 renderable->RegisterResources(counter);
468 void Finish(const NodeDefinition& n)
473 refCounterVisitor.counter.refCounts = &refCounts;
475 Visit(iNode, choices, refCounterVisitor);
478 Actor SceneDefinition::CreateNodes(Index iNode, const Customization::Choices& choices, NodeDefinition::CreateParams& params)
480 ActorCreatorVisitor actorCreatorVisitor(params);
482 Visit(iNode, choices, actorCreatorVisitor);
484 return actorCreatorVisitor.GetRoot();
487 void SceneDefinition::GetCustomizationOptions(const Customization::Choices& choices,
488 Customization::Map& outCustomizationOptions,
489 Customization::Choices* outMissingChoices) const
491 struct : NodeDefinition::IConstVisitor
493 const Customization::Choices* choices; // choices that we know about.
494 Customization::Map* options; // tags are registered here. NO OWNERSHIP.
495 Customization::Choices* missingChoices; // tags will be registered with the default 0. NO OWNERSHIP.
497 void Start(const NodeDefinition& n)
501 const std::string& tag = n.mCustomization->mTag;
502 if(missingChoices != nullptr && choices->Get(tag) == Customization::NONE)
504 missingChoices->Set(tag, 0);
507 auto customization = options->Get(tag);
510 customization = options->Set(tag, {});
512 customization->nodes.push_back(n.mName);
513 customization->numOptions = std::max(customization->numOptions,
514 static_cast<uint32_t>(n.mChildren.size()));
518 void Finish(const NodeDefinition& n)
522 } customizationRegistrationVisitor;
523 customizationRegistrationVisitor.choices = &choices;
524 customizationRegistrationVisitor.options = &outCustomizationOptions;
525 customizationRegistrationVisitor.missingChoices = outMissingChoices;
527 for(auto i : mRootNodeIds)
529 Visit(i, choices, customizationRegistrationVisitor);
533 NodeDefinition* SceneDefinition::AddNode(std::unique_ptr<NodeDefinition>&& nodeDef)
535 // add next index (to which we're about to push) as a child to the designated parent, if any.
536 if(nodeDef->mParentIdx != INVALID_INDEX)
538 mNodes[nodeDef->mParentIdx]->mChildren.push_back(mNodes.size());
541 mNodes.push_back(std::move(nodeDef));
543 return mNodes.back().get();
546 bool SceneDefinition::ReparentNode(const std::string& name, const std::string& newParentName, Index siblingOrder)
548 LOGD(("reparenting %s to %s @ %d", name.c_str(), newParentName.c_str(), siblingOrder));
550 std::unique_ptr<NodeDefinition>* nodePtr = nullptr;
551 std::unique_ptr<NodeDefinition>* newParentPtr = nullptr;
552 if(!FindNode(name, &nodePtr) || !FindNode(newParentName, &newParentPtr))
557 auto& node = *nodePtr;
558 auto iNode = std::distance(mNodes.data(), nodePtr);
560 DEBUG_ONLY(auto dumpNode = [](NodeDefinition const& n) {
561 std::ostringstream stream;
562 stream << n.mName << " (" << n.mParentIdx << "):";
563 for(auto i : n.mChildren)
567 LOGD(("%s", stream.str().c_str()));
570 // Remove node from children of previous parent (if any).
571 if(node->mParentIdx != INVALID_INDEX)
573 LOGD(("old parent:"));
574 DEBUG_ONLY(dumpNode(*mNodes[node->mParentIdx]);)
576 auto& children = mNodes[node->mParentIdx]->mChildren;
577 children.erase(std::remove(children.begin(), children.end(), iNode), children.end());
579 DEBUG_ONLY(dumpNode(*mNodes[node->mParentIdx]);)
582 // Register node to new parent.
583 LOGD(("new parent:"));
584 DEBUG_ONLY(dumpNode(**newParentPtr);)
585 auto& children = (*newParentPtr)->mChildren;
586 if(siblingOrder > children.size())
588 siblingOrder = children.size();
590 children.insert(children.begin() + siblingOrder, 1, iNode);
591 DEBUG_ONLY(dumpNode(**newParentPtr);)
593 // Update parent index.
595 DEBUG_ONLY(dumpNode(*node);)
596 auto iParent = std::distance(mNodes.data(), newParentPtr);
597 node->mParentIdx = iParent;
598 DEBUG_ONLY(dumpNode(*node);)
602 bool SceneDefinition::RemoveNode(const std::string& name)
604 std::unique_ptr<NodeDefinition>* node = nullptr;
605 if(!FindNode(name, &node))
610 // Reset node def pointers recursively.
611 auto& thisNodes = mNodes;
612 unsigned int numReset = 0;
613 std::function<void(std::unique_ptr<NodeDefinition>&)> resetFn =
614 [&thisNodes, &resetFn, &numReset](std::unique_ptr<NodeDefinition>& nd) {
615 LOGD(("resetting %d", &nd - thisNodes.data()));
616 for(auto i : nd->mChildren)
618 resetFn(thisNodes[i]);
626 // Gather indices of dead nodes into a vector which we sort on insertion.
627 std::vector<Index> offsets;
628 offsets.reserve(numReset);
629 for(auto& n : mNodes)
633 offsets.push_back(std::distance(mNodes.data(), &n));
637 // Erase dead nodes as they don't have to be processed anymore.
638 mNodes.erase(std::remove(mNodes.begin(), mNodes.end(), decltype(mNodes)::value_type()), mNodes.end());
640 // Offset all indices (parent and child) by the index they'd sort into in offsets.
643 INDEX_FOR_REMOVAL = INVALID_INDEX
645 auto offsetter = [&offsets](Index& i) {
646 auto iFind = std::lower_bound(offsets.begin(), offsets.end(), i);
647 if(iFind != offsets.end() && *iFind == i)
649 LOGD(("marking %d for removal.", i));
650 i = INDEX_FOR_REMOVAL;
655 auto distance = std::distance(offsets.begin(), iFind);
658 LOGD(("offsetting %d by %d.", i, distance));
665 for(auto& nd : mNodes)
667 bool parentOffsetResult = offsetter(nd->mParentIdx);
668 DALI_ASSERT_ALWAYS(parentOffsetResult); // since nodes were recursively removed, we should not be finding invalid parents at this point.
670 auto& children = nd->mChildren;
671 for(auto i0 = children.begin(), i1 = children.end(); i0 != i1; ++i0)
676 children.erase(std::remove(children.begin(), children.end(), INDEX_FOR_REMOVAL), children.end());
682 void SceneDefinition::GetNodeModelStack(Index index, MatrixStack& model) const
684 auto& thisNodes = mNodes;
685 std::function<void(int)> buildStack = [&model, &thisNodes, &buildStack](int i) {
686 auto node = thisNodes[i].get();
687 if(node->mParentIdx != INVALID_INDEX)
689 buildStack(node->mParentIdx);
691 model.Push(node->GetLocalSpace());
696 NodeDefinition* SceneDefinition::FindNode(const std::string& name, Index* outIndex)
698 auto iBegin = mNodes.begin();
699 auto iEnd = mNodes.end();
700 auto iFind = std::find_if(iBegin, iEnd, [&name](const std::unique_ptr<NodeDefinition>& nd) {
701 return nd->mName == name;
704 auto result = iFind != iEnd ? iFind->get() : nullptr;
705 if(result && outIndex)
707 *outIndex = std::distance(iBegin, iFind);
712 const NodeDefinition* SceneDefinition::FindNode(const std::string& name, Index* outIndex) const
714 auto iBegin = mNodes.begin();
715 auto iEnd = mNodes.end();
716 auto iFind = std::find_if(iBegin, iEnd, [&name](const std::unique_ptr<NodeDefinition>& nd) {
717 return nd->mName == name;
720 auto result = iFind != iEnd ? iFind->get() : nullptr;
721 if(result && outIndex)
723 *outIndex = std::distance(iBegin, iFind);
728 Index SceneDefinition::FindNodeIndex(const NodeDefinition& node) const
730 auto iBegin = mNodes.begin();
731 auto iEnd = mNodes.end();
732 auto iFind = std::find_if(iBegin, iEnd, [&node](const std::unique_ptr<NodeDefinition>& n) {
733 return n.get() == &node;
735 return iFind != iEnd ? std::distance(iBegin, iFind) : INVALID_INDEX;
738 void SceneDefinition::FindNodes(NodePredicate predicate, NodeConsumer consumer, unsigned int limit)
741 for(auto& defp : mNodes)
755 void SceneDefinition::FindNodes(NodePredicate predicate, ConstNodeConsumer consumer, unsigned int limit) const
758 for(auto& defp : mNodes)
772 void SceneDefinition::ApplyConstraints(Actor& root,
773 std::vector<ConstraintRequest>&& constrainables,
774 StringCallback onError) const
776 for(auto& cr : constrainables)
778 auto& nodeDef = mNodes[cr.mConstraint->mSourceIdx];
779 auto sourceName = nodeDef->mName.c_str();
780 Property::Index iTarget = cr.mTarget.GetPropertyIndex(cr.mConstraint->mProperty);
781 if(iTarget != Property::INVALID_INDEX)
783 auto propertyType = cr.mTarget.GetPropertyType(iTarget);
784 auto iFind = GetConstraintFactory().find(propertyType);
785 if(iFind == GetConstraintFactory().end())
787 onError(FormatString("node '%s': Property '%s' has unsupported type '%s'; ignored.",
789 cr.mConstraint->mProperty.c_str(),
790 PropertyTypes::GetName(propertyType)));
794 Constraint constraint = iFind->second(cr.mTarget, iTarget);
796 Actor source = root.FindChildByName(nodeDef->mName);
799 auto targetName = cr.mTarget.GetProperty(Actor::Property::NAME).Get<std::string>();
800 onError(FormatString("node '%s': Failed to locate constraint source %s@%s; ignored.",
802 cr.mConstraint->mProperty.c_str(),
803 targetName.c_str()));
806 else if(source == cr.mTarget)
808 onError(FormatString("node '%s': Cyclic constraint definition for property '%s'; ignored.",
810 cr.mConstraint->mProperty.c_str()));
814 Property::Index iSource = source.GetPropertyIndex(cr.mConstraint->mProperty);
815 constraint.AddSource(Source{source, iSource});
820 auto targetName = cr.mTarget.GetProperty(Actor::Property::NAME).Get<std::string>();
821 onError(FormatString("node '%s': Failed to create constraint for property %s@%s; ignored.",
823 cr.mConstraint->mProperty.c_str(),
824 targetName.c_str()));
829 void SceneDefinition::ConfigureSkeletonJoints(uint32_t iRoot, const SkeletonDefinition::Vector& skeletons, Actor root) const
831 // 1, For each skeleton, for each joint, walk upwards until we reach mNodes[iRoot]. If we do, record +1
832 // to the refcount of each node we have visited, in our temporary registry. Those with refcount 1
833 // are the leaves, while the most descendant node with the highest refcount is the root of the skeleton.
834 std::map<Index, std::vector<Index>> rootsJoints;
835 std::vector<Index> path;
837 for(auto& s : skeletons)
839 std::map<uint32_t, uint32_t> jointRefs;
840 for(auto& j : s.mJoints)
842 auto nodeIdx = j.mNodeIdx;
843 do // Traverse upwards and record each node we have visited until we reach the scene root.
845 path.push_back(nodeIdx);
850 auto node = GetNode(nodeIdx);
851 nodeIdx = node->mParentIdx;
852 } while(nodeIdx != INVALID_INDEX);
854 if(nodeIdx == iRoot) // If the joint is in the correct scene, increment the reference count for all visited nodes.
865 // Only record the skeleton if we have encountered the root of the current scene.
866 if(jointRefs.empty())
871 Index root = s.mRootNodeIdx;
873 auto iFind = jointRefs.find(root);
874 if(iFind != jointRefs.end())
876 maxRef = iFind->second;
879 std::vector<Index> joints;
880 for(auto& j : jointRefs) // NOTE: jointRefs are sorted, so joints will also be.
882 // The most descendant node with the highest ref count is the root of the skeleton.
883 if(j.second > maxRef || (j.second == maxRef && IsAncestor(*this, root, j.first, iRoot)))
887 RemoveFromSorted(joints, root);
890 else if(j.second == 1) // This one's a leaf.
892 InsertUniqueSorted(joints, j.first);
896 // Merge skeletons that share the same root.
897 auto& finalJoints = rootsJoints[root];
900 if(std::find_if(finalJoints.begin(), finalJoints.end(), [this, j, root](Index jj) {
901 return IsAncestor(*this, j, jj, root);
902 }) != finalJoints.end())
904 continue; // if the joint is found to be an ancestor of another joint already registered, move on.
908 while(i != root) // See if the current joint is a better leaf, i.e. descended from another leaf - which we'll then remove.
910 auto node = GetNode(i);
911 i = node->mParentIdx;
913 RemoveFromSorted(finalJoints, i);
916 InsertUniqueSorted(finalJoints, j);
920 // 2, Merge records where one root joint is descendant of another. Handle leaf node changes - remove previous
921 // leaf nodes that now have descendants, and add new ones.
922 auto iRoots = rootsJoints.begin();
923 auto iRootsEnd = rootsJoints.end();
924 while(iRoots != iRootsEnd)
926 auto i = iRoots->first;
928 while(i != iRoot) // Starting with the root joint of the skeleton, traverse upwards.
930 auto node = GetNode(i);
931 i = node->mParentIdx;
933 auto iFind = rootsJoints.find(i);
934 if(iFind != rootsJoints.end()) // Check if we've reached the root of another skeleton.
936 // Now find out which leaf of iFind is an ancestor, if any.
937 auto iFindLeaf = std::find_if(iFind->second.begin(), iFind->second.end(), [this, iRoots, iFind](Index j) {
938 return IsAncestor(*this, j, iRoots->first, iFind->first);
940 if(iFindLeaf != iFind->second.end())
942 iFind->second.erase(iFindLeaf); // Will no longer be a leaf -- remove it.
945 // Merge iRoots with iFind
946 auto& targetJoints = iFind->second;
947 if(iRoots->second.empty()) // The root is a leaf.
949 InsertUniqueSorted(targetJoints, iRoots->first);
952 for(auto j : iRoots->second)
954 InsertUniqueSorted(targetJoints, j);
958 break; // Traverse no more
962 iRoots = merged ? rootsJoints.erase(iRoots) : std::next(iRoots);
965 // 3, For each root, register joint matrices and constraints
966 for(const auto& r : rootsJoints)
968 auto node = GetNode(r.first);
969 auto rootJoint = root.FindChildByName(node->mName);
970 DALI_ASSERT_ALWAYS(!!rootJoint);
972 DALI_ASSERT_DEBUG(rootJoint.GetPropertyIndex(JOINT_MATRIX) == Property::INVALID_INDEX);
973 auto propJointMatrix = rootJoint.RegisterProperty(JOINT_MATRIX, Matrix{false});
974 Constraint constraint = Constraint::New<Matrix>(rootJoint, propJointMatrix, [](Matrix& output, const PropertyInputContainer& inputs) {
975 output.SetTransformComponents(Vector3::ONE, inputs[0]->GetQuaternion(), inputs[1]->GetVector3());
977 constraint.AddSource(Source(rootJoint, Actor::Property::ORIENTATION));
978 constraint.AddSource(Source(rootJoint, Actor::Property::POSITION));
981 for(const auto j : r.second)
984 auto joint = rootJoint.FindChildByName(node->mName);
985 ConfigureJointMatrix(joint, rootJoint, propJointMatrix);
990 void SceneDefinition::EnsureUniqueSkinningShaderInstances(ResourceBundle& resources) const
992 std::map<Index, std::map<Index, std::vector<Index*>>> skinningShaderUsers;
993 for(auto& node : mNodes)
995 for(auto& renderable : node->mRenderables)
997 ResourceReflector reflector;
998 renderable->ReflectResources(reflector);
1002 const auto& mesh = resources.mMeshes[*reflector.iMesh].first;
1003 if(mesh.IsSkinned())
1005 skinningShaderUsers[*reflector.iShader][mesh.mSkeletonIdx].push_back(reflector.iShader);
1011 // For each shader, and each skeleton using the same shader as the first skeleton,
1012 // update the shader references (from nodes with skinned meshes) with a new copy of
1013 // the shader definition from the node using the first skeleton.
1014 for(auto& users : skinningShaderUsers)
1016 auto& skeletons = users.second;
1017 auto iterSkeleton = skeletons.begin();
1018 // skipping the first skeleton.
1021 resources.mShaders.reserve(resources.mShaders.size() + std::distance(iterSkeleton, skeletons.end()));
1022 const ShaderDefinition& shaderDef = resources.mShaders[users.first].first;
1024 while(iterSkeleton != skeletons.end())
1026 Index iShader = resources.mShaders.size();
1027 resources.mShaders.push_back({shaderDef, Shader()});
1029 for(auto& i : iterSkeleton->second)
1038 void SceneDefinition::ConfigureSkinningShaders(const ResourceBundle& resources,
1040 std::vector<SkinningShaderConfigurationRequest>&& requests) const
1042 if(requests.empty())
1047 SortAndDeduplicateSkinningRequests(requests);
1049 for(auto& request : requests)
1051 auto& skeleton = resources.mSkeletons[request.mSkeletonIdx];
1052 if(skeleton.mJoints.empty())
1054 LOGD(("Skeleton %d has no joints.", request.mSkeletonIdx));
1059 for(auto& joint : skeleton.mJoints)
1061 auto node = GetNode(joint.mNodeIdx);
1062 Actor actor = rootActor.FindChildByName(node->mName);
1063 ConfigureBoneMatrix(joint.mInverseBindMatrix, actor, request.mShader, boneIdx);
1068 bool SceneDefinition::ConfigureBlendshapeShaders(const ResourceBundle& resources,
1070 std::vector<BlendshapeShaderConfigurationRequest>&& requests,
1071 StringCallback onError) const
1073 if(requests.empty())
1078 // Sort requests by shaders.
1079 std::sort(requests.begin(), requests.end());
1081 // Remove duplicates.
1082 auto i = requests.begin();
1083 auto iEnd = requests.end();
1084 Shader s = i->mShader;
1088 // Multiple identical shader instances are removed.
1089 while(i != iEnd && i->mShader == s)
1091 i->mShader = Shader();
1103 // Configure the rest.
1106 for(auto& i : requests)
1109 if(FindNode(i.mNodeName, &iNode))
1111 const auto& node = GetNode(iNode);
1113 const auto& mesh = resources.mMeshes[i.mMeshIdx];
1115 if(mesh.first.HasBlendShapes())
1117 Actor actor = rootActor.FindChildByName(node->mName);
1119 // Sets the property to be animated.
1120 BlendShapes::ConfigureProperties(mesh, i.mShader, actor);
1128 void SceneDefinition::EnsureUniqueBlendShapeShaderInstances(ResourceBundle& resources) const
1130 std::map<Index, std::map<std::string, std::vector<Index*>>> blendShapeShaderUsers;
1131 for(auto& node : mNodes)
1133 for(auto& renderable : node->mRenderables)
1135 ResourceReflector reflector;
1136 renderable->ReflectResources(reflector);
1140 const auto& mesh = resources.mMeshes[*reflector.iMesh].first;
1141 if(mesh.HasBlendShapes())
1143 blendShapeShaderUsers[*reflector.iShader][node->mName].push_back(reflector.iShader);
1149 for(auto& users : blendShapeShaderUsers)
1151 resources.mShaders.reserve(resources.mShaders.size() + users.second.size() - 1u);
1152 const ShaderDefinition& shaderDef = resources.mShaders[users.first].first;
1154 auto nodesIt = users.second.begin();
1155 auto nodesEndIt = users.second.end();
1156 // skipping the first node.
1158 while(nodesIt != nodesEndIt)
1160 Index iShader = resources.mShaders.size();
1161 resources.mShaders.push_back({shaderDef, Shader()});
1163 auto& nodes = *nodesIt;
1164 for(auto& shader : nodes.second)
1173 SceneDefinition& SceneDefinition::operator=(SceneDefinition&& other)
1175 SceneDefinition temp(std::move(other));
1176 std::swap(mNodes, temp.mNodes);
1177 std::swap(mRootNodeIds, temp.mRootNodeIds);
1181 bool SceneDefinition::FindNode(const std::string& name, std::unique_ptr<NodeDefinition>** result)
1183 // We're searching from the end assuming a higher probability of operations targeting
1184 // recently added nodes. (conf.: root, which is immovable, cannot be removed, and was
1185 // the first to be added, is index 0.)
1186 auto iFind = std::find_if(mNodes.rbegin(), mNodes.rend(), [&name](const std::unique_ptr<NodeDefinition>& nd) {
1187 return nd->mName == name;
1190 const bool success = iFind != mNodes.begin();
1191 if(success && result)
1200 } // namespace Dali::Scene3D::Loader