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

Revision 13041, 10.3 kB (checked in by robert, 2 years ago)

Ran script to remove trailing spaces and tabs

  • Property svn:eol-style set to native
Line 
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 <osgAnimation/BoneMapVisitor>
18#include <sstream>
19
20using namespace osgAnimation;
21
22
23RigTransformHardware::RigTransformHardware()
24{
25    _needInit = true;
26    _bonesPerVertex = 0;
27    _nbVertexes = 0;
28}
29
30osg::Vec4Array* RigTransformHardware::getVertexAttrib(int index)
31{
32    if (index >= (int)_boneWeightAttribArrays.size())
33        return 0;
34    return _boneWeightAttribArrays[index].get();
35}
36
37int RigTransformHardware::getNumVertexAttrib()
38{
39    return _boneWeightAttribArrays.size();
40}
41
42osg::Uniform* RigTransformHardware::getMatrixPaletteUniform()
43{
44    return _uniformMatrixPalette.get();
45}
46
47
48void RigTransformHardware::computeMatrixPaletteUniform(const osg::Matrix& transformFromSkeletonToGeometry, const osg::Matrix& invTransformFromSkeletonToGeometry)
49{
50    for (int i = 0; i < (int)_bonePalette.size(); i++)
51    {
52        osg::ref_ptr<Bone> bone = _bonePalette[i].get();
53        const osg::Matrix& invBindMatrix = bone->getInvBindMatrixInSkeletonSpace();
54        const osg::Matrix& boneMatrix = bone->getMatrixInSkeletonSpace();
55        osg::Matrix resultBoneMatrix = invBindMatrix * boneMatrix;
56        osg::Matrix result =  transformFromSkeletonToGeometry * resultBoneMatrix * invTransformFromSkeletonToGeometry;
57        if (!_uniformMatrixPalette->setElement(i, result))
58            OSG_WARN << "RigTransformHardware::computeUniformMatrixPalette can't set uniform at " << i << " elements" << std::endl;
59    }
60}
61
62
63int RigTransformHardware::getNumBonesPerVertex() const { return _bonesPerVertex;}
64int RigTransformHardware::getNumVertexes() const { return _nbVertexes;}
65
66bool RigTransformHardware::createPalette(int nbVertexes, BoneMap boneMap, const VertexInfluenceSet::VertexIndexToBoneWeightMap& vertexIndexToBoneWeightMap)
67{
68    typedef std::map<std::string, int> BoneNameCountMap;
69    typedef std::map<std::string, int> BoneNamePaletteIndex;
70    BoneNamePaletteIndex bname2palette;
71    BonePalette palette;
72    BoneNameCountMap boneNameCountMap;
73
74    // init vertex attribute data
75    VertexIndexWeightList vertexIndexWeight;
76    vertexIndexWeight.resize(nbVertexes);
77
78    int maxBonePerVertex = 0;
79    for (VertexInfluenceSet::VertexIndexToBoneWeightMap::const_iterator it = vertexIndexToBoneWeightMap.begin(); it != vertexIndexToBoneWeightMap.end(); ++it)
80    {
81        int vertexIndex = it->first;
82        const VertexInfluenceSet::BoneWeightList& boneWeightList = it->second;
83        int bonesForThisVertex = 0;
84        for (VertexInfluenceSet::BoneWeightList::const_iterator it = boneWeightList.begin(); it != boneWeightList.end(); ++it)
85        {
86            const VertexInfluenceSet::BoneWeight& bw = *it;
87            if (boneNameCountMap.find(bw.getBoneName()) != boneNameCountMap.end())
88            {
89                boneNameCountMap[bw.getBoneName()]++;
90                bonesForThisVertex++; // count max number of bones per vertexes
91                vertexIndexWeight[vertexIndex].push_back(IndexWeightEntry(bname2palette[bw.getBoneName()],bw.getWeight()));
92            }
93            else if (fabs(bw.getWeight()) > 1e-2) // dont use bone with weight too small
94            {
95                if (boneMap.find(bw.getBoneName()) == boneMap.end())
96                {
97                    OSG_INFO << "RigTransformHardware::createPalette can't find bone " << bw.getBoneName() << " skip this influence" << std::endl;
98                    continue;
99                }
100                boneNameCountMap[bw.getBoneName()] = 1; // for stats
101                bonesForThisVertex++;
102                palette.push_back(boneMap[bw.getBoneName()]);
103                bname2palette[bw.getBoneName()] = palette.size()-1;
104                vertexIndexWeight[vertexIndex].push_back(IndexWeightEntry(bname2palette[bw.getBoneName()],bw.getWeight()));
105            }
106            else
107            {
108                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;
109            }
110        }
111        maxBonePerVertex = osg::maximum(maxBonePerVertex, bonesForThisVertex);
112    }
113    OSG_INFO << "RigTransformHardware::createPalette maximum number of bone per vertex is " << maxBonePerVertex << std::endl;
114    OSG_INFO << "RigTransformHardware::createPalette matrix palette has " << boneNameCountMap.size() << " entries" << std::endl;
115
116    for (BoneNameCountMap::iterator it = boneNameCountMap.begin(); it != boneNameCountMap.end(); ++it)
117    {
118        OSG_INFO << "RigTransformHardware::createPalette Bone " << it->first << " is used " << it->second << " times" << std::endl;
119    }
120
121    OSG_INFO << "RigTransformHardware::createPalette will use " << boneNameCountMap.size() * 4 << " uniforms" << std::endl;
122
123
124    for (int i = 0 ; i < (int)vertexIndexWeight.size(); i++)
125        vertexIndexWeight[i].resize(maxBonePerVertex);
126
127    _nbVertexes = nbVertexes;
128    _bonesPerVertex = maxBonePerVertex;
129    _bonePalette = palette;
130    _vertexIndexMatrixWeightList = vertexIndexWeight;
131    _uniformMatrixPalette = createVertexUniform();
132    _boneWeightAttribArrays = createVertexAttribList();
133    return true;
134}
135
136
137//
138// create vertex attribute by 2 bones
139// vec4(boneIndex0, weight0, boneIndex1, weight1)
140// if more bones are needed then other attributes are created
141// vec4(boneIndex2, weight2, boneIndex3, weight3)
142// the idea is to use this format to have a granularity smaller
143// than the 4 bones using two vertex attributes
144//
145RigTransformHardware::BoneWeightAttribList RigTransformHardware::createVertexAttribList()
146{
147    BoneWeightAttribList arrayList;
148    int nbArray = static_cast<int>(ceilf(getNumBonesPerVertex() * 0.5));
149    if (!nbArray)
150        return arrayList;
151
152    arrayList.resize(nbArray);
153    for (int i = 0; i < nbArray; i++)
154    {
155        osg::ref_ptr<osg::Vec4Array> array = new osg::Vec4Array;
156        arrayList[i] = array;
157        int nbVertexes = getNumVertexes();
158        array->resize(nbVertexes);
159        for (int j = 0; j < nbVertexes; j++)
160        {
161            for (int b = 0; b < 2; b++)
162            {
163                // the granularity is 2 so if we have only one bone
164                // it's convenient to init the second with a weight 0
165                int boneIndexInList = i*2 + b;
166                int boneIndexInVec4 = b*2;
167                (*array)[j][0 + boneIndexInVec4] = 0;
168                (*array)[j][1 + boneIndexInVec4] = 0;
169                if (boneIndexInList < getNumBonesPerVertex())
170                {
171                    float boneIndex = static_cast<float>(_vertexIndexMatrixWeightList[j][boneIndexInList].getIndex());
172                    float boneWeight = _vertexIndexMatrixWeightList[j][boneIndexInList].getWeight();
173                    // fill the vec4
174                    (*array)[j][0 + boneIndexInVec4] = boneIndex;
175                    (*array)[j][1 + boneIndexInVec4] = boneWeight;
176                }
177            }
178        }
179    }
180    return arrayList;
181}
182
183
184osg::Uniform* RigTransformHardware::createVertexUniform()
185{
186    osg::Uniform* uniform = new osg::Uniform(osg::Uniform::FLOAT_MAT4, "matrixPalette", _bonePalette.size());
187    return uniform;
188}
189
190
191void RigTransformHardware::setShader(osg::Shader* shader)
192{
193    _shader = shader;
194}
195
196bool RigTransformHardware::init(RigGeometry& geom)
197{
198    osg::Geometry& source = *geom.getSourceGeometry();
199    osg::Vec3Array* positionSrc = dynamic_cast<osg::Vec3Array*>(source.getVertexArray());
200    if (!positionSrc)
201    {
202        OSG_WARN << "RigTransformHardware no vertex array in the geometry " << geom.getName() << std::endl;
203        return false;
204    }
205
206    if (!geom.getSkeleton())
207    {
208        OSG_WARN << "RigTransformHardware no skeleton set in geometry " << geom.getName() << std::endl;
209        return false;
210    }
211
212
213    // copy shallow from source geometry to rig
214    geom.copyFrom(source);
215
216
217    BoneMapVisitor mapVisitor;
218    geom.getSkeleton()->accept(mapVisitor);
219    BoneMap bm = mapVisitor.getBoneMap();
220
221    if (!createPalette(positionSrc->size(),bm, geom.getVertexInfluenceSet().getVertexToBoneList()))
222        return false;
223
224    osg::ref_ptr<osg::Program> program = new osg::Program;
225    program->setName("HardwareSkinning");
226    if (!_shader.valid())
227        _shader = osg::Shader::readShaderFile(osg::Shader::VERTEX,"skinning.vert");
228
229    if (!_shader.valid()) {
230        OSG_WARN << "RigTransformHardware can't load VertexShader" << std::endl;
231        return false;
232    }
233
234    // replace max matrix by the value from uniform
235    {
236    std::string str = _shader->getShaderSource();
237    std::string toreplace = std::string("MAX_MATRIX");
238    std::size_t start = str.find(toreplace);
239    std::stringstream ss;
240    ss << getMatrixPaletteUniform()->getNumElements();
241    str.replace(start, toreplace.size(), ss.str());
242    _shader->setShaderSource(str);
243    OSG_INFO << "Shader " << str << std::endl;
244    }
245
246    int attribIndex = 11;
247    int nbAttribs = getNumVertexAttrib();
248    for (int i = 0; i < nbAttribs; i++)
249    {
250        std::stringstream ss;
251        ss << "boneWeight" << i;
252        program->addBindAttribLocation(ss.str(), attribIndex + i);
253        geom.setVertexAttribData(attribIndex + i, osg::Geometry::ArrayData(getVertexAttrib(i),osg::Geometry::BIND_PER_VERTEX));
254        OSG_INFO << "set vertex attrib " << ss.str() << std::endl;
255    }
256    program->addShader(_shader.get());
257
258    osg::ref_ptr<osg::StateSet> ss = geom.getOrCreateStateSet();
259    ss->addUniform(getMatrixPaletteUniform());
260    ss->addUniform(new osg::Uniform("nbBonesPerVertex", getNumBonesPerVertex()));
261    ss->setAttributeAndModes(program.get());
262
263    _needInit = false;
264    return true;
265}
266void RigTransformHardware::operator()(RigGeometry& geom)
267{
268    if (_needInit)
269        if (!init(geom))
270            return;
271    computeMatrixPaletteUniform(geom.getMatrixFromSkeletonToGeometry(), geom.getInvMatrixFromSkeletonToGeometry());
272}
Note: See TracBrowser for help on using the browser.