Files
OpenSceneGraph/src/osgPlugins/gles/RigAnimationVisitor.cpp
2016-07-12 11:54:51 +02:00

156 lines
6.1 KiB
C++

#include "RigAnimationVisitor"
struct sort_weights {
bool operator()(const std::pair<unsigned int, float> &left, const std::pair<unsigned int, float> &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<osgAnimation::RigGeometry*>(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<osgAnimation::MorphGeometry*>(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<unsigned int, float> > > vertexBoneWeights(rigGeometry.getVertexArray()->getNumElements());
// collect all bone/weight pairs for *all* vertices
for(unsigned int i = 0 ; i < static_cast<unsigned int>(rth.getNumVertexAttrib()) ; ++ i) {
osg::Vec4Array* weights = dynamic_cast<osg::Vec4Array*>(rth.getVertexAttrib(i));
for(unsigned int k = 0 ; k < weights->getNumElements() ; ++ k) {
vertexBoneWeights[k].push_back(std::pair<unsigned int, float>((*weights)[k][0], (*weights)[k][1]));
vertexBoneWeights[k].push_back(std::pair<unsigned int, float>((*weights)[k][2], (*weights)[k][3]));
}
}
osg::ref_ptr<osg::Vec4usArray> bones = new osg::Vec4usArray;
osg::ref_ptr<osg::Vec4Array> 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<unsigned int, float> > 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<unsigned short>(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<unsigned int, unsigned short>& oldIndexToNewIndex,
const osgAnimation::RigTransformHardware::BoneNamePaletteIndex& boneNamePaletteIndex) {
// map 'global' palette index to bone name
std::map<unsigned int, std::string> oldIndexToBoneName;
for(osgAnimation::RigTransformHardware::BoneNamePaletteIndex::const_iterator it = boneNamePaletteIndex.begin() ;
it != boneNamePaletteIndex.end() ; ++ it) {
oldIndexToBoneName[static_cast<unsigned int>(it->second)] = it->first;
}
// serialize geometry 'palette index' => 'bone name' with user value using animationBone_ as
// name prefix
for(std::map<unsigned int, unsigned short>::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);
}