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

Revision 11009, 10.6 kB (checked in by robert, 5 years ago)

From Cedric Pinson, "Here a list of changes:
Bone now inherit from MatrixTransform?. It simplify a lot the update of
Bone matrix. It helps to have the bone system more generic. eg it's now
possible to have animation data with precomputed bind matrix. The other
benefit, is now the collada plugin will be able to use osgAnimation to
display skinned mesh. Michael Plating did a great work to improve this
aspect, he is working on the collada plugin and should be able to submit
a new version soon.
The RigGeometry? has been refactored so now it works when you save and
reload RigGeometry? because the source is not touched anymore. The
benefit with this update is that it should be now possible to use a
MorphGeometry? as source for a RigGeometry?.

The bad news is that the format has changed, so i have rebuild osg-data
related to osgAnimation data, updated the blender exporter to export to
the new format.
The fbx plugin could be touched about this commit, i dont compile it so
i can't give more information about it.
The bvh plugin has been updated by Wang rui so this one is fixed with
the new code of osgAnimation.
The examples has been updated to work with the new code too...

The example osg-data/example.osg should be remove, it's an old example
that does not work.

For people using blender the blender exporter up to date is here:
http://hg.plopbyte.net/osgexport2/
it will be merge to http://hg.plopbyte.net/osgexport/ as soon as the
modification will be push in the trunk.
"

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>
[11009]17#include <osgAnimation/BoneMapVisitor>
[10693]18#include <sstream>
19
20using namespace osgAnimation;
21
[11009]22
23RigTransformHardware::RigTransformHardware()
24{
25    _needInit = true;
26    _bonesPerVertex = 0;
27    _nbVertexes = 0;
28}
29
[10693]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::notify(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;
[11009]79    for (VertexInfluenceSet::VertexIndexToBoneWeightMap::const_iterator it = vertexIndexToBoneWeightMap.begin(); it != vertexIndexToBoneWeightMap.end(); ++it)
[10693]80    {
81        int vertexIndex = it->first;
82        const VertexInfluenceSet::BoneWeightList& boneWeightList = it->second;
83        int bonesForThisVertex = 0;
[11009]84        for (VertexInfluenceSet::BoneWeightList::const_iterator it = boneWeightList.begin(); it != boneWeightList.end(); ++it)
[10693]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                {
[11009]97                    osg::notify(osg::INFO) << "RigTransformHardware::createPalette can't find bone " << bw.getBoneName() << " skip this influence" << std::endl;
[10693]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::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;
109            }
110        }
111        maxBonePerVertex = osg::maximum(maxBonePerVertex, bonesForThisVertex);
112    }
113    osg::notify(osg::INFO) << "RigTransformHardware::createPalette maximum number of bone per vertex is " << maxBonePerVertex << std::endl;
114    osg::notify(osg::INFO) << "RigTransformHardware::createPalette matrix palette has " << boneNameCountMap.size() << " entries" << std::endl;
115
[11009]116    for (BoneNameCountMap::iterator it = boneNameCountMap.begin(); it != boneNameCountMap.end(); ++it)
[10693]117    {
118        osg::notify(osg::INFO) << "RigTransformHardware::createPalette Bone " << it->first << " is used " << it->second << " times" << std::endl;
119    }
120
121    osg::notify(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{
[11009]198    osg::Geometry& source = *geom.getSourceGeometry();
199    osg::Vec3Array* positionSrc = dynamic_cast<osg::Vec3Array*>(source.getVertexArray());
200    if (!positionSrc)
201    {
[10693]202        osg::notify(osg::WARN) << "RigTransformHardware no vertex array in the geometry " << geom.getName() << std::endl;
203        return false;
204    }
205
[11009]206    if (!geom.getSkeleton())
207    {
208        osg::notify(osg::WARN) << "RigTransformHardware no skeleton set in geometry " << geom.getName() << std::endl;
[10693]209        return false;
210    }
211
212
[11009]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()))
[10693]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::notify(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::notify(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::notify(osg::INFO) << "set vertex attrib " << ss.str() << std::endl;
255    }
256    program->addShader(_shader.get());
257
258    osg::ref_ptr<osg::StateSet> ss = new osg::StateSet;
259    ss->addUniform(getMatrixPaletteUniform());
260    ss->addUniform(new osg::Uniform("nbBonesPerVertex", getNumBonesPerVertex()));
261    ss->setAttributeAndModes(program.get());
262    geom.setStateSet(ss.get());
263    _needInit = false;
264    return true;
265}
[11009]266void RigTransformHardware::operator()(RigGeometry& geom)
[10693]267{
[11009]268    if (_needInit)
269        if (!init(geom))
270            return;
[10693]271    computeMatrixPaletteUniform(geom.getMatrixFromSkeletonToGeometry(), geom.getInvMatrixFromSkeletonToGeometry());
272}
Note: See TracBrowser for help on using the browser.