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