root/OpenSceneGraph/trunk/src/osgAnimation/RigTransformHardware.cpp @ 10693

Revision 10693, 10.1 kB (checked in by cedricpinson, 5 years ago)

From Cedric Pinson, The following commit include:
* Refactore of RigGeometry? to support hardware skinning
* Refactore of Timeline to split Action in differents files
* Add example how to use hardware skinning

RevLine 
[10693]1/*  -*-c++-*-
2 *  Copyright (C) 2009 Cedric Pinson <cedric.pinson@plopbyte.net>
3 *
4 * This library is open source and may be redistributed and/or modified under 
5 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
6 * (at your option) any later version.  The full license is in LICENSE file
7 * included with this distribution, and on the openscenegraph.org website.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * OpenSceneGraph Public License for more details.
13 */
14
15#include <osgAnimation/RigTransformHardware>
16#include <osgAnimation/RigGeometry>
17#include <sstream>
18
19using namespace osgAnimation;
20
21osg::Vec4Array* RigTransformHardware::getVertexAttrib(int index)
22{
23    if (index >= (int)_boneWeightAttribArrays.size())
24        return 0;
25    return _boneWeightAttribArrays[index].get();
26}
27
28int RigTransformHardware::getNumVertexAttrib()
29{
30    return _boneWeightAttribArrays.size();
31}
32
33osg::Uniform* RigTransformHardware::getMatrixPaletteUniform()
34{
35    return _uniformMatrixPalette.get();
36}
37
38
39void RigTransformHardware::computeMatrixPaletteUniform(const osg::Matrix& transformFromSkeletonToGeometry, const osg::Matrix& invTransformFromSkeletonToGeometry)
40{
41    for (int i = 0; i < (int)_bonePalette.size(); i++)
42    {
43        osg::ref_ptr<Bone> bone = _bonePalette[i].get();
44        const osg::Matrix& invBindMatrix = bone->getInvBindMatrixInSkeletonSpace();
45        const osg::Matrix& boneMatrix = bone->getMatrixInSkeletonSpace();
46        osg::Matrix resultBoneMatrix = invBindMatrix * boneMatrix;
47        osg::Matrix result =  transformFromSkeletonToGeometry * resultBoneMatrix * invTransformFromSkeletonToGeometry;
48        if (!_uniformMatrixPalette->setElement(i, result))
49            osg::notify(osg::WARN) << "RigTransformHardware::computeUniformMatrixPalette can't set uniform at " << i << " elements" << std::endl;
50    }
51}
52       
53
54int RigTransformHardware::getNumBonesPerVertex() const { return _bonesPerVertex;}
55int RigTransformHardware::getNumVertexes() const { return _nbVertexes;}
56
57bool RigTransformHardware::createPalette(int nbVertexes, BoneMap boneMap, const VertexInfluenceSet::VertexIndexToBoneWeightMap& vertexIndexToBoneWeightMap)
58{
59    typedef std::map<std::string, int> BoneNameCountMap;
60    typedef std::map<std::string, int> BoneNamePaletteIndex;
61    BoneNamePaletteIndex bname2palette;
62    BonePalette palette;
63    BoneNameCountMap boneNameCountMap;
64
65    // init vertex attribute data
66    VertexIndexWeightList vertexIndexWeight;
67    vertexIndexWeight.resize(nbVertexes);
68
69    int maxBonePerVertex = 0;
70    for (VertexInfluenceSet::VertexIndexToBoneWeightMap::const_iterator it = vertexIndexToBoneWeightMap.begin(); it != vertexIndexToBoneWeightMap.end(); it++)
71    {
72        int vertexIndex = it->first;
73        const VertexInfluenceSet::BoneWeightList& boneWeightList = it->second;
74        int bonesForThisVertex = 0;
75        for (VertexInfluenceSet::BoneWeightList::const_iterator it = boneWeightList.begin(); it != boneWeightList.end(); it++)
76        {
77            const VertexInfluenceSet::BoneWeight& bw = *it;
78            if (boneNameCountMap.find(bw.getBoneName()) != boneNameCountMap.end())
79            {
80                boneNameCountMap[bw.getBoneName()]++;
81                bonesForThisVertex++; // count max number of bones per vertexes
82                vertexIndexWeight[vertexIndex].push_back(IndexWeightEntry(bname2palette[bw.getBoneName()],bw.getWeight()));
83            }
84            else if (fabs(bw.getWeight()) > 1e-2) // dont use bone with weight too small
85            {
86                if (boneMap.find(bw.getBoneName()) == boneMap.end())
87                {
88                    osg::notify(osg::WARN) << "RigTransformHardware::createPalette can't find bone " << bw.getBoneName() << " skip this influence" << std::endl;
89                    continue;
90                }
91                boneNameCountMap[bw.getBoneName()] = 1; // for stats
92                bonesForThisVertex++;
93                palette.push_back(boneMap[bw.getBoneName()]);
94                bname2palette[bw.getBoneName()] = palette.size()-1;
95                vertexIndexWeight[vertexIndex].push_back(IndexWeightEntry(bname2palette[bw.getBoneName()],bw.getWeight()));
96            }
97            else
98            {
99                osg::notify(osg::WARN) << "RigTransformHardware::createPalette Bone " << bw.getBoneName() << " has a weight " << bw.getWeight() << " for vertex " << vertexIndex << " this bone will not be in the palette" << std::endl;
100            }
101        }
102        maxBonePerVertex = osg::maximum(maxBonePerVertex, bonesForThisVertex);
103    }
104    osg::notify(osg::INFO) << "RigTransformHardware::createPalette maximum number of bone per vertex is " << maxBonePerVertex << std::endl;
105    osg::notify(osg::INFO) << "RigTransformHardware::createPalette matrix palette has " << boneNameCountMap.size() << " entries" << std::endl;
106
107    for (BoneNameCountMap::iterator it = boneNameCountMap.begin(); it != boneNameCountMap.end(); it++)
108    {
109        osg::notify(osg::INFO) << "RigTransformHardware::createPalette Bone " << it->first << " is used " << it->second << " times" << std::endl;
110    }
111
112    osg::notify(osg::INFO) << "RigTransformHardware::createPalette will use " << boneNameCountMap.size() * 4 << " uniforms" << std::endl;
113
114
115    for (int i = 0 ; i < (int)vertexIndexWeight.size(); i++)
116        vertexIndexWeight[i].resize(maxBonePerVertex);
117           
118    _nbVertexes = nbVertexes;
119    _bonesPerVertex = maxBonePerVertex;
120    _bonePalette = palette;
121    _vertexIndexMatrixWeightList = vertexIndexWeight;
122    _uniformMatrixPalette = createVertexUniform();
123    _boneWeightAttribArrays = createVertexAttribList();
124    return true;
125}
126
127
128//
129// create vertex attribute by 2 bones
130// vec4(boneIndex0, weight0, boneIndex1, weight1)
131// if more bones are needed then other attributes are created
132// vec4(boneIndex2, weight2, boneIndex3, weight3)
133// the idea is to use this format to have a granularity smaller
134// than the 4 bones using two vertex attributes
135//
136RigTransformHardware::BoneWeightAttribList RigTransformHardware::createVertexAttribList()
137{
138    BoneWeightAttribList arrayList;
139    int nbArray = static_cast<int>(ceilf(getNumBonesPerVertex() * 0.5));
140    if (!nbArray)
141        return arrayList;
142
143    arrayList.resize(nbArray);
144    for (int i = 0; i < nbArray; i++)
145    {
146        osg::ref_ptr<osg::Vec4Array> array = new osg::Vec4Array;
147        arrayList[i] = array;
148        int nbVertexes = getNumVertexes();
149        array->resize(nbVertexes);
150        for (int j = 0; j < nbVertexes; j++)
151        {
152            for (int b = 0; b < 2; b++)
153            {
154                // the granularity is 2 so if we have only one bone
155                // it's convenient to init the second with a weight 0
156                int boneIndexInList = i*2 + b;
157                int boneIndexInVec4 = b*2;
158                (*array)[j][0 + boneIndexInVec4] = 0;
159                (*array)[j][1 + boneIndexInVec4] = 0;
160                if (boneIndexInList < getNumBonesPerVertex())
161                {
162                    float boneIndex = static_cast<float>(_vertexIndexMatrixWeightList[j][boneIndexInList].getIndex());
163                    float boneWeight = _vertexIndexMatrixWeightList[j][boneIndexInList].getWeight();
164                    // fill the vec4
165                    (*array)[j][0 + boneIndexInVec4] = boneIndex;
166                    (*array)[j][1 + boneIndexInVec4] = boneWeight;
167                }
168            }
169        }
170    }
171    return arrayList;
172}
173
174
175osg::Uniform* RigTransformHardware::createVertexUniform()
176{
177    osg::Uniform* uniform = new osg::Uniform(osg::Uniform::FLOAT_MAT4, "matrixPalette", _bonePalette.size());
178    return uniform;
179}
180
181
182void RigTransformHardware::setShader(osg::Shader* shader)
183{
184    _shader = shader;
185}
186
187bool RigTransformHardware::init(RigGeometry& geom)
188{
189    osg::Vec3Array* pos = dynamic_cast<osg::Vec3Array*>(geom.getVertexArray());
190    if (!pos) {
191        osg::notify(osg::WARN) << "RigTransformHardware no vertex array in the geometry " << geom.getName() << std::endl;
192        return false;
193    }
194
195    if (!geom.getSkeleton()) {
196        osg::notify(osg::WARN) << "RigTransformHardware no skeleting set in geometry " << geom.getName() << std::endl;
197        return false;
198    }
199
200    Bone::BoneMap bm = geom.getSkeleton()->getBoneMap();
201
202    if (!createPalette(pos->size(),bm, geom.getVertexInfluenceSet().getVertexToBoneList()))
203        return false;
204
205    osg::ref_ptr<osg::Program> program = new osg::Program;
206    program->setName("HardwareSkinning");
207    if (!_shader.valid())
208        _shader = osg::Shader::readShaderFile(osg::Shader::VERTEX,"skinning.vert");
209
210    if (!_shader.valid()) {
211        osg::notify(osg::WARN) << "RigTransformHardware can't load VertexShader" << std::endl;
212        return false;
213    }
214
215    // replace max matrix by the value from uniform
216    {
217    std::string str = _shader->getShaderSource();
218    std::string toreplace = std::string("MAX_MATRIX");
219    std::size_t start = str.find(toreplace);
220    std::stringstream ss;
221    ss << getMatrixPaletteUniform()->getNumElements();
222    str.replace(start, toreplace.size(), ss.str());
223    _shader->setShaderSource(str);
224    osg::notify(osg::INFO) << "Shader " << str << std::endl;
225    }
226
227    int attribIndex = 11;
228    int nbAttribs = getNumVertexAttrib();
229    for (int i = 0; i < nbAttribs; i++)
230    {
231        std::stringstream ss;
232        ss << "boneWeight" << i;
233        program->addBindAttribLocation(ss.str(), attribIndex + i);
234        geom.setVertexAttribData(attribIndex + i, osg::Geometry::ArrayData(getVertexAttrib(i),osg::Geometry::BIND_PER_VERTEX));
235        osg::notify(osg::INFO) << "set vertex attrib " << ss.str() << std::endl;
236    }
237    program->addShader(_shader.get());
238
239    osg::ref_ptr<osg::StateSet> ss = new osg::StateSet;
240    ss->addUniform(getMatrixPaletteUniform());
241    ss->addUniform(new osg::Uniform("nbBonesPerVertex", getNumBonesPerVertex()));
242    ss->setAttributeAndModes(program.get());
243    geom.setStateSet(ss.get());
244    _needInit = false;
245    return true;
246}
247void RigTransformHardware::update(RigGeometry& geom)
248{
249    computeMatrixPaletteUniform(geom.getMatrixFromSkeletonToGeometry(), geom.getInvMatrixFromSkeletonToGeometry());
250}
Note: See TracBrowser for help on using the browser.