230 lines
7.7 KiB
C++
230 lines
7.7 KiB
C++
/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab
|
|
*
|
|
* This application is open source and may be redistributed and/or modified
|
|
* freely and without restriction, both in commercial and non commercial
|
|
* applications, as long as this copyright notice is maintained.
|
|
*
|
|
* This application is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
*
|
|
*/
|
|
|
|
#ifndef MOST_INFLUENCED_GEOMETRY_BY_BONE_H
|
|
#define MOST_INFLUENCED_GEOMETRY_BY_BONE_H
|
|
|
|
#include <algorithm>
|
|
|
|
#include <osg/NodeVisitor>
|
|
#include <osg/Geometry>
|
|
#include <osg/Array>
|
|
|
|
#include <osgAnimation/RigGeometry>
|
|
#include <osgAnimation/Bone>
|
|
|
|
#include "StatLogger"
|
|
|
|
|
|
class InfluenceAttribute;
|
|
|
|
//{
|
|
// "Bone001": {
|
|
// Geom1: {
|
|
// numVertexInfluenced: (int),
|
|
// gloabalWeight: (float)
|
|
// },
|
|
// Geom2: {
|
|
// numVertexInfluenced: (int),
|
|
// gloabalWeight: (float)
|
|
// },
|
|
// ...
|
|
// },
|
|
// "Bone002": {
|
|
// Geom1: {
|
|
// numVertexInfluenced: (int),
|
|
// gloabalWeight: (float)
|
|
// },
|
|
// Geom4: {
|
|
// numVertexInfluenced: (int),
|
|
// gloabalWeight: (float)
|
|
// },
|
|
// ...
|
|
// },
|
|
// ...
|
|
//}
|
|
//
|
|
//Here we store influences by bone, we will sort it and take the biggest one
|
|
typedef std::map< osgAnimation::Bone*, std::map< osgAnimation::RigGeometry*, InfluenceAttribute > > RigGeometryInfluenceByBoneMap;
|
|
|
|
typedef std::map< osgAnimation::RigGeometry*, InfluenceAttribute > BoneInfluenceMap;
|
|
typedef std::pair< osgAnimation::RigGeometry*, InfluenceAttribute > BoneInfluence;
|
|
typedef std::vector< BoneInfluence > BoneInfluences;
|
|
|
|
|
|
typedef std::set< osgAnimation::RigGeometry* > RigGeometrySet;
|
|
typedef std::set< osgAnimation::Bone* > BoneSet;
|
|
|
|
//Here we simply collect all bones and all rigGeometries
|
|
class CollectBonesAndRigGeometriesVisitor: public osg::NodeVisitor {
|
|
|
|
public:
|
|
CollectBonesAndRigGeometriesVisitor():
|
|
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
|
|
{}
|
|
|
|
void apply(osg::Geometry &geometry) {
|
|
osgAnimation::RigGeometry *rigGeometry = dynamic_cast<osgAnimation::RigGeometry*>(&geometry);
|
|
if(rigGeometry) {
|
|
_rigGeometrySet.insert(rigGeometry);
|
|
}
|
|
traverse(geometry);
|
|
}
|
|
|
|
void apply(osg::MatrixTransform &node) {
|
|
osgAnimation::Bone *bone = dynamic_cast<osgAnimation::Bone*>(&node);
|
|
if(bone) {
|
|
_boneSet.insert(bone);
|
|
}
|
|
|
|
traverse(node);
|
|
}
|
|
|
|
RigGeometrySet& getRigGeometrySet() {
|
|
return _rigGeometrySet;
|
|
}
|
|
|
|
BoneSet& getBoneSet() {
|
|
return _boneSet;
|
|
}
|
|
|
|
protected:
|
|
RigGeometrySet _rigGeometrySet;
|
|
BoneSet _boneSet;
|
|
};
|
|
|
|
|
|
//Store and compute influence attributes i.e number of influenced vertex and accumulate weight
|
|
class InfluenceAttribute {
|
|
public:
|
|
InfluenceAttribute():
|
|
_accumulatedWeight(0),
|
|
_weightCount(0)
|
|
{}
|
|
|
|
void addWeight(float weight) {
|
|
_accumulatedWeight += weight;
|
|
_weightCount++;
|
|
}
|
|
|
|
unsigned int getNumInfluencedVertex() {
|
|
return _weightCount;
|
|
}
|
|
|
|
unsigned int getNumInfluencedVertex() const {
|
|
return _weightCount;
|
|
}
|
|
|
|
float getNormalizedWeight() const {
|
|
if(_weightCount == 0) return 0;
|
|
return _accumulatedWeight / _weightCount;
|
|
}
|
|
|
|
protected:
|
|
float _accumulatedWeight;
|
|
unsigned int _weightCount;
|
|
};
|
|
|
|
typedef std::pair< std::string, osgAnimation::Bone* > StringBonePair;
|
|
typedef std::pair< osgAnimation::RigGeometry*, unsigned int > RigGeometryIntPair;
|
|
|
|
class BoneNameBoneMap : public std::map<std::string, osgAnimation::Bone*> {
|
|
|
|
public:
|
|
BoneNameBoneMap(const BoneSet& bones) {
|
|
for(BoneSet::iterator bone = bones.begin(); bone != bones.end(); ++bone) {
|
|
insert(StringBonePair((*bone)->getName(), *bone));
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
class RigGeometryIndexMap : public std::map<osgAnimation::RigGeometry*, unsigned int> {
|
|
|
|
public:
|
|
RigGeometryIndexMap(const RigGeometrySet& rigGeometrySet) {
|
|
unsigned int index = 0;
|
|
for(RigGeometrySet::iterator rigGeometry = rigGeometrySet.begin(); rigGeometry != rigGeometrySet.end(); ++rigGeometry, ++index) {
|
|
insert(RigGeometryIntPair(*rigGeometry, index));
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
class ComputeMostInfluencedGeometryByBone {
|
|
|
|
public:
|
|
ComputeMostInfluencedGeometryByBone(RigGeometrySet &rigGeometrySet, BoneSet &boneSet):
|
|
_rigGeometrySet(rigGeometrySet),
|
|
_boneSet(boneSet),
|
|
_logger("ComputeMostInfluencedGeometryByBone::compute(...)")
|
|
{}
|
|
|
|
void compute() {
|
|
RigGeometryIndexMap rigGeometryIndexMap(_rigGeometrySet);
|
|
|
|
RigGeometryInfluenceByBoneMap ribbm;
|
|
computeInfluences(_boneSet, _rigGeometrySet, ribbm);
|
|
for(RigGeometryInfluenceByBoneMap::iterator boneInfluencePair = ribbm.begin(); boneInfluencePair != ribbm.end(); ++boneInfluencePair) {
|
|
osg::ref_ptr<osgAnimation::Bone> bone = boneInfluencePair->first;
|
|
BoneInfluenceMap boneInfluenceMap = boneInfluencePair->second;
|
|
BoneInfluences influences(boneInfluenceMap.begin(), boneInfluenceMap.end());
|
|
|
|
std::sort(influences.begin(), influences.end(), sort_influences());
|
|
bone->setUserValue("rigIndex", rigGeometryIndexMap [ influences.front().first ]);
|
|
}
|
|
|
|
RigGeometrySet &rigGeometrySet(_rigGeometrySet);
|
|
for(RigGeometrySet::iterator rigGeometry = rigGeometrySet.begin(); rigGeometry != rigGeometrySet.end(); ++rigGeometry) {
|
|
(*rigGeometry)->setUserValue("rigIndex", rigGeometryIndexMap[ *rigGeometry ]);
|
|
}
|
|
}
|
|
|
|
protected:
|
|
void computeInfluences(const BoneSet& bones, const RigGeometrySet& rigGeometries, RigGeometryInfluenceByBoneMap& rigGeometryInfluenceByBoneMap) {
|
|
BoneNameBoneMap boneMap(bones);
|
|
|
|
for(RigGeometrySet::const_iterator rigGeometry = rigGeometries.begin(); rigGeometry != rigGeometries.end(); ++rigGeometry) {
|
|
osg::ref_ptr<osgAnimation::VertexInfluenceMap> vertexInfluenceMap = (*rigGeometry)->getInfluenceMap();
|
|
|
|
for(osgAnimation::VertexInfluenceMap::iterator vertexInfluencePair = vertexInfluenceMap->begin(); vertexInfluencePair != vertexInfluenceMap->end(); ++vertexInfluencePair) {
|
|
BoneNameBoneMap::iterator bone_it = boneMap.find(vertexInfluencePair->first);
|
|
if(bone_it == boneMap.end()) continue;
|
|
osg::ref_ptr<osgAnimation::Bone> bone = bone_it->second;
|
|
const osgAnimation::VertexInfluence& vertexInfluence = (*vertexInfluencePair).second;
|
|
|
|
for(osgAnimation::VertexInfluence::const_iterator vertexIndexWeight = vertexInfluence.begin(); vertexIndexWeight != vertexInfluence.end(); ++vertexIndexWeight) {
|
|
rigGeometryInfluenceByBoneMap[bone.get()][*rigGeometry].addWeight((*vertexIndexWeight).second);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct sort_influences {
|
|
//We sort influences by number of influenced vertex first and then by normalized weight (number_of_vertex_influence / accumulated_weight)
|
|
//i.e we choose to keep geometries with many vertex insted of geometries with high normalized weight, it makes more sense for geometry
|
|
//selection via bone influence box @see AABBonBoneVisitor class
|
|
bool operator()(const BoneInfluence &a, const BoneInfluence &b) {
|
|
return (a.second.getNumInfluencedVertex() > b.second.getNumInfluencedVertex()) ||
|
|
(a.second.getNumInfluencedVertex() == b.second.getNumInfluencedVertex() && \
|
|
a.second.getNormalizedWeight() > b.second.getNormalizedWeight());
|
|
}
|
|
};
|
|
|
|
RigGeometrySet &_rigGeometrySet;
|
|
BoneSet &_boneSet;
|
|
StatLogger _logger;
|
|
};
|
|
|
|
|
|
#endif // MOST_INFLUENCED_GEOMETRY_BY_BONE_H
|