#include "RigAnimationVisitor" struct sort_weights { bool operator()(const std::pair &left, const std::pair &right) { // in case weights are equal, order elements by ascending bone ids if(left.second == right.second) { return left.first < right.first; } else { return left.second > right.second; } } }; void RigAnimationVisitor::apply(osg::Drawable& drawable) { // skip drawables already processed if (isProcessed(drawable)) { return; } apply(drawable.asGeometry()); setProcessed(drawable); } void RigAnimationVisitor::apply(osg::Geometry* geometry) { osgAnimation::RigGeometry* rig = dynamic_cast(geometry); if(rig) { apply(*rig); } } void RigAnimationVisitor::apply(osgAnimation::RigGeometry& rigGeometry) { // find skeleton osgAnimation::UpdateRigGeometry rigUpdater; osg::Geometry* source = rigGeometry.getSourceGeometry(); if(osgAnimation::MorphGeometry* morphGeometry = dynamic_cast(source)) { // skip normals blending when rigging a morph as targets may not have normals yet morphGeometry->setMorphNormals(false); } rigUpdater.update(0, &rigGeometry); osgAnimation::RigTransformHardware rth; rth(rigGeometry); std::vector< std::vector< std::pair > > vertexBoneWeights(rigGeometry.getVertexArray()->getNumElements()); // collect all bone/weight pairs for *all* vertices for(unsigned int i = 0 ; i < static_cast(rth.getNumVertexAttrib()) ; ++ i) { osg::Vec4Array* weights = dynamic_cast(rth.getVertexAttrib(i)); for(unsigned int k = 0 ; k < weights->getNumElements() ; ++ k) { vertexBoneWeights[k].push_back(std::pair((*weights)[k][0], (*weights)[k][1])); vertexBoneWeights[k].push_back(std::pair((*weights)[k][2], (*weights)[k][3])); } } osg::ref_ptr bones = new osg::Vec4usArray; osg::ref_ptr weights = new osg::Vec4Array; // for each vertex a partial sort to keep only n max weights (hardcoded to 4) for(unsigned int i = 0 ; i < vertexBoneWeights.size() ; ++ i) { std::vector< std::pair > maxVertexBoneWeight(4); std::partial_sort_copy(vertexBoneWeights[i].begin(), vertexBoneWeights[i].end(), maxVertexBoneWeight.begin(), maxVertexBoneWeight.end(), sort_weights()); osg::Vec4 vertexWeights; osg::Vec4us vertexBones; for(unsigned int j = 0 ; j < 4 ; ++ j) { vertexBones[j] = maxVertexBoneWeight[j].first; vertexWeights[j] = maxVertexBoneWeight[j].second; } normalizeWeight(vertexWeights); bones->push_back(vertexBones); weights->push_back(vertexWeights); } RigAnimationVisitor::boneIndices geometryBoneIndices = remapGeometryBones(*bones); applyBoneIndicesRemap(*bones, geometryBoneIndices); serializeBonesUserValues(*bones, geometryBoneIndices, rth.getBoneNameToPalette()); bones->setUserValue("bones", true); weights->setUserValue("weights", true); // attach bones & weights to source geometry during scene graph processing source->setVertexAttribArray(source->getNumVertexAttribArrays(), bones.get(), osg::Array::BIND_PER_VERTEX); source->setVertexAttribArray(source->getNumVertexAttribArrays(), weights.get(), osg::Array::BIND_PER_VERTEX); rigGeometry.setRigTransformImplementation(0); //Remove current implementation to force implementation re-init } RigAnimationVisitor::boneIndices RigAnimationVisitor::remapGeometryBones(const osg::Vec4usArray& bones) { RigAnimationVisitor::boneIndices remap; for(unsigned int i = 0 ; i < bones.getNumElements() ; ++ i) { for(unsigned int j = 0 ; j < 4 ; ++ j) { if(remap.find(bones[i][j]) == remap.end()) { remap[bones[i][j]] = static_cast(remap.size() - 1); } } } return remap; } void RigAnimationVisitor::applyBoneIndicesRemap(osg::Vec4usArray& bones, const RigAnimationVisitor::boneIndices& remap) { for(unsigned int i = 0 ; i < bones.getNumElements() ; ++ i) { RigAnimationVisitor::boneIndices::const_iterator x = remap.find(bones[i][0]), y = remap.find(bones[i][1]), z = remap.find(bones[i][2]), w = remap.find(bones[i][3]); bones[i] = osg::Vec4us(x->second, y->second, z->second, w->second); } } void RigAnimationVisitor::serializeBonesUserValues(osg::Vec4usArray& bones, const std::map& oldIndexToNewIndex, const osgAnimation::RigTransformHardware::BoneNamePaletteIndex& boneNamePaletteIndex) { // map 'global' palette index to bone name std::map oldIndexToBoneName; for(osgAnimation::RigTransformHardware::BoneNamePaletteIndex::const_iterator it = boneNamePaletteIndex.begin() ; it != boneNamePaletteIndex.end() ; ++ it) { oldIndexToBoneName[static_cast(it->second)] = it->first; } // serialize geometry 'palette index' => 'bone name' with user value using animationBone_ as // name prefix for(std::map::const_iterator it = oldIndexToNewIndex.begin() ; it != oldIndexToNewIndex.end() ; ++ it) { std::ostringstream oss; oss << "animationBone_" << it->second; bones.setUserValue(oss.str(), oldIndexToBoneName[it->first]); } } bool RigAnimationVisitor::isProcessed(osg::Drawable& node) { return _processed.find(&node) != _processed.end(); } void RigAnimationVisitor::setProcessed(osg::Drawable& node) { _processed.insert(&node); }