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 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(Skinning::JOINT_MATRIX);
248 if(myPropJointMatrix == Property::INVALID_INDEX)
250 myPropJointMatrix = actor.RegisterProperty(Skinning::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, ModelNode joint, ModelPrimitive primitive, Index& boneIdx)
308 // Register bone transform on shader.
309 Internal::GetImplementation(joint).SetBoneMatrix(ibm, primitive, boneIdx);
313 template<class Visitor, class SceneDefinition>
314 void VisitInternal(Index iNode, const Customization::Choices& choices, Visitor& v, SceneDefinition& sd)
316 auto& node = *sd.GetNode(iNode);
319 if(node.mCustomization)
321 if(!node.mChildren.empty())
323 auto choice = choices.Get(node.mCustomization->mTag);
324 Index i = std::min(choice != Customization::NONE ? choice : 0, static_cast<Index>(node.mChildren.size() - 1));
325 sd.Visit(node.mChildren[i], choices, v);
330 for(auto i : node.mChildren)
332 sd.Visit(i, choices, v);
341 SceneDefinition::SceneDefinition()
346 EnsureJointDebugShaderCreated();
350 SceneDefinition::SceneDefinition(SceneDefinition&& other)
351 : mNodes(std::move(other.mNodes)),
352 mRootNodeIds(std::move(other.mRootNodeIds))
355 EnsureJointDebugShaderCreated();
359 SceneDefinition::~SceneDefinition()
365 sJointDebugShader = Shader();
370 uint32_t Dali::Scene3D::Loader::SceneDefinition::AddRootNode(Index iNode)
372 if(iNode < mNodes.size())
374 uint32_t result = mRootNodeIds.size();
375 mRootNodeIds.push_back(iNode);
380 ExceptionFlinger(ASSERT_LOCATION) << "Failed to add new root with node " << iNode << " -- index out of bounds.";
385 const std::vector<Index>& SceneDefinition::GetRoots() const
390 void SceneDefinition::RemoveRootNode(Index iRoot)
392 if(iRoot < mRootNodeIds.size())
394 mRootNodeIds.erase(mRootNodeIds.begin() + iRoot);
398 ExceptionFlinger(ASSERT_LOCATION) << "Failed to remove root " << iRoot << " -- index out of bounds.";
402 uint32_t SceneDefinition::GetNodeCount() const
404 return mNodes.size();
407 const NodeDefinition* SceneDefinition::GetNode(Index iNode) const
409 return mNodes[iNode].get();
412 NodeDefinition* SceneDefinition::GetNode(Index iNode)
414 if(iNode != Scene3D::Loader::INVALID_INDEX && iNode < mNodes.size())
416 return mNodes[iNode].get();
421 void SceneDefinition::Visit(Index iNode, const Customization::Choices& choices, NodeDefinition::IVisitor& v)
423 VisitInternal(iNode, choices, v, *this);
426 void SceneDefinition::Visit(Index iNode, const Customization::Choices& choices, NodeDefinition::IConstVisitor& v) const
428 VisitInternal(iNode, choices, v, *this);
431 void SceneDefinition::CountResourceRefs(Index iNode, const Customization::Choices& choices, ResourceRefCounts& refCounts) const
433 struct RefCounter : IResourceReceiver
435 ResourceRefCounts* refCounts;
437 void Register(ResourceType::Value type, Index id)
439 ++(*refCounts)[type][id];
443 struct : NodeDefinition::IConstVisitor
447 void Start(const NodeDefinition& n)
449 for(auto& renderable : n.mRenderables)
451 renderable->RegisterResources(counter);
455 void Finish(const NodeDefinition& n)
460 refCounterVisitor.counter.refCounts = &refCounts;
462 Visit(iNode, choices, refCounterVisitor);
465 ModelNode SceneDefinition::CreateNodes(Index iNode, const Customization::Choices& choices, NodeDefinition::CreateParams& params)
467 ActorCreatorVisitor actorCreatorVisitor(params);
469 Visit(iNode, choices, actorCreatorVisitor);
471 return actorCreatorVisitor.GetRoot();
474 void SceneDefinition::GetCustomizationOptions(const Customization::Choices& choices,
475 Customization::Map& outCustomizationOptions,
476 Customization::Choices* outMissingChoices) const
478 struct : NodeDefinition::IConstVisitor
480 const Customization::Choices* choices; // choices that we know about.
481 Customization::Map* options; // tags are registered here. NO OWNERSHIP.
482 Customization::Choices* missingChoices; // tags will be registered with the default 0. NO OWNERSHIP.
484 void Start(const NodeDefinition& n)
488 const std::string& tag = n.mCustomization->mTag;
489 if(missingChoices != nullptr && choices->Get(tag) == Customization::NONE)
491 missingChoices->Set(tag, 0);
494 auto customization = options->Get(tag);
497 customization = options->Set(tag, {});
499 customization->nodes.push_back(n.mName);
500 customization->numOptions = std::max(customization->numOptions,
501 static_cast<uint32_t>(n.mChildren.size()));
505 void Finish(const NodeDefinition& n)
509 } customizationRegistrationVisitor;
510 customizationRegistrationVisitor.choices = &choices;
511 customizationRegistrationVisitor.options = &outCustomizationOptions;
512 customizationRegistrationVisitor.missingChoices = outMissingChoices;
514 for(auto i : mRootNodeIds)
516 Visit(i, choices, customizationRegistrationVisitor);
520 NodeDefinition* SceneDefinition::AddNode(std::unique_ptr<NodeDefinition>&& nodeDef)
522 // add next index (to which we're about to push) as a child to the designated parent, if any.
523 if(nodeDef->mParentIdx != INVALID_INDEX)
525 mNodes[nodeDef->mParentIdx]->mChildren.push_back(mNodes.size());
528 mNodes.push_back(std::move(nodeDef));
530 return mNodes.back().get();
533 bool SceneDefinition::ReparentNode(const std::string& name, const std::string& newParentName, Index siblingOrder)
535 LOGD(("reparenting %s to %s @ %d", name.c_str(), newParentName.c_str(), siblingOrder));
537 std::unique_ptr<NodeDefinition>* nodePtr = nullptr;
538 std::unique_ptr<NodeDefinition>* newParentPtr = nullptr;
539 if(!FindNode(name, &nodePtr) || !FindNode(newParentName, &newParentPtr))
544 auto& node = *nodePtr;
545 auto iNode = std::distance(mNodes.data(), nodePtr);
547 DEBUG_ONLY(auto dumpNode = [](NodeDefinition const& n) {
548 std::ostringstream stream;
549 stream << n.mName << " (" << n.mParentIdx << "):";
550 for(auto i : n.mChildren)
554 LOGD(("%s", stream.str().c_str()));
557 // Remove node from children of previous parent (if any).
558 if(node->mParentIdx != INVALID_INDEX)
560 LOGD(("old parent:"));
561 DEBUG_ONLY(dumpNode(*mNodes[node->mParentIdx]);)
563 auto& children = mNodes[node->mParentIdx]->mChildren;
564 children.erase(std::remove(children.begin(), children.end(), iNode), children.end());
566 DEBUG_ONLY(dumpNode(*mNodes[node->mParentIdx]);)
569 // Register node to new parent.
570 LOGD(("new parent:"));
571 DEBUG_ONLY(dumpNode(**newParentPtr);)
572 auto& children = (*newParentPtr)->mChildren;
573 if(siblingOrder > children.size())
575 siblingOrder = children.size();
577 children.insert(children.begin() + siblingOrder, 1, iNode);
578 DEBUG_ONLY(dumpNode(**newParentPtr);)
580 // Update parent index.
582 DEBUG_ONLY(dumpNode(*node);)
583 auto iParent = std::distance(mNodes.data(), newParentPtr);
584 node->mParentIdx = iParent;
585 DEBUG_ONLY(dumpNode(*node);)
589 bool SceneDefinition::RemoveNode(const std::string& name)
591 std::unique_ptr<NodeDefinition>* node = nullptr;
592 if(!FindNode(name, &node))
597 // Reset node def pointers recursively.
598 auto& thisNodes = mNodes;
599 unsigned int numReset = 0;
600 std::function<void(std::unique_ptr<NodeDefinition>&)> resetFn =
601 [&thisNodes, &resetFn, &numReset](std::unique_ptr<NodeDefinition>& nd) {
602 LOGD(("resetting %d", &nd - thisNodes.data()));
603 for(auto i : nd->mChildren)
605 resetFn(thisNodes[i]);
613 // Gather indices of dead nodes into a vector which we sort on insertion.
614 std::vector<Index> offsets;
615 offsets.reserve(numReset);
616 for(auto& n : mNodes)
620 offsets.push_back(std::distance(mNodes.data(), &n));
624 // Erase dead nodes as they don't have to be processed anymore.
625 mNodes.erase(std::remove(mNodes.begin(), mNodes.end(), decltype(mNodes)::value_type()), mNodes.end());
627 // Offset all indices (parent and child) by the index they'd sort into in offsets.
630 INDEX_FOR_REMOVAL = INVALID_INDEX
632 auto offsetter = [&offsets](Index& i) {
633 auto iFind = std::lower_bound(offsets.begin(), offsets.end(), i);
634 if(iFind != offsets.end() && *iFind == i)
636 LOGD(("marking %d for removal.", i));
637 i = INDEX_FOR_REMOVAL;
642 auto distance = std::distance(offsets.begin(), iFind);
645 LOGD(("offsetting %d by %d.", i, distance));
652 for(auto& nd : mNodes)
654 bool parentOffsetResult = offsetter(nd->mParentIdx);
655 DALI_ASSERT_ALWAYS(parentOffsetResult); // since nodes were recursively removed, we should not be finding invalid parents at this point.
657 auto& children = nd->mChildren;
658 for(auto i0 = children.begin(), i1 = children.end(); i0 != i1; ++i0)
663 children.erase(std::remove(children.begin(), children.end(), INDEX_FOR_REMOVAL), children.end());
669 void SceneDefinition::GetNodeModelStack(Index index, MatrixStack& model) const
671 auto& thisNodes = mNodes;
672 std::function<void(int)> buildStack = [&model, &thisNodes, &buildStack](int i) {
673 auto node = thisNodes[i].get();
674 if(node->mParentIdx != INVALID_INDEX)
676 buildStack(node->mParentIdx);
678 model.Push(node->GetLocalSpace());
683 NodeDefinition* SceneDefinition::FindNode(const std::string& name, Index* outIndex)
685 auto iBegin = mNodes.begin();
686 auto iEnd = mNodes.end();
687 auto iFind = std::find_if(iBegin, iEnd, [&name](const std::unique_ptr<NodeDefinition>& nd) {
688 return nd->mName == name;
691 auto result = iFind != iEnd ? iFind->get() : nullptr;
692 if(result && outIndex)
694 *outIndex = std::distance(iBegin, iFind);
699 const NodeDefinition* SceneDefinition::FindNode(const std::string& name, Index* outIndex) const
701 auto iBegin = mNodes.begin();
702 auto iEnd = mNodes.end();
703 auto iFind = std::find_if(iBegin, iEnd, [&name](const std::unique_ptr<NodeDefinition>& nd) {
704 return nd->mName == name;
707 auto result = iFind != iEnd ? iFind->get() : nullptr;
708 if(result && outIndex)
710 *outIndex = std::distance(iBegin, iFind);
715 Index SceneDefinition::FindNodeIndex(const NodeDefinition& node) const
717 auto iBegin = mNodes.begin();
718 auto iEnd = mNodes.end();
719 auto iFind = std::find_if(iBegin, iEnd, [&node](const std::unique_ptr<NodeDefinition>& n) {
720 return n.get() == &node;
722 return iFind != iEnd ? std::distance(iBegin, iFind) : INVALID_INDEX;
725 void SceneDefinition::FindNodes(NodePredicate predicate, NodeConsumer consumer, unsigned int limit)
728 for(auto& defp : mNodes)
742 void SceneDefinition::FindNodes(NodePredicate predicate, ConstNodeConsumer consumer, unsigned int limit) const
745 for(auto& defp : mNodes)
759 void SceneDefinition::ApplyConstraints(Actor& root,
760 std::vector<ConstraintRequest>&& constrainables,
761 StringCallback onError) const
763 for(auto& cr : constrainables)
765 auto& nodeDef = mNodes[cr.mConstraint->mSourceIdx];
766 auto sourceName = nodeDef->mName.c_str();
767 Property::Index iTarget = cr.mTarget.GetPropertyIndex(cr.mConstraint->mProperty);
768 if(iTarget != Property::INVALID_INDEX)
770 auto propertyType = cr.mTarget.GetPropertyType(iTarget);
771 auto iFind = GetConstraintFactory().find(propertyType);
772 if(iFind == GetConstraintFactory().end())
774 onError(FormatString("node '%s': Property '%s' has unsupported type '%s'; ignored.",
776 cr.mConstraint->mProperty.c_str(),
777 PropertyTypes::GetName(propertyType)));
781 Constraint constraint = iFind->second(cr.mTarget, iTarget);
783 Actor source = root.FindChildByName(nodeDef->mName);
786 auto targetName = cr.mTarget.GetProperty(Actor::Property::NAME).Get<std::string>();
787 onError(FormatString("node '%s': Failed to locate constraint source %s@%s; ignored.",
789 cr.mConstraint->mProperty.c_str(),
790 targetName.c_str()));
793 else if(source == cr.mTarget)
795 onError(FormatString("node '%s': Cyclic constraint definition for property '%s'; ignored.",
797 cr.mConstraint->mProperty.c_str()));
801 Property::Index iSource = source.GetPropertyIndex(cr.mConstraint->mProperty);
802 constraint.AddSource(Source{source, iSource});
807 auto targetName = cr.mTarget.GetProperty(Actor::Property::NAME).Get<std::string>();
808 onError(FormatString("node '%s': Failed to create constraint for property %s@%s; ignored.",
810 cr.mConstraint->mProperty.c_str(),
811 targetName.c_str()));
816 void SceneDefinition::ConfigureSkeletonJoints(uint32_t iRoot, const SkeletonDefinition::Vector& skeletons, Actor root) const
818 // 1, For each skeleton, for each joint, walk upwards until we reach mNodes[iRoot]. If we do, record +1
819 // to the refcount of each node we have visited, in our temporary registry. Those with refcount 1
820 // are the leaves, while the most descendant node with the highest refcount is the root of the skeleton.
821 std::map<Index, std::vector<Index>> rootsJoints;
822 std::vector<Index> path;
824 for(auto& s : skeletons)
826 std::map<uint32_t, uint32_t> jointRefs;
827 for(auto& j : s.mJoints)
829 auto nodeIdx = j.mNodeIdx;
830 do // Traverse upwards and record each node we have visited until we reach the scene root.
832 path.push_back(nodeIdx);
837 auto node = GetNode(nodeIdx);
838 nodeIdx = node->mParentIdx;
839 } while(nodeIdx != INVALID_INDEX);
841 if(nodeIdx == iRoot) // If the joint is in the correct scene, increment the reference count for all visited nodes.
852 // Only record the skeleton if we have encountered the root of the current scene.
853 if(jointRefs.empty())
858 Index root = s.mRootNodeIdx;
860 auto iFind = jointRefs.find(root);
861 if(iFind != jointRefs.end())
863 maxRef = iFind->second;
866 std::vector<Index> joints;
867 for(auto& j : jointRefs) // NOTE: jointRefs are sorted, so joints will also be.
869 // The most descendant node with the highest ref count is the root of the skeleton.
870 if(j.second > maxRef || (j.second == maxRef && IsAncestor(*this, root, j.first, iRoot)))
874 RemoveFromSorted(joints, root);
877 else if(j.second == 1) // This one's a leaf.
879 InsertUniqueSorted(joints, j.first);
883 // Merge skeletons that share the same root.
884 auto& finalJoints = rootsJoints[root];
887 if(std::find_if(finalJoints.begin(), finalJoints.end(), [this, j, root](Index jj) {
888 return IsAncestor(*this, j, jj, root);
889 }) != finalJoints.end())
891 continue; // if the joint is found to be an ancestor of another joint already registered, move on.
895 while(i != root) // See if the current joint is a better leaf, i.e. descended from another leaf - which we'll then remove.
897 auto node = GetNode(i);
898 i = node->mParentIdx;
900 RemoveFromSorted(finalJoints, i);
903 InsertUniqueSorted(finalJoints, j);
907 // 2, Merge records where one root joint is descendant of another. Handle leaf node changes - remove previous
908 // leaf nodes that now have descendants, and add new ones.
909 auto iRoots = rootsJoints.begin();
910 auto iRootsEnd = rootsJoints.end();
911 while(iRoots != iRootsEnd)
913 auto i = iRoots->first;
915 while(i != iRoot) // Starting with the root joint of the skeleton, traverse upwards.
917 auto node = GetNode(i);
918 i = node->mParentIdx;
920 auto iFind = rootsJoints.find(i);
921 if(iFind != rootsJoints.end()) // Check if we've reached the root of another skeleton.
923 // Now find out which leaf of iFind is an ancestor, if any.
924 auto iFindLeaf = std::find_if(iFind->second.begin(), iFind->second.end(), [this, iRoots, iFind](Index j) {
925 return IsAncestor(*this, j, iRoots->first, iFind->first);
927 if(iFindLeaf != iFind->second.end())
929 iFind->second.erase(iFindLeaf); // Will no longer be a leaf -- remove it.
932 // Merge iRoots with iFind
933 auto& targetJoints = iFind->second;
934 if(iRoots->second.empty()) // The root is a leaf.
936 InsertUniqueSorted(targetJoints, iRoots->first);
939 for(auto j : iRoots->second)
941 InsertUniqueSorted(targetJoints, j);
945 break; // Traverse no more
949 iRoots = merged ? rootsJoints.erase(iRoots) : std::next(iRoots);
952 // 3, For each root, register joint matrices and constraints
953 for(const auto& r : rootsJoints)
955 auto node = GetNode(r.first);
956 auto rootJoint = root.FindChildByName(node->mName);
957 DALI_ASSERT_ALWAYS(!!rootJoint);
959 DALI_ASSERT_DEBUG(rootJoint.GetPropertyIndex(Skinning::JOINT_MATRIX) == Property::INVALID_INDEX);
960 auto propJointMatrix = rootJoint.RegisterProperty(Skinning::JOINT_MATRIX, Matrix{false});
961 Constraint constraint = Constraint::New<Matrix>(rootJoint, propJointMatrix, [](Matrix& output, const PropertyInputContainer& inputs) {
962 output.SetTransformComponents(Vector3::ONE, inputs[0]->GetQuaternion(), inputs[1]->GetVector3());
964 constraint.AddSource(Source(rootJoint, Actor::Property::ORIENTATION));
965 constraint.AddSource(Source(rootJoint, Actor::Property::POSITION));
968 for(const auto j : r.second)
971 auto joint = rootJoint.FindChildByName(node->mName);
972 ConfigureJointMatrix(joint, rootJoint, propJointMatrix);
977 void SceneDefinition::EnsureUniqueSkinningShaderInstances(ResourceBundle& resources) const
979 std::map<Index, std::map<Index, std::vector<Index*>>> skinningShaderUsers;
980 for(auto& node : mNodes)
982 for(auto& renderable : node->mRenderables)
984 ResourceReflector reflector;
985 renderable->ReflectResources(reflector);
989 const auto& mesh = resources.mMeshes[*reflector.iMesh].first;
992 skinningShaderUsers[*reflector.iShader][mesh.mSkeletonIdx].push_back(reflector.iShader);
998 // For each shader, and each skeleton using the same shader as the first skeleton,
999 // update the shader references (from nodes with skinned meshes) with a new copy of
1000 // the shader definition from the node using the first skeleton.
1001 for(auto& users : skinningShaderUsers)
1003 auto& skeletons = users.second;
1004 auto iterSkeleton = skeletons.begin();
1005 // skipping the first skeleton.
1008 resources.mShaders.reserve(resources.mShaders.size() + std::distance(iterSkeleton, skeletons.end()));
1009 const ShaderDefinition& shaderDef = resources.mShaders[users.first].first;
1011 while(iterSkeleton != skeletons.end())
1013 Index iShader = resources.mShaders.size();
1014 resources.mShaders.push_back({shaderDef, Shader()});
1016 for(auto& i : iterSkeleton->second)
1025 void SceneDefinition::ConfigureSkinningShaders(const ResourceBundle& resources,
1027 std::vector<SkinningShaderConfigurationRequest>&& requests) const
1029 if(requests.empty())
1034 SortAndDeduplicateSkinningRequests(requests);
1036 for(auto& request : requests)
1038 auto& skeleton = resources.mSkeletons[request.mSkeletonIdx];
1039 if(skeleton.mJoints.empty())
1041 LOGD(("Skeleton %d has no joints.", request.mSkeletonIdx));
1046 for(auto& joint : skeleton.mJoints)
1048 auto node = GetNode(joint.mNodeIdx);
1049 ModelNode modelNode = ModelNode::DownCast(rootActor.FindChildByName(node->mName));
1054 ConfigureBoneMatrix(joint.mInverseBindMatrix, modelNode, request.mPrimitive, boneIdx);
1059 bool SceneDefinition::ConfigureBlendshapeShaders(const ResourceBundle& resources,
1061 std::vector<BlendshapeShaderConfigurationRequest>&& requests,
1062 StringCallback onError) const
1064 if(requests.empty())
1069 // Sort requests by shaders.
1070 std::sort(requests.begin(), requests.end());
1072 // Remove duplicates.
1073 auto i = requests.begin();
1074 auto iEnd = requests.end();
1075 Shader s = i->mShader;
1079 // Multiple identical shader instances are removed.
1080 while(i != iEnd && i->mShader == s)
1082 i->mShader = Shader();
1094 // Configure the rest.
1097 for(auto& i : requests)
1100 if(FindNode(i.mNodeName, &iNode))
1102 const auto& node = GetNode(iNode);
1104 const auto& mesh = resources.mMeshes[i.mMeshIdx];
1106 if(mesh.first.HasBlendShapes())
1108 Actor actor = rootActor.FindChildByName(node->mName);
1109 Scene3D::ModelNode node = Scene3D::ModelNode::DownCast(actor);
1114 BlendShapes::BlendShapeData data;
1115 data.components = 0x0;
1116 for(auto&& blendShape : mesh.first.mBlendShapes)
1118 data.weights.push_back(blendShape.weight);
1119 data.components |= (blendShape.deltas.IsDefined() * BlendShapes::Component::POSITIONS) |
1120 (blendShape.normals.IsDefined() * BlendShapes::Component::NORMALS) | (blendShape.tangents.IsDefined() * BlendShapes::Component::TANGENTS);
1122 for(auto&& factor : mesh.second.blendShapeUnnormalizeFactor)
1124 data.unnormalizeFactors.push_back(factor);
1126 data.version = mesh.first.mBlendShapeVersion;
1127 data.bufferOffset = mesh.second.blendShapeBufferOffset;
1128 data.mActor = actor;
1129 Internal::GetImplementation(node).SetBlendShapeData(data, i.mPrimitive);
1137 void SceneDefinition::EnsureUniqueBlendShapeShaderInstances(ResourceBundle& resources) const
1139 std::map<Index, std::map<std::string, std::vector<Index*>>> blendShapeShaderUsers;
1140 for(auto& node : mNodes)
1142 for(auto& renderable : node->mRenderables)
1144 ResourceReflector reflector;
1145 renderable->ReflectResources(reflector);
1149 const auto& mesh = resources.mMeshes[*reflector.iMesh].first;
1150 if(mesh.HasBlendShapes())
1152 blendShapeShaderUsers[*reflector.iShader][node->mName].push_back(reflector.iShader);
1158 for(auto& users : blendShapeShaderUsers)
1160 resources.mShaders.reserve(resources.mShaders.size() + users.second.size() - 1u);
1161 const ShaderDefinition& shaderDef = resources.mShaders[users.first].first;
1163 auto nodesIt = users.second.begin();
1164 auto nodesEndIt = users.second.end();
1165 // skipping the first node.
1167 while(nodesIt != nodesEndIt)
1169 Index iShader = resources.mShaders.size();
1170 resources.mShaders.push_back({shaderDef, Shader()});
1172 auto& nodes = *nodesIt;
1173 for(auto& shader : nodes.second)
1182 SceneDefinition& SceneDefinition::operator=(SceneDefinition&& other)
1184 SceneDefinition temp(std::move(other));
1185 std::swap(mNodes, temp.mNodes);
1186 std::swap(mRootNodeIds, temp.mRootNodeIds);
1190 bool SceneDefinition::FindNode(const std::string& name, std::unique_ptr<NodeDefinition>** result)
1192 // We're searching from the end assuming a higher probability of operations targeting
1193 // recently added nodes. (conf.: root, which is immovable, cannot be removed, and was
1194 // the first to be added, is index 0.)
1195 auto iFind = std::find_if(mNodes.rbegin(), mNodes.rend(), [&name](const std::unique_ptr<NodeDefinition>& nd) {
1196 return nd->mName == name;
1199 const bool success = iFind != mNodes.begin();
1200 if(success && result)
1209 } // namespace Dali::Scene3D::Loader