-void SceneDefinition::ConfigureSkeletonJoints(uint32_t iRoot, const SkeletonDefinition::Vector& skeletons, Actor root) const
-{
- // 1, For each skeleton, for each joint, walk upwards until we reach mNodes[iRoot]. If we do, record +1
- // to the refcount of each node we have visited, in our temporary registry. Those with refcount 1
- // are the leaves, while the most descendant node with the highest refcount is the root of the skeleton.
- std::map<Index, std::vector<Index>> rootsJoints;
- std::vector<Index> path;
- path.reserve(16);
- for(auto& s : skeletons)
- {
- std::map<uint32_t, uint32_t> jointRefs;
- for(auto& j : s.mJoints)
- {
- auto nodeIdx = j.mNodeIdx;
- do // Traverse upwards and record each node we have visited until we reach the scene root.
- {
- path.push_back(nodeIdx);
- if(nodeIdx == iRoot)
- {
- break;
- }
- auto node = GetNode(nodeIdx);
- nodeIdx = node->mParentIdx;
- } while(nodeIdx != INVALID_INDEX);
-
- if(nodeIdx == iRoot) // If the joint is in the correct scene, increment the reference count for all visited nodes.
- {
- for(auto i : path)
- {
- ++jointRefs[i];
- }
- }
-
- path.clear();
- }
-
- // Only record the skeleton if we have encountered the root of the current scene.
- if(jointRefs.empty())
- {
- continue;
- }
-
- Index root = s.mRootNodeIdx;
- uint32_t maxRef = 0;
- auto iFind = jointRefs.find(root);
- if(iFind != jointRefs.end())
- {
- maxRef = iFind->second;
- }
-
- std::vector<Index> joints;
- for(auto& j : jointRefs) // NOTE: jointRefs are sorted, so joints will also be.
- {
- // The most descendant node with the highest ref count is the root of the skeleton.
- if(j.second > maxRef || (j.second == maxRef && IsAncestor(*this, root, j.first, iRoot)))
- {
- maxRef = j.second;
-
- RemoveFromSorted(joints, root);
- root = j.first;
- }
- else if(j.second == 1) // This one's a leaf.
- {
- InsertUniqueSorted(joints, j.first);
- }
- }
-
- // Merge skeletons that share the same root.
- auto& finalJoints = rootsJoints[root];
- for(auto j : joints)
- {
- if(std::find_if(finalJoints.begin(), finalJoints.end(), [this, j, root](Index jj) {
- return IsAncestor(*this, j, jj, root);
- }) != finalJoints.end())
- {
- continue; // if the joint is found to be an ancestor of another joint already registered, move on.
- }
-
- auto i = j;
- while(i != root) // See if the current joint is a better leaf, i.e. descended from another leaf - which we'll then remove.
- {
- auto node = GetNode(i);
- i = node->mParentIdx;
-
- RemoveFromSorted(finalJoints, i);
- }
-
- InsertUniqueSorted(finalJoints, j);
- }
- }
-
- // 2, Merge records where one root joint is descendant of another. Handle leaf node changes - remove previous
- // leaf nodes that now have descendants, and add new ones.
- auto iRoots = rootsJoints.begin();
- auto iRootsEnd = rootsJoints.end();
- while(iRoots != iRootsEnd)
- {
- auto i = iRoots->first;
- bool merged = false;
- while(i != iRoot) // Starting with the root joint of the skeleton, traverse upwards.
- {
- auto node = GetNode(i);
- i = node->mParentIdx;
-
- auto iFind = rootsJoints.find(i);
- if(iFind != rootsJoints.end()) // Check if we've reached the root of another skeleton.
- {
- // Now find out which leaf of iFind is an ancestor, if any.
- auto iFindLeaf = std::find_if(iFind->second.begin(), iFind->second.end(), [this, iRoots, iFind](Index j) {
- return IsAncestor(*this, j, iRoots->first, iFind->first);
- });
- if(iFindLeaf != iFind->second.end())
- {
- iFind->second.erase(iFindLeaf); // Will no longer be a leaf -- remove it.
- }
-
- // Merge iRoots with iFind
- auto& targetJoints = iFind->second;
- if(iRoots->second.empty()) // The root is a leaf.
- {
- InsertUniqueSorted(targetJoints, iRoots->first);
- }
- else
- for(auto j : iRoots->second)
- {
- InsertUniqueSorted(targetJoints, j);
- }
-
- merged = true;
- break; // Traverse no more
- }
- }
-
- iRoots = merged ? rootsJoints.erase(iRoots) : std::next(iRoots);
- }
-
- // 3, For each root, register joint matrices and constraints
- for(const auto& r : rootsJoints)
- {
- auto node = GetNode(r.first);
- auto rootJoint = root.FindChildByName(node->mName);
- DALI_ASSERT_ALWAYS(!!rootJoint);
-
- DALI_ASSERT_DEBUG(rootJoint.GetPropertyIndex(Skinning::JOINT_MATRIX) == Property::INVALID_INDEX);
- auto propJointMatrix = rootJoint.RegisterProperty(Skinning::JOINT_MATRIX, Matrix{false});
- Constraint constraint = Constraint::New<Matrix>(rootJoint, propJointMatrix, [](Matrix& output, const PropertyInputContainer& inputs) {
- output.SetTransformComponents(Vector3::ONE, inputs[0]->GetQuaternion(), inputs[1]->GetVector3());
- });
- constraint.AddSource(Source(rootJoint, Actor::Property::ORIENTATION));
- constraint.AddSource(Source(rootJoint, Actor::Property::POSITION));
- constraint.Apply();
-
- for(const auto j : r.second)
- {
- node = GetNode(j);
- auto joint = rootJoint.FindChildByName(node->mName);
- ConfigureJointMatrix(joint, rootJoint, propJointMatrix);
- }
- }
-}
-