1/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab 2 * 3 * This application is open source and may be redistributed and/or modified 4 * freely and without restriction, both in commercial and non commercial 5 * applications, as long as this copyright notice is maintained. 6 * 7 * This application is distributed in the hope that it will be useful, 8 * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 10 * 11*/ 12 13#ifndef MOST_INFLUENCED_GEOMETRY_BY_BONE_H 14#define MOST_INFLUENCED_GEOMETRY_BY_BONE_H 15 16#include <algorithm> 17 18#include <osg/NodeVisitor> 19#include <osg/Geometry> 20#include <osg/Array> 21 22#include <osgAnimation/RigGeometry> 23#include <osgAnimation/Bone> 24 25#include "StatLogger" 26 27 28class InfluenceAttribute; 29 30//{ 31// "Bone001": { 32// Geom1: { 33// numVertexInfluenced: (int), 34// gloabalWeight: (float) 35// }, 36// Geom2: { 37// numVertexInfluenced: (int), 38// gloabalWeight: (float) 39// }, 40// ... 41// }, 42// "Bone002": { 43// Geom1: { 44// numVertexInfluenced: (int), 45// gloabalWeight: (float) 46// }, 47// Geom4: { 48// numVertexInfluenced: (int), 49// gloabalWeight: (float) 50// }, 51// ... 52// }, 53// ... 54//} 55// 56//Here we store influences by bone, we will sort it and take the biggest one 57typedef std::map< osgAnimation::Bone*, std::map< osgAnimation::RigGeometry*, InfluenceAttribute > > RigGeometryInfluenceByBoneMap; 58 59typedef std::map< osgAnimation::RigGeometry*, InfluenceAttribute > BoneInfluenceMap; 60typedef std::pair< osgAnimation::RigGeometry*, InfluenceAttribute > BoneInfluence; 61typedef std::vector< BoneInfluence > BoneInfluences; 62 63 64typedef std::set< osgAnimation::RigGeometry* > RigGeometrySet; 65typedef std::set< osgAnimation::Bone* > BoneSet; 66 67//Here we simply collect all bones and all rigGeometries 68class CollectBonesAndRigGeometriesVisitor: public osg::NodeVisitor { 69 70public: 71 CollectBonesAndRigGeometriesVisitor(): 72 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) 73 {} 74 75 void apply(osg::Geometry &geometry) { 76 osgAnimation::RigGeometry *rigGeometry = dynamic_cast<osgAnimation::RigGeometry*>(&geometry); 77 if(rigGeometry) { 78 _rigGeometrySet.insert(rigGeometry); 79 } 80 traverse(geometry); 81 } 82 83 void apply(osg::MatrixTransform &node) { 84 osgAnimation::Bone *bone = dynamic_cast<osgAnimation::Bone*>(&node); 85 if(bone) { 86 _boneSet.insert(bone); 87 } 88 89 traverse(node); 90 } 91 92 RigGeometrySet& getRigGeometrySet() { 93 return _rigGeometrySet; 94 } 95 96 BoneSet& getBoneSet() { 97 return _boneSet; 98 } 99 100protected: 101 RigGeometrySet _rigGeometrySet; 102 BoneSet _boneSet; 103}; 104 105 106//Store and compute influence attributes i.e number of influenced vertex and accumulate weight 107class InfluenceAttribute { 108public: 109 InfluenceAttribute(): 110 _accumulatedWeight(0), 111 _weightCount(0) 112 {} 113 114 void addWeight(float weight) { 115 _accumulatedWeight += weight; 116 _weightCount++; 117 } 118 119 unsigned int getNumInfluencedVertex() { 120 return _weightCount; 121 } 122 123 unsigned int getNumInfluencedVertex() const { 124 return _weightCount; 125 } 126 127 float getNormalizedWeight() const { 128 if(_weightCount == 0) return 0; 129 return _accumulatedWeight / _weightCount; 130 } 131 132protected: 133 float _accumulatedWeight; 134 unsigned int _weightCount; 135}; 136 137typedef std::pair< std::string, osgAnimation::Bone* > StringBonePair; 138typedef std::pair< osgAnimation::RigGeometry*, unsigned int > RigGeometryIntPair; 139 140class BoneNameBoneMap : public std::map<std::string, osgAnimation::Bone*> { 141 142public: 143 BoneNameBoneMap(const BoneSet& bones) { 144 for(BoneSet::const_iterator bone = bones.begin(); bone != bones.end(); ++bone) { 145 insert(StringBonePair((*bone)->getName(), *bone)); 146 } 147 } 148}; 149 150 151class RigGeometryIndexMap : public std::map<osgAnimation::RigGeometry*, unsigned int> { 152 153public: 154 RigGeometryIndexMap(const RigGeometrySet& rigGeometrySet) { 155 unsigned int index = 0; 156 for(RigGeometrySet::const_iterator rigGeometry = rigGeometrySet.begin(); rigGeometry != rigGeometrySet.end(); ++rigGeometry, ++index) { 157 insert(RigGeometryIntPair(*rigGeometry, index)); 158 } 159 } 160}; 161 162 163class ComputeMostInfluencedGeometryByBone { 164 165public: 166 ComputeMostInfluencedGeometryByBone(RigGeometrySet &rigGeometrySet, BoneSet &boneSet): 167 _rigGeometrySet(rigGeometrySet), 168 _boneSet(boneSet), 169 _logger("ComputeMostInfluencedGeometryByBone::compute(...)") 170 {} 171 172 void compute() { 173 RigGeometryIndexMap rigGeometryIndexMap(_rigGeometrySet); 174 175 RigGeometryInfluenceByBoneMap ribbm; 176 computeInfluences(_boneSet, _rigGeometrySet, ribbm); 177 for(RigGeometryInfluenceByBoneMap::iterator boneInfluencePair = ribbm.begin(); boneInfluencePair != ribbm.end(); ++boneInfluencePair) { 178 osg::ref_ptr<osgAnimation::Bone> bone = boneInfluencePair->first; 179 BoneInfluenceMap boneInfluenceMap = boneInfluencePair->second; 180 BoneInfluences influences(boneInfluenceMap.begin(), boneInfluenceMap.end()); 181 182 std::sort(influences.begin(), influences.end(), sort_influences()); 183 bone->setUserValue("rigIndex", rigGeometryIndexMap [ influences.front().first ]); 184 } 185 186 RigGeometrySet &rigGeometrySet(_rigGeometrySet); 187 for(RigGeometrySet::iterator rigGeometry = rigGeometrySet.begin(); rigGeometry != rigGeometrySet.end(); ++rigGeometry) { 188 (*rigGeometry)->setUserValue("rigIndex", rigGeometryIndexMap[ *rigGeometry ]); 189 } 190 } 191 192protected: 193 void computeInfluences(const BoneSet& bones, const RigGeometrySet& rigGeometries, RigGeometryInfluenceByBoneMap& rigGeometryInfluenceByBoneMap) { 194 BoneNameBoneMap boneMap(bones); 195 196 for(RigGeometrySet::const_iterator rigGeometry = rigGeometries.begin(); rigGeometry != rigGeometries.end(); ++rigGeometry) { 197 osg::ref_ptr<osgAnimation::VertexInfluenceMap> vertexInfluenceMap = (*rigGeometry)->getInfluenceMap(); 198 199 for(osgAnimation::VertexInfluenceMap::iterator vertexInfluencePair = vertexInfluenceMap->begin(); vertexInfluencePair != vertexInfluenceMap->end(); ++vertexInfluencePair) { 200 BoneNameBoneMap::iterator bone_it = boneMap.find(vertexInfluencePair->first); 201 if(bone_it == boneMap.end()) continue; 202 osg::ref_ptr<osgAnimation::Bone> bone = bone_it->second; 203 const osgAnimation::VertexInfluence& vertexInfluence = (*vertexInfluencePair).second; 204 205 for(osgAnimation::VertexInfluence::const_iterator vertexIndexWeight = vertexInfluence.begin(); vertexIndexWeight != vertexInfluence.end(); ++vertexIndexWeight) { 206 rigGeometryInfluenceByBoneMap[bone.get()][*rigGeometry].addWeight((*vertexIndexWeight).second); 207 } 208 } 209 } 210 } 211 212 struct sort_influences { 213 //We sort influences by number of influenced vertex first and then by normalized weight (number_of_vertex_influence / accumulated_weight) 214 //i.e we choose to keep geometries with many vertex insted of geometries with high normalized weight, it makes more sense for geometry 215 //selection via bone influence box @see AABBonBoneVisitor class 216 bool operator()(const BoneInfluence &a, const BoneInfluence &b) { 217 return (a.second.getNumInfluencedVertex() > b.second.getNumInfluencedVertex()) || 218 (a.second.getNumInfluencedVertex() == b.second.getNumInfluencedVertex() && \ 219 a.second.getNormalizedWeight() > b.second.getNormalizedWeight()); 220 } 221 }; 222 223 RigGeometrySet &_rigGeometrySet; 224 BoneSet &_boneSet; 225 StatLogger _logger; 226}; 227 228 229#endif // MOST_INFLUENCED_GEOMETRY_BY_BONE_H 230