diff --git a/examples/osganimationhardware/osganimationhardware.cpp b/examples/osganimationhardware/osganimationhardware.cpp index afd56015c..f8d7be4da 100644 --- a/examples/osganimationhardware/osganimationhardware.cpp +++ b/examples/osganimationhardware/osganimationhardware.cpp @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include @@ -40,89 +42,100 @@ static unsigned int getRandomValueinRange(unsigned int v) } -osg::ref_ptr program; +osg::ref_ptr CommonProgram; // show how to override the default RigTransformHardware for customized usage struct MyRigTransformHardware : public osgAnimation::RigTransformHardware { - - void operator()(osgAnimation::RigGeometry& geom) + int _maxmatrix; + MyRigTransformHardware() : _maxmatrix(99){} + virtual bool init(osgAnimation::RigGeometry& rig) { - if (_needInit) - if (!init(geom)) - return; - computeMatrixPaletteUniform(geom.getMatrixFromSkeletonToGeometry(), geom.getInvMatrixFromSkeletonToGeometry()); - } - - bool init(osgAnimation::RigGeometry& geom) - { - osg::Vec3Array* pos = dynamic_cast(geom.getVertexArray()); - if (!pos) { - osg::notify(osg::WARN) << "RigTransformHardware no vertex array in the geometry " << geom.getName() << std::endl; + if(_perVertexInfluences.empty()) + { + prepareData(rig); return false; } - - if (!geom.getSkeleton()) { - osg::notify(osg::WARN) << "RigTransformHardware no skeleting set in geometry " << geom.getName() << std::endl; + if(!rig.getSkeleton()) return false; - } osgAnimation::BoneMapVisitor mapVisitor; - geom.getSkeleton()->accept(mapVisitor); - osgAnimation::BoneMap bm = mapVisitor.getBoneMap(); + rig.getSkeleton()->accept(mapVisitor); + osgAnimation::BoneMap boneMap = mapVisitor.getBoneMap(); - if (!createPalette(pos->size(),bm, geom.getVertexInfluenceSet().getVertexToBoneList())) + if (!buildPalette(boneMap,rig) ) return false; - int attribIndex = 11; - int nbAttribs = getNumVertexAttrib(); + osg::Geometry& source = *rig.getSourceGeometry(); + osg::Vec3Array* positionSrc = dynamic_cast(source.getVertexArray()); - // use a global program for all avatar - if (!program.valid()) { - program = new osg::Program; - program->setName("HardwareSkinning"); - if (!_shader.valid()) - _shader = osg::Shader::readShaderFile(osg::Shader::VERTEX,"shaders/skinning.vert"); + if (!positionSrc) + { + OSG_WARN << "RigTransformHardware no vertex array in the geometry " << rig.getName() << std::endl; + return false; + } - if (!_shader.valid()) { - osg::notify(osg::WARN) << "RigTransformHardware can't load VertexShader" << std::endl; + // copy shallow from source geometry to rig + rig.copyFrom(source); + + osg::ref_ptr vertexshader; + osg::ref_ptr stateset = rig.getOrCreateStateSet(); + if(!CommonProgram.valid()) + { + CommonProgram = new osg::Program; + CommonProgram->setName("HardwareSkinning"); + + //set default source if _shader is not user setted + if (!vertexshader.valid()) + { + vertexshader = osg::Shader::readShaderFile(osg::Shader::VERTEX,"skinning.vert"); + } + + if (!vertexshader.valid()) + { + OSG_WARN << "RigTransformHardware can't load VertexShader" << std::endl; return false; } // replace max matrix by the value from uniform { - std::string str = _shader->getShaderSource(); + std::string str = vertexshader->getShaderSource(); std::string toreplace = std::string("MAX_MATRIX"); std::size_t start = str.find(toreplace); - std::stringstream ss; - ss << getMatrixPaletteUniform()->getNumElements(); - str.replace(start, toreplace.size(), ss.str()); - _shader->setShaderSource(str); - osg::notify(osg::INFO) << "Shader " << str << std::endl; - } - - program->addShader(_shader.get()); - - for (int i = 0; i < nbAttribs; i++) - { - std::stringstream ss; - ss << "boneWeight" << i; - program->addBindAttribLocation(ss.str(), attribIndex + i); - - osg::notify(osg::INFO) << "set vertex attrib " << ss.str() << std::endl; + if (std::string::npos != start) + { + std::stringstream ss; + ss << _maxmatrix;//getMatrixPaletteUniform()->getNumElements(); + str.replace(start, toreplace.size(), ss.str()); + vertexshader->setShaderSource(str); + } + else + { + OSG_WARN<< "MAX_MATRIX not found in Shader! " << str << std::endl; + } + OSG_INFO << "Shader " << str << std::endl; } + CommonProgram->addShader(vertexshader.get()); } - for (int i = 0; i < nbAttribs; i++) + + unsigned int nbAttribs = getNumVertexAttrib(); + for (unsigned int i = 0; i < nbAttribs; i++) { std::stringstream ss; ss << "boneWeight" << i; - geom.setVertexAttribArray(attribIndex + i, getVertexAttrib(i)); + CommonProgram->addBindAttribLocation(ss.str(), _minAttribIndex + i); + rig.setVertexAttribArray(_minAttribIndex + i, getVertexAttrib(i)); + OSG_INFO << "set vertex attrib " << ss.str() << std::endl; } - osg::ref_ptr ss = new osg::StateSet; - ss->addUniform(getMatrixPaletteUniform()); - ss->addUniform(new osg::Uniform("nbBonesPerVertex", getNumBonesPerVertex())); - ss->setAttributeAndModes(program.get()); - geom.setStateSet(ss.get()); + + stateset->removeUniform("nbBonesPerVertex"); + stateset->addUniform(new osg::Uniform("nbBonesPerVertex",_bonesPerVertex)); + + stateset->removeUniform("matrixPalette"); + stateset->addUniform(_uniformMatrixPalette); + + stateset->setAttribute(CommonProgram.get()); + _needInit = false; return true; } @@ -133,6 +146,7 @@ struct MyRigTransformHardware : public osgAnimation::RigTransformHardware struct SetupRigGeometry : public osg::NodeVisitor { bool _hardware; + SetupRigGeometry( bool hardware = true) : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), _hardware(hardware) {} void apply(osg::Geode& geode) @@ -142,10 +156,15 @@ struct SetupRigGeometry : public osg::NodeVisitor } void apply(osg::Drawable& geom) { - if (_hardware) { + if (_hardware) + { osgAnimation::RigGeometry* rig = dynamic_cast(&geom); if (rig) + { rig->setRigTransformImplementation(new MyRigTransformHardware); + osgAnimation::MorphGeometry *morph=dynamic_cast(rig->getSourceGeometry()); + if(morph)morph->setMorphTransformImplementation(new osgAnimation::MorphTransformHardware); + } } #if 0 @@ -169,7 +188,8 @@ osg::Group* createCharacterInstance(osg::Group* character, bool hardware) osgAnimation::BasicAnimationManager* anim = dynamic_cast(animationManager); const osgAnimation::AnimationList& list = animationManager->getAnimationList(); int v = getRandomValueinRange(list.size()); - if (list[v]->getName() == std::string("MatIpo_ipo")) { + if (list[v]->getName() == std::string("MatIpo_ipo")) + { anim->playAnimation(list[v].get()); v = (v + 1)%list.size(); } @@ -193,7 +213,10 @@ int main (int argc, char* argv[]) bool hardware = true; int maxChar = 10; - while (psr.read("--software")) { hardware = false; } + while (psr.read("--software")) + { + hardware = false; + } while (psr.read("--number", maxChar)) {} osg::ref_ptr node = osgDB::readRefNodeFiles(psr); @@ -243,9 +266,10 @@ int main (int argc, char* argv[]) double xChar = maxChar; double yChar = xChar * 9.0/16; - for (double i = 0.0; i < xChar; i++) { - for (double j = 0.0; j < yChar; j++) { - + for (double i = 0.0; i < xChar; i++) + { + for (double j = 0.0; j < yChar; j++) + { osg::ref_ptr c = createCharacterInstance(root.get(), hardware); osg::MatrixTransform* tr = new osg::MatrixTransform; tr->setMatrix(osg::Matrix::translate( 2.0 * (i - xChar * .5), @@ -257,7 +281,6 @@ int main (int argc, char* argv[]) } std::cout << "created " << xChar * yChar << " instance" << std::endl; - return viewer.run(); } diff --git a/examples/osganimationskinning/osganimationskinning.cpp b/examples/osganimationskinning/osganimationskinning.cpp index 7bfa9b017..2536353a6 100644 --- a/examples/osganimationskinning/osganimationskinning.cpp +++ b/examples/osganimationskinning/osganimationskinning.cpp @@ -134,13 +134,10 @@ void initVertexMap(osgAnimation::Bone* b0, osgAnimation::RigGeometry* geom, osg::Vec3Array* array) { - osgAnimation::VertexInfluenceSet vertexesInfluences; osgAnimation::VertexInfluenceMap* vim = new osgAnimation::VertexInfluenceMap; - (*vim)[b0->getName()].setName(b0->getName()); (*vim)[b1->getName()].setName(b1->getName()); (*vim)[b2->getName()].setName(b2->getName()); - for (int i = 0; i < (int)array->size(); i++) { float val = (*array)[i][0]; diff --git a/include/osgAnimation/AnimationManagerBase b/include/osgAnimation/AnimationManagerBase index bdc0c5c89..0c5b8ec36 100644 --- a/include/osgAnimation/AnimationManagerBase +++ b/include/osgAnimation/AnimationManagerBase @@ -42,6 +42,17 @@ namespace osgAnimation const AnimationList& getAnimationList() const { return _animations;} AnimationList& getAnimationList() { return _animations;} + //uniformisation of the API + inline Animation * getRegisteredAnimation(unsigned int i) { return _animations[i].get();} + inline unsigned int getNumRegisteredAnimations() const { return _animations.size();} + inline void addRegisteredAnimation(Animation* animation) + { + _needToLink = true; + _animations.push_back(animation); + buildTargetReference(); + } + void removeRegisteredAnimation(Animation* animation); + /** Callback method called by the NodeVisitor when visiting a node.*/ virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); @@ -49,7 +60,6 @@ namespace osgAnimation this Operation must be done each frame */ void clearTargets(); - LinkVisitor* getOrCreateLinkVisitor(); void setLinkVisitor(LinkVisitor*); diff --git a/include/osgAnimation/MorphGeometry b/include/osgAnimation/MorphGeometry index 1a242efd7..ab6c1bc8f 100644 --- a/include/osgAnimation/MorphGeometry +++ b/include/osgAnimation/MorphGeometry @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -28,7 +29,8 @@ namespace osgAnimation public: - enum Method { + enum Method + { NORMALIZED, RELATIVE }; @@ -59,18 +61,45 @@ namespace osgAnimation virtual const char* libraryName() const { return "osgAnimation"; } virtual const char* className() const { return "MorphGeometry"; } - virtual void transformSoftwareMethod(); + // set implementation of rig method + inline void setMorphTransformImplementation(MorphTransform*mt) { _morphTransformImplementation=mt; } + inline MorphTransform* getMorphTransformImplementation() { return _morphTransformImplementation.get(); } + inline const MorphTransform* getMorphTransformImplementation() const { return _morphTransformImplementation.get(); } /** Set the morphing method. */ - void setMethod(Method method) { _method = method; } + inline void setMethod(Method method) { _method = method; } /** Get the morphing method. */ inline Method getMethod() const { return _method; } /** Set flag for morphing normals. */ - void setMorphNormals(bool morphNormals) { _morphNormals = morphNormals; } + inline void setMorphNormals(bool morphNormals) { _morphNormals = morphNormals; } /** Get the flag for morphing normals. */ inline bool getMorphNormals() const { return _morphNormals; } + /** Get the list of MorphTargets.*/ + inline const MorphTargetList& getMorphTargetList() const { return _morphTargets; } + + /** Get the list of MorphTargets. Warning if you modify this array you will have to call dirty() */ + inline MorphTargetList& getMorphTargetList() { return _morphTargets; } + + /** Return the \c MorphTarget at position \c i.*/ + inline const MorphTarget& getMorphTarget( unsigned int i ) const { return _morphTargets[i]; } + + /** Return the \c MorphTarget at position \c i.*/ + inline MorphTarget& getMorphTarget( unsigned int i ) { return _morphTargets[i]; } + + /** Set source of vertices for this morph geometry */ + inline void setVertexSource(osg::Vec3Array *v) { _positionSource=v; } + + /** Get source of vertices for this morph geometry */ + inline osg::Vec3Array * getVertexSource() const { return _positionSource.get(); } + + /** Set source of normals for this morph geometry */ + inline void setNormalSource(osg::Vec3Array *n) { _normalSource=n; } + + /** Get source of normals for this morph geometry */ + inline osg::Vec3Array * getNormalSource() const { return _normalSource.get(); } + /** Add a \c MorphTarget to the \c MorphGeometry. * If \c MorphTarget is not \c NULL and is not contained in the \c MorphGeometry * then increment its reference count, add it to the MorphTargets list and @@ -80,20 +109,30 @@ namespace osgAnimation * @param weight The weight to be added to the \c MorphGeometry. * @return \c true for success; \c false otherwise. */ - virtual void addMorphTarget( osg::Geometry *morphTarget, float weight = 1.0 ) { _morphTargets.push_back(MorphTarget(morphTarget, weight)); _dirty = true; } + virtual void addMorphTarget( osg::Geometry *morphTarget, float weight = 1.0 ) + { + _morphTargets.push_back(MorphTarget(morphTarget, weight)); + _dirty = true; + } - virtual void removeMorphTarget( osg::Geometry *morphTarget ) { - for(MorphTargetList::iterator iterator = _morphTargets.begin() ; iterator != _morphTargets.end() ; ++ iterator) { - if(iterator->getGeometry() == morphTarget) { + virtual void removeMorphTarget( osg::Geometry *morphTarget ) + { + for(MorphTargetList::iterator iterator = _morphTargets.begin() ; iterator != _morphTargets.end() ; ++ iterator) + { + if(iterator->getGeometry() == morphTarget) + { _morphTargets.erase(iterator); break; } } } - virtual void removeMorphTarget( const std::string& name ) { - for(MorphTargetList::iterator iterator = _morphTargets.begin() ; iterator != _morphTargets.end() ; ++ iterator) { - if(iterator->getGeometry() && iterator->getGeometry()->getName() == name) { + virtual void removeMorphTarget( const std::string& name ) + { + for(MorphTargetList::iterator iterator = _morphTargets.begin() ; iterator != _morphTargets.end() ; ++ iterator) + { + if(iterator->getGeometry() && iterator->getGeometry()->getName() == name) + { _morphTargets.erase(iterator); break; } @@ -101,7 +140,8 @@ namespace osgAnimation } - void setWeight(unsigned int index, float morphWeight) + /** update a morph target at index setting its current weight to morphWeight */ + inline void setWeight(unsigned int index, float morphWeight) { if (index < _morphTargets.size()) { @@ -111,29 +151,22 @@ namespace osgAnimation } /** Set the MorphGeometry dirty.*/ - void dirty() { _dirty = true; } + inline void dirty(bool b=true) { _dirty = b; } + inline bool isDirty() const { return _dirty; } - /** Get the list of MorphTargets.*/ - const MorphTargetList& getMorphTargetList() const { return _morphTargets; } - - /** Get the list of MorphTargets. Warning if you modify this array you will have to call dirty() */ - MorphTargetList& getMorphTargetList() { return _morphTargets; } - - /** Return the \c MorphTarget at position \c i.*/ - inline const MorphTarget& getMorphTarget( unsigned int i ) const { return _morphTargets[i]; } - - /** Return the \c MorphTarget at position \c i.*/ - inline MorphTarget& getMorphTarget( unsigned int i ) { return _morphTargets[i]; } + /** for retrocompatibility */ + void transformSoftwareMethod() { (*_morphTransformImplementation.get())(*this); } protected: + osg::ref_ptr _morphTransformImplementation; /// Do we need to recalculate the morphed geometry? bool _dirty; Method _method; MorphTargetList _morphTargets; - std::vector _positionSource; - std::vector _normalSource; + osg::ref_ptr _positionSource; + osg::ref_ptr _normalSource; /// Do we also morph between normals? bool _morphNormals; @@ -153,7 +186,8 @@ namespace osgAnimation void addTarget(const std::string& name) { _targetNames.push_back(name); } unsigned int getNumTarget() const { return _targetNames.size(); } const std::string& getTargetName(unsigned int index) { return _targetNames[index]; } - void removeTarget(const std::string& name) { + void removeTarget(const std::string& name) + { TargetNames::iterator found = std::find(_targetNames.begin(), _targetNames.end(), name); if(found != _targetNames.end()) _targetNames.erase(found); @@ -163,9 +197,7 @@ namespace osgAnimation const std::vector& getTargetNames() const { return _targetNames; } std::vector& getTargetNames() { return _targetNames; } - void setTargetNames(const TargetNames& targetNames) { - _targetNames.assign(targetNames.begin(), targetNames.end()); - } + void setTargetNames(const TargetNames& targetNames) { _targetNames.assign(targetNames.begin(), targetNames.end()); } /** Callback method called by the NodeVisitor when visiting a node.*/ virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); @@ -195,11 +227,15 @@ namespace osgAnimation if (!geom) return; - geom->transformSoftwareMethod(); + if (!geom->getMorphTransformImplementation()) + { + geom->setMorphTransformImplementation( new MorphTransformSoftware); + } + + MorphTransform& implementation = *geom->getMorphTransformImplementation(); + (implementation)(*geom); } }; - - } #endif diff --git a/include/osgAnimation/MorphTransformHardware b/include/osgAnimation/MorphTransformHardware new file mode 100644 index 000000000..838fc99cf --- /dev/null +++ b/include/osgAnimation/MorphTransformHardware @@ -0,0 +1,65 @@ +/* -*-c++-*- + * Copyright (C) 2017 Julien Valentin + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. + */ + +#ifndef OSGANIMATION_MORPH_TRANSFORM_HARDWARE +#define OSGANIMATION_MORPH_TRANSFORM_HARDWARE 1 + +#include +#include +#include +#include +#include +#include + +///texture unit reserved for morphtarget TBO +#define MORPHTRANSHW_DEFAULTMORPHTEXTUREUNIT 7 + +namespace osgAnimation +{ + class MorphGeometry; + + /// This class manage format for hardware morphing + class OSGANIMATION_EXPORT MorphTransformHardware : public MorphTransform + { + public: + + MorphTransformHardware(); + + MorphTransformHardware(const MorphTransformHardware& rth, const osg::CopyOp& copyop); + + META_Object(osgAnimation,MorphTransformHardware); + + virtual void operator()(MorphGeometry&); + + inline void setShader( osg::Shader*s ) { _shader=s; } + inline const osg::Shader * getShader() const { return _shader.get(); } + inline osg::Shader * getShader() { return _shader.get(); } + + ///texture unit reserved for morphtarget TBO default is 7 + void setReservedTextureUnit(unsigned int t) { _reservedTextureUnit=t; } + unsigned int getReservedTextureUnit() const { return _reservedTextureUnit; } + + protected: + + bool init(MorphGeometry&); + + osg::ref_ptr _uniformTargetsWeight; + osg::ref_ptr _shader; + + bool _needInit; + unsigned int _reservedTextureUnit; + }; +} + +#endif diff --git a/include/osgAnimation/MorphTransformSoftware b/include/osgAnimation/MorphTransformSoftware new file mode 100644 index 000000000..50546501a --- /dev/null +++ b/include/osgAnimation/MorphTransformSoftware @@ -0,0 +1,46 @@ +/* -*-c++-*- + * Copyright (C) 2009 Cedric Pinson + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. + */ + +#ifndef OSGANIMATION_MORPHTRANSFORM_SOFTWARE +#define OSGANIMATION_MORPHTRANSFORM_SOFTWARE 1 + +#include +#include +#include +#include + +namespace osgAnimation +{ + + class MorphGeometry; + + /// This class manage format for software morphing + class OSGANIMATION_EXPORT MorphTransformSoftware : public MorphTransform + { + public: + MorphTransformSoftware():_needInit(true) {} + MorphTransformSoftware(const MorphTransformSoftware& rts,const osg::CopyOp& copyop): MorphTransform(rts, copyop), _needInit(true) {} + + META_Object(osgAnimation,MorphTransformSoftware) + + bool init(MorphGeometry&); + virtual void operator()(MorphGeometry&); + + protected: + bool _needInit; + + }; +} + +#endif diff --git a/include/osgAnimation/RigGeometry b/include/osgAnimation/RigGeometry index 74a5cf00f..f9a2b1887 100644 --- a/include/osgAnimation/RigGeometry +++ b/include/osgAnimation/RigGeometry @@ -23,7 +23,6 @@ namespace osgAnimation { - // The idea is to compute a bounding box with a factor x of the first step we compute the bounding box class OSGANIMATION_EXPORT RigComputeBoundingBoxCallback : public osg::Drawable::ComputeBoundingBoxCallback { @@ -37,8 +36,8 @@ namespace osgAnimation META_Object(osgAnimation, RigComputeBoundingBoxCallback); - void reset() { _computed = false; } + virtual osg::BoundingBox computeBound(const osg::Drawable& drawable) const; protected: mutable bool _computed; @@ -57,40 +56,36 @@ namespace osgAnimation META_Object(osgAnimation, RigGeometry); - void setInfluenceMap(VertexInfluenceMap* vertexInfluenceMap) { _vertexInfluenceMap = vertexInfluenceMap; } - const VertexInfluenceMap* getInfluenceMap() const { return _vertexInfluenceMap.get();} - VertexInfluenceMap* getInfluenceMap() { return _vertexInfluenceMap.get();} + inline void setInfluenceMap(VertexInfluenceMap* vertexInfluenceMap) { _vertexInfluenceMap = vertexInfluenceMap; } + inline const VertexInfluenceMap* getInfluenceMap() const { return _vertexInfluenceMap.get(); } + inline VertexInfluenceMap* getInfluenceMap() { return _vertexInfluenceMap.get(); } - const Skeleton* getSkeleton() const; - Skeleton* getSkeleton(); + inline const Skeleton* getSkeleton() const { return _root.get(); } + inline Skeleton* getSkeleton() { return _root.get(); } // will be used by the update callback to init correctly the rig mesh - void setSkeleton(Skeleton*); + inline void setSkeleton(Skeleton* root) { _root = root; } - void setNeedToComputeMatrix(bool state) { _needToComputeMatrix = state;} - bool getNeedToComputeMatrix() const { return _needToComputeMatrix;} - - - // this build the internal database about vertex influence and bones - void buildVertexInfluenceSet(); - const VertexInfluenceSet& getVertexInfluenceSet() const; + void setNeedToComputeMatrix(bool state) { _needToComputeMatrix = state; } + bool getNeedToComputeMatrix() const { return _needToComputeMatrix; } void computeMatrixFromRootSkeleton(); - // set implementation of rig method - void setRigTransformImplementation(RigTransform*); - RigTransform* getRigTransformImplementation(); - const RigTransform* getRigTransformImplementation() const { return _rigTransformImplementation.get(); } + inline RigTransform* getRigTransformImplementation() { return _rigTransformImplementation.get(); } + inline void setRigTransformImplementation(RigTransform* rig) { _rigTransformImplementation = rig; } + inline const RigTransform* getRigTransformImplementation() const { return _rigTransformImplementation.get(); } - virtual void drawImplementation(osg::RenderInfo& renderInfo) const; void update(); + void buildVertexInfluenceSet() { _rigTransformImplementation->prepareData(*this); } + const osg::Matrix& getMatrixFromSkeletonToGeometry() const; + const osg::Matrix& getInvMatrixFromSkeletonToGeometry() const; - osg::Geometry* getSourceGeometry(); - const osg::Geometry* getSourceGeometry() const; - void setSourceGeometry(osg::Geometry* geometry); + inline osg::Geometry* getSourceGeometry() { return _geometry.get(); } + inline const osg::Geometry* getSourceGeometry() const { return _geometry.get(); } + inline void setSourceGeometry(osg::Geometry* geometry) { _geometry = geometry; } void copyFrom(osg::Geometry& from); @@ -111,8 +106,6 @@ namespace osgAnimation osg::ref_ptr _geometry; osg::ref_ptr _rigTransformImplementation; - - VertexInfluenceSet _vertexInfluenceSet; osg::ref_ptr _vertexInfluenceMap; osg::Matrix _matrixFromSkeletonToGeometry; @@ -120,8 +113,7 @@ namespace osgAnimation osg::observer_ptr _root; bool _needToComputeMatrix; - - }; + }; struct UpdateRigGeometry : public osg::Drawable::UpdateCallback @@ -135,7 +127,8 @@ namespace osgAnimation META_Object(osgAnimation, UpdateRigGeometry); - virtual void update(osg::NodeVisitor* nv, osg::Drawable* drw) { + virtual void update(osg::NodeVisitor* nv, osg::Drawable* drw) + { RigGeometry* geom = dynamic_cast(drw); if(!geom) return; @@ -151,7 +144,7 @@ namespace osgAnimation osg::notify(osg::WARN) << "A RigGeometry did not find a parent skeleton for RigGeometry ( " << geom->getName() << " )" << std::endl; return; } - geom->buildVertexInfluenceSet(); + geom->getRigTransformImplementation()->prepareData(*geom); geom->setSkeleton(finder._root.get()); } @@ -161,7 +154,8 @@ namespace osgAnimation if(geom->getNeedToComputeMatrix()) geom->computeMatrixFromRootSkeleton(); - if(geom->getSourceGeometry()) { + if(geom->getSourceGeometry()) + { osg::Drawable::UpdateCallback * up = dynamic_cast(geom->getSourceGeometry()->getUpdateCallback()); if(up) up->update(nv, geom->getSourceGeometry()); diff --git a/include/osgAnimation/RigTransform b/include/osgAnimation/RigTransform index 66ae0a219..ba41b8db1 100644 --- a/include/osgAnimation/RigTransform +++ b/include/osgAnimation/RigTransform @@ -1,15 +1,15 @@ - /* -*-c++-*- - * Copyright (C) 2009 Cedric Pinson - * - * This library is open source and may be redistributed and/or modified under - * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or - * (at your option) any later version. The full license is in LICENSE file - * included with this distribution, and on the openscenegraph.org website. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * OpenSceneGraph Public License for more details. +/* -*-c++-*- +* Copyright (C) 2009 Cedric Pinson +* +* This library is open source and may be redistributed and/or modified under +* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or +* (at your option) any later version. The full license is in LICENSE file +* included with this distribution, and on the openscenegraph.org website. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* OpenSceneGraph Public License for more details. */ #ifndef OSGANIMATION_RIGTRANSFORM @@ -33,10 +33,31 @@ namespace osgAnimation virtual void operator()(RigGeometry&) {} + /// to call manually when a skeleton is reacheable from the rig + /// in order to prepare technic data before rendering + virtual bool prepareData(RigGeometry&) { return true; } + protected: virtual ~RigTransform() {} }; + class MorphGeometry; + + class MorphTransform : public osg::Object + { + public: + MorphTransform() {} + MorphTransform(const MorphTransform& org, const osg::CopyOp& copyop): + osg::Object(org, copyop) {} + + META_Object(osgAnimation,MorphTransform) + + virtual void operator()(MorphGeometry&) {} + + protected: + virtual ~MorphTransform() {} + + }; } diff --git a/include/osgAnimation/RigTransformHardware b/include/osgAnimation/RigTransformHardware index ce9006ddd..5b020f4a4 100644 --- a/include/osgAnimation/RigTransformHardware +++ b/include/osgAnimation/RigTransformHardware @@ -1,5 +1,6 @@ /* -*-c++-*- * Copyright (C) 2009 Cedric Pinson + * Copyright (C) 2017 Julien Valentin * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or @@ -22,6 +23,8 @@ #include #include +#define RIGTRANSHW_DEFAULT_FIRST_VERTATTRIB_TARGETTED 11 + namespace osgAnimation { class RigGeometry; @@ -37,54 +40,42 @@ namespace osgAnimation META_Object(osgAnimation,RigTransformHardware); - typedef osg::Matrix MatrixType; - typedef osgAnimation::Bone BoneType; typedef std::vector > BoneWeightAttribList; - typedef std::vector > BonePalette; - typedef std::map BoneNamePaletteIndex; - + typedef std::vector > BonePalette; + typedef std::map BoneNamePaletteIndex; typedef std::vector MatrixPalette; - struct IndexWeightEntry - { - int _boneIndex; - float _boneWeight; - IndexWeightEntry() { _boneIndex = 0; _boneWeight = 0;} - IndexWeightEntry(int index, float weight) { _boneIndex = index; _boneWeight = weight;} - int getIndex() const { return _boneIndex; } - float getWeight() const { return _boneWeight; } - }; - typedef std::vector > VertexIndexWeightList; + ///set the first Vertex Attribute Array index of the rig generated by this technic (default:11) + void setFirstVertexAttributeTarget(unsigned int i) { _minAttribIndex=i; } + unsigned int getFirstVertexAttributeTarget()const { return _minAttribIndex; } + void setShader(osg::Shader* shader) { _shader = shader; } + const osg::Shader* getShader() const { return _shader.get(); } + osg::Shader* getShader() { return _shader.get(); } - osg::Vec4Array* getVertexAttrib(int index); - int getNumVertexAttrib(); + osg::Vec4Array* getVertexAttrib(unsigned int index); + unsigned int getNumVertexAttrib() const { return _boneWeightAttribArrays.size(); } + + const unsigned int &getNumBonesPerVertex() const { return _bonesPerVertex; } + const unsigned int &getNumVertexes() const { return _nbVertices; } + + const BoneNamePaletteIndex& getBoneNameToPalette() { return _boneNameToPalette; } + const BonePalette& getBonePalette() { return _bonePalette; } + osg::Uniform* getMatrixPaletteUniform() { return _uniformMatrixPalette.get(); } - osg::Uniform* getMatrixPaletteUniform(); void computeMatrixPaletteUniform(const osg::Matrix& transformFromSkeletonToGeometry, const osg::Matrix& invTransformFromSkeletonToGeometry); - int getNumBonesPerVertex() const; - int getNumVertexes() const; - - bool createPalette(int nbVertexes, BoneMap boneMap, const VertexInfluenceSet::VertexIndexToBoneWeightMap& vertexIndexToBoneWeightMap); - + // update rig if needed virtual void operator()(RigGeometry&); - void setShader(osg::Shader*); - const BoneNamePaletteIndex& getBoneNameToPalette() { - return _boneNameToPalette; - } + // init/reset animations data + virtual bool prepareData(RigGeometry& ); protected: - bool init(RigGeometry&); + unsigned int _bonesPerVertex; + unsigned int _nbVertices; - BoneWeightAttribList createVertexAttribList(); - osg::Uniform* createVertexUniform(); - - int _bonesPerVertex; - int _nbVertexes; - VertexIndexWeightList _vertexIndexMatrixWeightList; BonePalette _bonePalette; BoneNamePaletteIndex _boneNameToPalette; BoneWeightAttribList _boneWeightAttribArrays; @@ -92,6 +83,13 @@ namespace osgAnimation osg::ref_ptr _shader; bool _needInit; + unsigned int _minAttribIndex; + bool buildPalette(const BoneMap& boneMap,const RigGeometry& rig); + + //on first update + virtual bool init(RigGeometry& ); + + std::vector _perVertexInfluences; }; } diff --git a/include/osgAnimation/RigTransformSoftware b/include/osgAnimation/RigTransformSoftware index 300e513fd..4424498ba 100644 --- a/include/osgAnimation/RigTransformSoftware +++ b/include/osgAnimation/RigTransformSoftware @@ -1,5 +1,6 @@ /* -*-c++-*- * Copyright (C) 2009 Cedric Pinson + * Copyright (C) 2017 Julien Valentin * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or @@ -36,36 +37,49 @@ namespace osgAnimation META_Object(osgAnimation,RigTransformSoftware) virtual void operator()(RigGeometry&); + //to call when a skeleton is reacheable from the rig to prepare technic data + virtual bool prepareData(RigGeometry&); - class BoneWeight + typedef std::pair LocalBoneIDWeight; + class BonePtrWeight: LocalBoneIDWeight { public: - BoneWeight(Bone* bone, float weight) : _bone(bone), _weight(weight) {} - const Bone* getBone() const { return _bone.get(); } - float getWeight() const { return _weight; } - void setWeight(float w) { _weight = w; } + BonePtrWeight(unsigned int id,float weight, Bone*bone=0 ): LocalBoneIDWeight(id,weight), _boneptr(bone) {} + BonePtrWeight(const BonePtrWeight &bw2): LocalBoneIDWeight(bw2.getBoneID(),bw2.getWeight()), _boneptr(bw2._boneptr.get()) {} + inline const float & getWeight() const { return second; } + inline void setWeight(float b) { second=b; } + inline const unsigned int & getBoneID() const { return first; } + inline void setBoneID(unsigned int b) { first=b; } + inline bool operator< (const BonePtrWeight &b1) const { + if (second > b1.second) return true; + if (second < b1.second) return false; + return (first > b1.first); + } + ///set Bone pointer + inline const Bone * getBonePtr() const { return _boneptr.get(); } + inline void setBonePtr(Bone*b) { _boneptr=b; } protected: - osg::observer_ptr _bone; - float _weight; + osg::observer_ptr< Bone > _boneptr; }; - typedef std::vector BoneWeightList; - typedef std::vector VertexList; + typedef std::vector BonePtrWeightList; - class UniqBoneSetVertexSet + /// map a set of boneinfluence to a list of vertex indices sharing this set + class VertexGroup { public: - BoneWeightList& getBones() { return _bones; } - VertexList& getVertexes() { return _vertexes; } + inline BonePtrWeightList& getBoneWeights() { return _boneweights; } - void resetMatrix() + inline IndexList& getVertices() { return _vertexes; } + + inline void resetMatrix() { _result.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1); } - void accummulateMatrix(const osg::Matrix& invBindMatrix, const osg::Matrix& matrix, osg::Matrix::value_type weight) + inline void accummulateMatrix(const osg::Matrix& invBindMatrix, const osg::Matrix& matrix, osg::Matrix::value_type weight) { osg::Matrix m = invBindMatrix * matrix; osg::Matrix::value_type* ptr = m.ptr(); @@ -86,20 +100,19 @@ namespace osgAnimation ptrresult[13] += ptr[13] * weight; ptrresult[14] += ptr[14] * weight; } - void computeMatrixForVertexSet() + inline void computeMatrixForVertexSet() { - if (_bones.empty()) + if (_boneweights.empty()) { - osg::notify(osg::WARN) << this << " RigTransformSoftware::UniqBoneSetVertexSet no bones found" << std::endl; + osg::notify(osg::WARN) << this << " RigTransformSoftware::VertexGroup no bones found" << std::endl; _result = osg::Matrix::identity(); return; } resetMatrix(); - int size = _bones.size(); - for (int i = 0; i < size; i++) + for(BonePtrWeightList::iterator bwit=_boneweights.begin(); bwit!=_boneweights.end(); ++bwit ) { - const Bone* bone = _bones[i].getBone(); + const Bone* bone = bwit->getBonePtr(); if (!bone) { osg::notify(osg::WARN) << this << " RigTransformSoftware::computeMatrixForVertexSet Warning a bone is null, skip it" << std::endl; @@ -107,68 +120,67 @@ namespace osgAnimation } const osg::Matrix& invBindMatrix = bone->getInvBindMatrixInSkeletonSpace(); const osg::Matrix& matrix = bone->getMatrixInSkeletonSpace(); - osg::Matrix::value_type w = _bones[i].getWeight(); + osg::Matrix::value_type w = bwit->getWeight(); accummulateMatrix(invBindMatrix, matrix, w); } } - const osg::Matrix& getMatrix() const { return _result;} + void normalize(); + inline const osg::Matrix& getMatrix() const { return _result; } protected: - BoneWeightList _bones; - VertexList _vertexes; + BonePtrWeightList _boneweights; + IndexList _vertexes; osg::Matrix _result; }; - - - template void compute(const osg::Matrix& transform, const osg::Matrix& invTransform, const V* src, V* dst) + template + inline void compute(const osg::Matrix& transform, const osg::Matrix& invTransform, const V* src, V* dst) { // the result of matrix mult should be cached to be used for vertexes transform and normal transform and maybe other computation - int size = _boneSetVertexSet.size(); - for (int i = 0; i < size; i++) + for(VertexGroupList::iterator itvg=_uniqVertexGroupList.begin(); itvg!=_uniqVertexGroupList.end(); ++itvg) { - UniqBoneSetVertexSet& uniq = _boneSetVertexSet[i]; + VertexGroup& uniq = *itvg; uniq.computeMatrixForVertexSet(); osg::Matrix matrix = transform * uniq.getMatrix() * invTransform; - const VertexList& vertexes = uniq.getVertexes(); - int vertexSize = vertexes.size(); - for (int j = 0; j < vertexSize; j++) + const IndexList& vertexes = uniq.getVertices(); + for(IndexList::const_iterator vertIDit=vertexes.begin(); vertIDit!=vertexes.end(); ++vertIDit) { - int idx = vertexes[j]; - dst[idx] = src[idx] * matrix; + dst[*vertIDit] = src[*vertIDit] * matrix; } + } } - - template void computeNormal(const osg::Matrix& transform, const osg::Matrix& invTransform, const V* src, V* dst) + template + inline void computeNormal(const osg::Matrix& transform, const osg::Matrix& invTransform, const V* src, V* dst) { - int size = _boneSetVertexSet.size(); - for (int i = 0; i < size; i++) + for(VertexGroupList::iterator itvg=_uniqVertexGroupList.begin(); itvg!=_uniqVertexGroupList.end(); ++itvg) { - UniqBoneSetVertexSet& uniq = _boneSetVertexSet[i]; + VertexGroup& uniq = *itvg; uniq.computeMatrixForVertexSet(); osg::Matrix matrix = transform * uniq.getMatrix() * invTransform; - const VertexList& vertexes = uniq.getVertexes(); - int vertexSize = vertexes.size(); - for (int j = 0; j < vertexSize; j++) + const IndexList& vertexes = uniq.getVertices(); + for(IndexList::const_iterator vertIDit=vertexes.begin(); vertIDit!=vertexes.end(); ++vertIDit) { - int idx = vertexes[j]; - dst[idx] = osg::Matrix::transform3x3(src[idx],matrix); + dst[*vertIDit] = osg::Matrix::transform3x3(src[*vertIDit],matrix); } } } protected: - bool init(RigGeometry&); - void initVertexSetFromBones(const BoneMap& map, const VertexInfluenceSet::UniqVertexSetToBoneSetList& influence); - std::vector _boneSetVertexSet; - bool _needInit; + virtual bool init(RigGeometry&); + std::map _invalidInfluence; + + typedef std::vector VertexGroupList; + VertexGroupList _uniqVertexGroupList; + + void buildMinimumUpdateSet(const RigGeometry&rig ); + }; } diff --git a/include/osgAnimation/VertexInfluence b/include/osgAnimation/VertexInfluence index 47941a43f..1bf1e6e01 100644 --- a/include/osgAnimation/VertexInfluence +++ b/include/osgAnimation/VertexInfluence @@ -1,5 +1,6 @@ /* -*-c++-*- * Copyright (C) 2008 Cedric Pinson + * Copyright (C) 2017 Julien Valentin * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or @@ -23,22 +24,31 @@ namespace osgAnimation { + class Skeleton; - // first is vertex index, and second the weight, the - typedef std::pair VertexIndexWeight; - typedef std::vector VertexList; - class OSGANIMATION_EXPORT VertexInfluence : public VertexList + // first is bonename, and second the weight + typedef std::pair BoneWeight; + // first is vertex index, and second the weight + typedef std::pair VertexIndexWeight; + // list of IndexWeight + typedef std::vector IndexWeightList; + // list of IndexWeight + typedef std::vector BoneWeightList; + // list of Index + typedef std::vector IndexList; + + //Bone influence list + class OSGANIMATION_EXPORT VertexInfluence : public IndexWeightList { public: - const std::string& getName() const { return _name;} - void setName(const std::string& name) { _name = name;} - + const std::string& getName() const { return _name; } + void setName(const std::string& name) { _name = name; } protected: // the name is the bone to link to std::string _name; }; - class VertexInfluenceMap : public std::map , public osg::Object + class VertexInfluenceMap : public std::map, public osg::Object { public: META_Object(osgAnimation, VertexInfluenceMap); @@ -46,61 +56,32 @@ namespace osgAnimation VertexInfluenceMap() {} VertexInfluenceMap(const osgAnimation::VertexInfluenceMap& org, const osg::CopyOp& copyop): std::map(org), - osg::Object(org, copyop) - {} - }; + osg::Object(org, copyop) {} + ///normalize per vertex weights given numvert of the attached mesh + void normalize(unsigned int numvert); - // this class manage VertexInfluence database by mesh - // reference bones per vertex ... - class OSGANIMATION_EXPORT VertexInfluenceSet - { - public: - typedef std::vector BoneToVertexList; + ///remove weakest influences in order to fit targetted numbonepervertex + void cullInfluenceCountPerVertex(unsigned int maxnumbonepervertex, float minweight=0, bool renormalize=true); - class BoneWeight + //compute PerVertexInfluenceList + void computePerVertexInfluenceList(std::vector& perVertexInfluenceList, unsigned int numvert) const; + + /// map a set of boneinfluence to a list of vertex indices sharing this set + class VertexGroup: public std::pair { public: - BoneWeight(const std::string& name, float weight) : _boneName(name), _weight(weight) {} - const std::string& getBoneName() const { return _boneName; } - float getWeight() const { return _weight; } - void setWeight(float weight) { _weight = weight; } - bool operator==(const BoneWeight& b) const { return (_boneName == b.getBoneName() && _weight == b.getWeight()); } - protected: - std::string _boneName; - float _weight; + inline const BoneWeightList& getBoneWeights() const { return first; } + inline void setBoneWeights( BoneWeightList& o ) { first=o; } + inline IndexList& vertIDs() { return second; } }; - typedef std::vector BoneWeightList; - typedef std::map VertexIndexToBoneWeightMap; + /// compute the minimal VertexGroup Set in which vertices shares the same influence set + void computeMinimalVertexGroupList(std::vector&uniqVertexGroupList, unsigned int numvert) const; - class UniqVertexSetToBoneSet - { - public: - void setBones(BoneWeightList& bones) { _bones = bones;} - const BoneWeightList& getBones() const { return _bones;} - std::vector& getVertexes() { return _vertexes;} - const std::vector& getVertexes() const { return _vertexes;} - protected: - std::vector _vertexes; - BoneWeightList _bones; // here we could limit matrix operation by caching (weight * matrix) - }; - - typedef std::vector UniqVertexSetToBoneSetList; - - const UniqVertexSetToBoneSetList& getUniqVertexSetToBoneSetList() const { return _uniqVertexSetToBoneSet;} - void addVertexInfluence(const VertexInfluence& v); - void buildVertex2BoneList(); - void buildUniqVertexSetToBoneSetList(); - void clear(); - - const VertexIndexToBoneWeightMap& getVertexToBoneList() const; - protected: - BoneToVertexList _bone2Vertexes; - VertexIndexToBoneWeightMap _vertex2Bones; - UniqVertexSetToBoneSetList _uniqVertexSetToBoneSet; + //Experimental removal of unexpressed bone from the skeleton + void removeUnexpressedBones(Skeleton &skel) const; }; - } #endif diff --git a/src/osgAnimation/AnimationManagerBase.cpp b/src/osgAnimation/AnimationManagerBase.cpp index 1c47851a7..237b44203 100644 --- a/src/osgAnimation/AnimationManagerBase.cpp +++ b/src/osgAnimation/AnimationManagerBase.cpp @@ -101,7 +101,12 @@ void AnimationManagerBase::registerAnimation (Animation* animation) buildTargetReference(); } -void AnimationManagerBase::unregisterAnimation (Animation* animation) +void AnimationManagerBase::removeRegisteredAnimation(Animation* animation) +{ + unregisterAnimation(animation); +} + +void AnimationManagerBase::unregisterAnimation(Animation* animation) { AnimationList::iterator it = std::find(_animations.begin(), _animations.end(), animation); if (it != _animations.end()) diff --git a/src/osgAnimation/CMakeLists.txt b/src/osgAnimation/CMakeLists.txt index b0e920544..7a79b914d 100644 --- a/src/osgAnimation/CMakeLists.txt +++ b/src/osgAnimation/CMakeLists.txt @@ -34,6 +34,8 @@ SET(TARGET_H ${HEADER_PATH}/RigTransform ${HEADER_PATH}/RigTransformHardware ${HEADER_PATH}/RigTransformSoftware + ${HEADER_PATH}/MorphTransformHardware + ${HEADER_PATH}/MorphTransformSoftware ${HEADER_PATH}/Sampler ${HEADER_PATH}/Skeleton ${HEADER_PATH}/StackedMatrixElement @@ -75,6 +77,8 @@ SET(TARGET_SRC RigGeometry.cpp RigTransformHardware.cpp RigTransformSoftware.cpp + MorphTransformHardware.cpp + MorphTransformSoftware.cpp Skeleton.cpp StackedMatrixElement.cpp StackedQuaternionElement.cpp diff --git a/src/osgAnimation/MorphGeometry.cpp b/src/osgAnimation/MorphGeometry.cpp index 9735c06f9..964ff35a2 100644 --- a/src/osgAnimation/MorphGeometry.cpp +++ b/src/osgAnimation/MorphGeometry.cpp @@ -23,28 +23,31 @@ using namespace osgAnimation; MorphGeometry::MorphGeometry() : _dirty(false), _method(NORMALIZED), + _positionSource(0),_normalSource(0), _morphNormals(true) { setUseDisplayList(false); setUpdateCallback(new UpdateMorphGeometry); - setDataVariance(osg::Object::DYNAMIC); setUseVertexBufferObjects(true); + _morphTransformImplementation = new MorphTransformSoftware(); } -MorphGeometry::MorphGeometry(const osg::Geometry& b) : - osg::Geometry(b, osg::CopyOp::DEEP_COPY_ARRAYS), +MorphGeometry::MorphGeometry(const osg::Geometry& g) : + osg::Geometry(g, osg::CopyOp::DEEP_COPY_ARRAYS), _dirty(false), _method(NORMALIZED), + _positionSource(0),_normalSource(0), _morphNormals(true) { setUseDisplayList(false); setUpdateCallback(new UpdateMorphGeometry); - setDataVariance(osg::Object::DYNAMIC); setUseVertexBufferObjects(true); + _morphTransformImplementation = new MorphTransformSoftware(); } MorphGeometry::MorphGeometry(const MorphGeometry& b, const osg::CopyOp& copyop) : osg::Geometry(b,copyop), + _morphTransformImplementation(osg::clone(b._morphTransformImplementation.get(), copyop)), _dirty(b._dirty), _method(b._method), _morphTargets(b._morphTargets), @@ -56,151 +59,12 @@ MorphGeometry::MorphGeometry(const MorphGeometry& b, const osg::CopyOp& copyop) setUseVertexBufferObjects(true); } - -void MorphGeometry::transformSoftwareMethod() -{ - if (_dirty) - { - // See if we have an internal optimized geometry - osg::Geometry* morphGeometry = this; - - osg::Vec3Array* pos = dynamic_cast(morphGeometry->getVertexArray()); - - if(pos) - { - if ( _positionSource.size() != pos->size()) - { - _positionSource = std::vector(pos->begin(),pos->end()); - pos->setDataVariance(osg::Object::DYNAMIC); - } - - osg::Vec3Array* normal = dynamic_cast(morphGeometry->getNormalArray()); - bool normalmorphable = _morphNormals && normal; - if (normal && _normalSource.size() != normal->size()) - { - _normalSource = std::vector(normal->begin(),normal->end()); - normal->setDataVariance(osg::Object::DYNAMIC); - } - - - if (!_positionSource.empty()) - { - bool initialized = false; - if (_method == NORMALIZED) - { - // base * 1 - (sum of weights) + sum of (weight * target) - float baseWeight = 0; - for (unsigned int i=0; i < _morphTargets.size(); i++) - { - baseWeight += _morphTargets[i].getWeight(); - } - baseWeight = 1 - baseWeight; - - if (baseWeight != 0) - { - initialized = true; - for (unsigned int i=0; i < pos->size(); i++) - { - (*pos)[i] = _positionSource[i] * baseWeight; - } - if (normalmorphable) - { - for (unsigned int i=0; i < normal->size(); i++) - { - (*normal)[i] = _normalSource[i] * baseWeight; - } - } - } - } - else //if (_method == RELATIVE) - { - // base + sum of (weight * target) - initialized = true; - for (unsigned int i=0; i < pos->size(); i++) - { - (*pos)[i] = _positionSource[i]; - } - if (normalmorphable) - { - for (unsigned int i=0; i < normal->size(); i++) - { - (*normal)[i] = _normalSource[i]; - } - } - } - - for (unsigned int i=0; i < _morphTargets.size(); i++) - { - if (_morphTargets[i].getWeight() > 0) - { - // See if any the targets use the internal optimized geometry - osg::Geometry* targetGeometry = _morphTargets[i].getGeometry(); - - osg::Vec3Array* targetPos = dynamic_cast(targetGeometry->getVertexArray()); - osg::Vec3Array* targetNormals = dynamic_cast(targetGeometry->getNormalArray()); - normalmorphable = normalmorphable && targetNormals; - if(targetPos) - { - if (initialized) - { - // If vertices are initialized, add the morphtargets - for (unsigned int j=0; j < pos->size(); j++) - { - (*pos)[j] += (*targetPos)[j] * _morphTargets[i].getWeight(); - } - - if (normalmorphable) - { - for (unsigned int j=0; j < normal->size(); j++) - { - (*normal)[j] += (*targetNormals)[j] * _morphTargets[i].getWeight(); - } - } - } - else - { - // If not initialized, initialize with this morph target - initialized = true; - for (unsigned int j=0; j < pos->size(); j++) - { - (*pos)[j] = (*targetPos)[j] * _morphTargets[i].getWeight(); - } - - if (normalmorphable) - { - for (unsigned int j=0; j < normal->size(); j++) - { - (*normal)[j] = (*targetNormals)[j] * _morphTargets[i].getWeight(); - } - } - } - } - } - } - - pos->dirty(); - if (normalmorphable) - { - for (unsigned int j=0; j < normal->size(); j++) - { - (*normal)[j].normalize(); - } - normal->dirty(); - } - } - - dirtyBound(); - - } - _dirty = false; - } -} - UpdateMorph::UpdateMorph(const UpdateMorph& apc,const osg::CopyOp& copyop) : osg::Object(apc, copyop), osg::Callback(apc, copyop), AnimationUpdateCallback(apc, copyop) { + _targetNames=apc._targetNames; } UpdateMorph::UpdateMorph(const std::string& name) : AnimationUpdateCallback(name) @@ -243,8 +107,6 @@ void UpdateMorph::operator()(osg::Node* node, osg::NodeVisitor* nv) traverse(node,nv); } - - bool UpdateMorph::needLink() const { // the idea is to return true if nothing is linked diff --git a/src/osgAnimation/MorphTransformHardware.cpp b/src/osgAnimation/MorphTransformHardware.cpp new file mode 100644 index 000000000..ad2a6859d --- /dev/null +++ b/src/osgAnimation/MorphTransformHardware.cpp @@ -0,0 +1,213 @@ +/* -*-c++-*- + * Copyright (C) 2017 Julien Valentin + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. + */ + +#include +#include +#include +#include +#include + +using namespace osgAnimation; + +MorphTransformHardware::MorphTransformHardware(): + _needInit(true), + _reservedTextureUnit(MORPHTRANSHW_DEFAULTMORPHTEXTUREUNIT) +{ +} + +MorphTransformHardware::MorphTransformHardware(const MorphTransformHardware& rth, const osg::CopyOp& copyop): + MorphTransform(rth, copyop), + _uniformTargetsWeight(rth._uniformTargetsWeight), + _shader(rth._shader), + _needInit(rth._needInit), + _reservedTextureUnit(rth._reservedTextureUnit) +{ +} + +bool MorphTransformHardware::init(MorphGeometry& morphGeometry) +{ + osg::Vec3Array* pos = dynamic_cast(morphGeometry.getVertexArray()); + + osg::Vec3Array * vertexSource = (morphGeometry.getVertexSource()); + osg::Vec3Array * normalSource = (morphGeometry.getNormalSource()); + morphGeometry.setDataVariance(osg::Object::STATIC); + ///check for correct morph configuration + ///(blender osgexport doesn't set sources so assume morphgeom arrays are sources) + if(pos) + { + pos->setDataVariance(osg::Object::STATIC); + ///check if source is setted correctly + if (!vertexSource|| vertexSource->size() != pos->size()) + { + vertexSource =(static_cast( pos->clone(osg::CopyOp::DEEP_COPY_ARRAYS))); + } + osg::Vec3Array* normal = dynamic_cast(morphGeometry.getNormalArray()); + bool normalmorphable = morphGeometry.getMorphNormals() && normal&&(normal->getBinding()==osg::Array::BIND_PER_VERTEX); + if(!normalmorphable) + { + OSG_WARN << "MorphTransformHardware::morph geometry "<setDataVariance(osg::Object::STATIC); + if (normalmorphable && (!normalSource || normalSource->size() != normal->size())) + { + normalSource =(static_cast( normal->clone(osg::CopyOp::DEEP_COPY_ARRAYS))); + } + } + + ///end check + morphGeometry.setVertexArray(morphGeometry.getVertexSource()); + morphGeometry.setNormalArray(morphGeometry.getNormalSource(),osg::Array::BIND_PER_VERTEX); + morphGeometry.setDataVariance(osg::Object::STATIC); + + //create one TBO for all morphtargets (pack vertex/normal) + osg::Vec3Array * morphTargets=new osg::Vec3Array ; + MorphGeometry::MorphTargetList & morphlist=morphGeometry.getMorphTargetList(); + for(MorphGeometry::MorphTargetList::const_iterator curmorph=morphlist.begin(); curmorph!=morphlist.end(); ++curmorph) + { + const osg::Geometry * morphtargetgeom= curmorph->getGeometry() ; + const osg::Vec3Array *varray=(osg::Vec3Array*)morphtargetgeom->getVertexArray(); + const osg::Vec3Array *narray=(osg::Vec3Array*)morphtargetgeom->getNormalArray(); + if(morphGeometry.getMethod()==MorphGeometry::RELATIVE) + { + for(unsigned int i=0; igetNumElements(); ++i) + { + morphTargets->push_back( (*varray)[i]); + morphTargets->push_back( (*narray)[i]); + } + } + else + { + //convert to RELATIVE as it involve less math in the VS than NORMALIZED + const osg::Vec3Array *ovarray=(osg::Vec3Array*)morphGeometry.getVertexArray(); + const osg::Vec3Array *onarray=(osg::Vec3Array*)morphGeometry.getNormalArray(); + for(unsigned int i=0; igetNumElements(); ++i) + { + morphTargets->push_back( (*varray)[i]- (*ovarray)[i] ); + morphTargets->push_back( (*narray)[i]- (*onarray)[i] ); + } + } + } + + osg::TextureBuffer * morphTargetsTBO=new osg::TextureBuffer(); + morphTargetsTBO->setBufferData(morphTargets); + morphTargetsTBO->setInternalFormat( GL_RGB32F_ARB ); + + //create TBO Texture handle + osg::Uniform * morphTBOHandle=new osg::Uniform(osg::Uniform::SAMPLER_BUFFER,"morphTargets"); + morphTBOHandle->set((int)_reservedTextureUnit); + + //create dynamic uniform for morphtargets animation weights + _uniformTargetsWeight=new osg::Uniform(osg::Uniform::FLOAT,"morphWeights",morphlist.size()); + + + osg::ref_ptr program ; + osg::ref_ptr vertexshader; + osg::ref_ptr stateset = morphGeometry.getOrCreateStateSet(); + //grab geom source program and vertex shader if _shader is not setted + if(!_shader.valid() && (program = (osg::Program*)stateset->getAttribute(osg::StateAttribute::PROGRAM))) + { + for(unsigned int i=0;igetNumShaders();++i) + { + if(program->getShader(i)->getType()==osg::Shader::VERTEX) + { + // vertexshader=program->getShader(i); + program->removeShader(vertexshader); + } + } + } + + if (!program) + { + program = new osg::Program; + } + + program->setName("HardwareMorphing"); + //set default source if _shader is not user setted + if (!vertexshader.valid()) + { + if (!_shader.valid()) + vertexshader = osg::Shader::readShaderFile(osg::Shader::VERTEX,"morphing.vert"); + else + vertexshader=_shader; + } + + if (!vertexshader.valid()) + { + OSG_WARN << "RigTransformHardware can't load VertexShader" << std::endl; + return false; + } + + // replace max matrix by the value from uniform + { + std::string str = vertexshader->getShaderSource(); + std::string toreplace = std::string("MAX_MORPHWEIGHT"); + std::size_t start = str.find(toreplace); + if (std::string::npos == start) + { + // perhaps remanance from previous init (if saved after init) so reload shader + vertexshader = osg::Shader::readShaderFile(osg::Shader::VERTEX,"morphing.vert"); + if (!vertexshader.valid()) + { + OSG_WARN << "RigTransformHardware can't load VertexShader" << std::endl; + return false; + } + str = vertexshader->getShaderSource(); + start = str.find(toreplace); + } + if (std::string::npos != start) + { + std::stringstream ss; + ss << _uniformTargetsWeight->getNumElements(); + str.replace(start, toreplace.size(), ss.str()); + vertexshader->setShaderSource(str); + } + else + { + OSG_WARN << "MAX_MORPHWEIGHT not found in Shader! " << str << std::endl; + } + OSG_INFO << "Shader " << str << std::endl; + } + + program->addShader(vertexshader.get()); + //morphGeometry.setStateSet((osg::StateSet *) osg::CopyOp()(source.getOrCreateStateSet())); + + osg::ref_ptr ss = morphGeometry.getOrCreateStateSet(); + ss->addUniform(_uniformTargetsWeight); + ss->setTextureAttribute(_reservedTextureUnit,morphTargetsTBO); + ss->addUniform( morphTBOHandle); + ss->addUniform(new osg::Uniform("nbMorphVertex", morphGeometry.getVertexArray()->getNumElements())); + + ss->setAttributeAndModes(program.get()); + _needInit = false; + return true; +} + +void MorphTransformHardware::operator()(MorphGeometry& geom) +{ + if (_needInit && !init(geom)) return; + + if (geom.isDirty()) + { + ///upload new morph weights each update via uniform + int curimorph=0; + MorphGeometry::MorphTargetList & morphlist=geom.getMorphTargetList(); + for(MorphGeometry::MorphTargetList::const_iterator curmorph=morphlist.begin(); curmorph!=morphlist.end(); ++curmorph) + { + _uniformTargetsWeight->setElement(curimorph++, curmorph->getWeight()); + } + _uniformTargetsWeight->dirty(); + geom.dirty(false); + } +} diff --git a/src/osgAnimation/MorphTransformSoftware.cpp b/src/osgAnimation/MorphTransformSoftware.cpp new file mode 100644 index 000000000..1965a5ed1 --- /dev/null +++ b/src/osgAnimation/MorphTransformSoftware.cpp @@ -0,0 +1,181 @@ +/* -*-c++-*- + * Copyright (C) 2009 Cedric Pinson + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. + */ + + +#include +#include +#include +#include + +using namespace osgAnimation; + + +bool MorphTransformSoftware::init(MorphGeometry& morphGeometry){ + + morphGeometry.setDataVariance(osg::Object::DYNAMIC); + osg::Vec3Array* pos = dynamic_cast(morphGeometry.getVertexArray()); + osg::Vec3Array * vertexSource = (morphGeometry.getVertexSource()); + osg::Vec3Array * normalSource = (morphGeometry.getNormalSource()); + + // See if we have an internal optimized geometry + + if(pos) + { + if (!vertexSource|| vertexSource->size() != pos->size()) + { + morphGeometry.setVertexSource(new osg::Vec3Array(pos->begin(),pos->end())); + pos->setDataVariance(osg::Object::DYNAMIC); + } + + osg::Vec3Array* normal = dynamic_cast(morphGeometry.getNormalArray()); + bool normalmorphable = morphGeometry.getMorphNormals() && normal; + morphGeometry.setMorphNormals(normalmorphable); + if (normalmorphable && (!normalSource || normalSource->size() != normal->size())) + { + morphGeometry.setNormalSource(new osg::Vec3Array(normal->begin(),normal->end())); + normal->setDataVariance(osg::Object::DYNAMIC); + } + }else return false; + + _needInit=false; + return true; +} + +void MorphTransformSoftware::operator()(MorphGeometry& morphGeometry) +{ + if (_needInit) + if (!init(morphGeometry)) + return; + if (morphGeometry.isDirty()) + { + osg::Vec3Array* pos = static_cast(morphGeometry.getVertexArray()); + osg::Vec3Array & vertexSource = *(morphGeometry.getVertexSource()); + osg::Vec3Array& normalSource = *(morphGeometry.getNormalSource()); + osg::Vec3Array* normal = static_cast(morphGeometry.getNormalArray()); + bool normalmorphable = morphGeometry.getMorphNormals() && normal; + + + if (!vertexSource.empty()) + { + bool initialized = false; + if (morphGeometry.getMethod() == MorphGeometry::NORMALIZED) + { + // base * 1 - (sum of weights) + sum of (weight * target) + float baseWeight = 0; + for (unsigned int i=0; i < morphGeometry.getMorphTargetList().size(); i++) + { + baseWeight += morphGeometry.getMorphTarget(i).getWeight(); + } + baseWeight = 1 - baseWeight; + + if (baseWeight != 0) + { + initialized = true; + for (unsigned int i=0; i < pos->size(); i++) + { + (*pos)[i] = vertexSource[i] * baseWeight; + } + if (normalmorphable) + { + for (unsigned int i=0; i < normal->size(); i++) + { + (*normal)[i] = normalSource[i] * baseWeight; + } + } + } + } + else //if (_method == RELATIVE) + { + // base + sum of (weight * target) + initialized = true; + for (unsigned int i=0; i < pos->size(); i++) + { + (*pos)[i] = vertexSource[i]; + } + if (normalmorphable) + { + for (unsigned int i=0; i < normal->size(); i++) + { + (*normal)[i] = normalSource[i]; + } + } + } + + for (unsigned int i=0; i < morphGeometry.getMorphTargetList().size(); i++) + { + if (morphGeometry.getMorphTarget(i).getWeight() > 0) + { + // See if any the targets use the internal optimized geometry + osg::Geometry* targetGeometry = morphGeometry.getMorphTarget(i).getGeometry(); + + osg::Vec3Array* targetPos = dynamic_cast(targetGeometry->getVertexArray()); + osg::Vec3Array* targetNormals = dynamic_cast(targetGeometry->getNormalArray()); + normalmorphable = normalmorphable && targetNormals; + if(targetPos) + { + if (initialized) + { + // If vertices are initialized, add the morphtargets + for (unsigned int j=0; j < pos->size(); j++) + { + (*pos)[j] += (*targetPos)[j] * morphGeometry.getMorphTarget(i).getWeight(); + } + + if (normalmorphable) + { + for (unsigned int j=0; j < normal->size(); j++) + { + (*normal)[j] += (*targetNormals)[j] * morphGeometry.getMorphTarget(i).getWeight(); + } + } + } + else + { + // If not initialized, initialize with this morph target + initialized = true; + for (unsigned int j=0; j < pos->size(); j++) + { + (*pos)[j] = (*targetPos)[j] * morphGeometry.getMorphTarget(i).getWeight(); + } + + if (normalmorphable) + { + for (unsigned int j=0; j < normal->size(); j++) + { + (*normal)[j] = (*targetNormals)[j] * morphGeometry.getMorphTarget(i).getWeight(); + } + } + } + } + } + } + + pos->dirty(); + if (normalmorphable) + { + for (unsigned int j=0; j < normal->size(); j++) + { + (*normal)[j].normalize(); + } + normal->dirty(); + } + } + + morphGeometry.dirtyBound(); + + + morphGeometry.dirty(false); + } + +} diff --git a/src/osgAnimation/RigGeometry.cpp b/src/osgAnimation/RigGeometry.cpp index 938629a97..1f3764b48 100644 --- a/src/osgAnimation/RigGeometry.cpp +++ b/src/osgAnimation/RigGeometry.cpp @@ -58,7 +58,8 @@ RigGeometry::RigGeometry() _needToComputeMatrix = true; _matrixFromSkeletonToGeometry = _invMatrixFromSkeletonToGeometry = osg::Matrix::identity(); // disable the computation of boundingbox for the rig mesh - setComputeBoundingBoxCallback(new RigComputeBoundingBoxCallback); + setComputeBoundingBoxCallback(new RigComputeBoundingBoxCallback()); + _rigTransformImplementation = new osgAnimation::RigTransformSoftware; } @@ -66,12 +67,15 @@ RigGeometry::RigGeometry() RigGeometry::RigGeometry(const RigGeometry& b, const osg::CopyOp& copyop) : osg::Geometry(b,copyop), _geometry(b._geometry), - _vertexInfluenceSet(b._vertexInfluenceSet), + _rigTransformImplementation(osg::clone(b._rigTransformImplementation.get(), copyop)), _vertexInfluenceMap(b._vertexInfluenceMap), _needToComputeMatrix(b._needToComputeMatrix) -{ +{ + _needToComputeMatrix = true; + _matrixFromSkeletonToGeometry = _invMatrixFromSkeletonToGeometry = osg::Matrix::identity(); // disable the computation of boundingbox for the rig mesh - setComputeBoundingBoxCallback(new RigComputeBoundingBoxCallback); + + setComputeBoundingBoxCallback(new RigComputeBoundingBoxCallback()); // we don't copy the RigImplementation yet. because the RigImplementation need to be initialized in a valid graph, with a skeleton ... // don't know yet what to do with a clone of a RigGeometry @@ -82,29 +86,6 @@ const osg::Matrix& RigGeometry::getMatrixFromSkeletonToGeometry() const { return const osg::Matrix& RigGeometry::getInvMatrixFromSkeletonToGeometry() const { return _invMatrixFromSkeletonToGeometry;} -void RigGeometry::drawImplementation(osg::RenderInfo& renderInfo) const -{ - osg::Geometry::drawImplementation(renderInfo); -} - -void RigGeometry::buildVertexInfluenceSet() -{ - if (!_vertexInfluenceMap.valid()) - { - OSG_WARN << "buildVertexInfluenceSet can't be called without VertexInfluence already set to the RigGeometry ( " << getName() << " ) " << std::endl; - return; - } - _vertexInfluenceSet.clear(); - for (osgAnimation::VertexInfluenceMap::iterator it = _vertexInfluenceMap->begin(); - it != _vertexInfluenceMap->end(); - ++it) - _vertexInfluenceSet.addVertexInfluence(it->second); - - _vertexInfluenceSet.buildVertex2BoneList(); - _vertexInfluenceSet.buildUniqVertexSetToBoneSetList(); - OSG_DEBUG << "uniq groups " << _vertexInfluenceSet.getUniqVertexSetToBoneSetList().size() << " for " << getName() << std::endl; -} - void RigGeometry::computeMatrixFromRootSkeleton() { if (!_root.valid()) @@ -116,61 +97,56 @@ void RigGeometry::computeMatrixFromRootSkeleton() osg::Matrix notRoot = _root->getMatrix(); _matrixFromSkeletonToGeometry = mtxList[0] * osg::Matrix::inverse(notRoot); _invMatrixFromSkeletonToGeometry = osg::Matrix::inverse(_matrixFromSkeletonToGeometry); - _needToComputeMatrix = false; + _needToComputeMatrix = false; } void RigGeometry::update() { - if (!getRigTransformImplementation()) - { - _rigTransformImplementation = new RigTransformSoftware; - } - - RigTransform& implementation = *getRigTransformImplementation(); + RigTransform& implementation = *_rigTransformImplementation; (implementation)(*this); } void RigGeometry::copyFrom(osg::Geometry& from) { - bool copyToSelf = (this==&from); + if (this==&from) return; osg::Geometry& target = *this; - if (!copyToSelf) target.setStateSet(from.getStateSet()); + target.setStateSet(from.getStateSet()); // copy over primitive sets. - if (!copyToSelf) target.getPrimitiveSetList() = from.getPrimitiveSetList(); + target.getPrimitiveSetList() = from.getPrimitiveSetList(); if (from.getVertexArray()) { - if (!copyToSelf) target.setVertexArray(from.getVertexArray()); + target.setVertexArray(from.getVertexArray()); } if (from.getNormalArray()) { - if (!copyToSelf) target.setNormalArray(from.getNormalArray()); + target.setNormalArray(from.getNormalArray()); } if (from.getColorArray()) { - if (!copyToSelf) target.setColorArray(from.getColorArray()); + target.setColorArray(from.getColorArray()); } if (from.getSecondaryColorArray()) { - if (!copyToSelf) target.setSecondaryColorArray(from.getSecondaryColorArray()); + target.setSecondaryColorArray(from.getSecondaryColorArray()); } if (from.getFogCoordArray()) { - if (!copyToSelf) target.setFogCoordArray(from.getFogCoordArray()); + target.setFogCoordArray(from.getFogCoordArray()); } for(unsigned int ti=0;ti + * Copyright (C) 2017 Julien Valentin * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or @@ -19,133 +20,48 @@ using namespace osgAnimation; - -RigTransformHardware::RigTransformHardware() -{ - _needInit = true; - _bonesPerVertex = 0; - _nbVertexes = 0; -} +RigTransformHardware::RigTransformHardware(): + _bonesPerVertex (0), + _nbVertices (0), + _needInit (true), + _minAttribIndex(RIGTRANSHW_DEFAULT_FIRST_VERTATTRIB_TARGETTED) +{} RigTransformHardware::RigTransformHardware(const RigTransformHardware& rth, const osg::CopyOp& copyop): RigTransform(rth, copyop), _bonesPerVertex(rth._bonesPerVertex), - _nbVertexes(rth._nbVertexes), - _vertexIndexMatrixWeightList(rth._vertexIndexMatrixWeightList), + _nbVertices(rth._nbVertices), _bonePalette(rth._bonePalette), _boneNameToPalette(rth._boneNameToPalette), _boneWeightAttribArrays(rth._boneWeightAttribArrays), _uniformMatrixPalette(rth._uniformMatrixPalette), _shader(rth._shader), - _needInit(rth._needInit) + _needInit(rth._needInit), + _minAttribIndex(rth._minAttribIndex) { } -osg::Vec4Array* RigTransformHardware::getVertexAttrib(int index) +osg::Vec4Array* RigTransformHardware::getVertexAttrib(unsigned int index) { - if (index >= (int)_boneWeightAttribArrays.size()) + if (index >= _boneWeightAttribArrays.size()) return 0; return _boneWeightAttribArrays[index].get(); } -int RigTransformHardware::getNumVertexAttrib() -{ - return _boneWeightAttribArrays.size(); -} - -osg::Uniform* RigTransformHardware::getMatrixPaletteUniform() -{ - return _uniformMatrixPalette.get(); -} - - void RigTransformHardware::computeMatrixPaletteUniform(const osg::Matrix& transformFromSkeletonToGeometry, const osg::Matrix& invTransformFromSkeletonToGeometry) { - for (int i = 0; i < (int)_bonePalette.size(); i++) + for (unsigned int i = 0; i < _bonePalette.size(); ++i) { osg::ref_ptr bone = _bonePalette[i].get(); - const osg::Matrix& invBindMatrix = bone->getInvBindMatrixInSkeletonSpace(); - const osg::Matrix& boneMatrix = bone->getMatrixInSkeletonSpace(); - osg::Matrix resultBoneMatrix = invBindMatrix * boneMatrix; - osg::Matrix result = transformFromSkeletonToGeometry * resultBoneMatrix * invTransformFromSkeletonToGeometry; + const osg::Matrixf& invBindMatrix = bone->getInvBindMatrixInSkeletonSpace(); + const osg::Matrixf& boneMatrix = bone->getMatrixInSkeletonSpace(); + osg::Matrixf resultBoneMatrix = invBindMatrix * boneMatrix; + osg::Matrixf result = transformFromSkeletonToGeometry * resultBoneMatrix * invTransformFromSkeletonToGeometry; if (!_uniformMatrixPalette->setElement(i, result)) OSG_WARN << "RigTransformHardware::computeUniformMatrixPalette can't set uniform at " << i << " elements" << std::endl; } } - -int RigTransformHardware::getNumBonesPerVertex() const { return _bonesPerVertex;} -int RigTransformHardware::getNumVertexes() const { return _nbVertexes;} - -bool RigTransformHardware::createPalette(int nbVertexes, BoneMap boneMap, const VertexInfluenceSet::VertexIndexToBoneWeightMap& vertexIndexToBoneWeightMap) -{ - typedef std::map BoneNameCountMap; - BonePalette palette; - BoneNameCountMap boneNameCountMap; - - // init vertex attribute data - VertexIndexWeightList vertexIndexWeight; - vertexIndexWeight.resize(nbVertexes); - - int maxBonePerVertex = 0; - for (VertexInfluenceSet::VertexIndexToBoneWeightMap::const_iterator vit = vertexIndexToBoneWeightMap.begin(); vit != vertexIndexToBoneWeightMap.end(); ++vit) - { - int vertexIndex = vit->first; - const VertexInfluenceSet::BoneWeightList& boneWeightList = vit->second; - int bonesForThisVertex = 0; - for (VertexInfluenceSet::BoneWeightList::const_iterator it = boneWeightList.begin(); it != boneWeightList.end(); ++it) - { - const VertexInfluenceSet::BoneWeight& bw = *it; - if(fabs(bw.getWeight()) > 1e-2) // don't use bone with weight too small - { - if (boneNameCountMap.find(bw.getBoneName()) != boneNameCountMap.end()) - { - boneNameCountMap[bw.getBoneName()]++; - bonesForThisVertex++; // count max number of bones per vertexes - vertexIndexWeight[vertexIndex].push_back(IndexWeightEntry(_boneNameToPalette[bw.getBoneName()],bw.getWeight())); - } - else - { - if (boneMap.find(bw.getBoneName()) == boneMap.end()) - { - OSG_INFO << "RigTransformHardware::createPalette can't find bone " << bw.getBoneName() << " skip this influence" << std::endl; - continue; - } - boneNameCountMap[bw.getBoneName()] = 1; // for stats - bonesForThisVertex++; - palette.push_back(boneMap[bw.getBoneName()]); - _boneNameToPalette[bw.getBoneName()] = palette.size()-1; - vertexIndexWeight[vertexIndex].push_back(IndexWeightEntry(_boneNameToPalette[bw.getBoneName()],bw.getWeight())); - } - } - else - { - 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; - } - } - maxBonePerVertex = osg::maximum(maxBonePerVertex, bonesForThisVertex); - } - OSG_INFO << "RigTransformHardware::createPalette maximum number of bone per vertex is " << maxBonePerVertex << std::endl; - OSG_INFO << "RigTransformHardware::createPalette matrix palette has " << boneNameCountMap.size() << " entries" << std::endl; - - for (BoneNameCountMap::iterator it = boneNameCountMap.begin(); it != boneNameCountMap.end(); ++it) - { - OSG_INFO << "RigTransformHardware::createPalette Bone " << it->first << " is used " << it->second << " times" << std::endl; - } - - OSG_INFO << "RigTransformHardware::createPalette will use " << boneNameCountMap.size() * 4 << " uniforms" << std::endl; - - - _nbVertexes = nbVertexes; - _bonesPerVertex = maxBonePerVertex; - _bonePalette = palette; - _vertexIndexMatrixWeightList = vertexIndexWeight; - _uniformMatrixPalette = createVertexUniform(); - _boneWeightAttribArrays = createVertexAttribList(); - return true; -} - - // // create vertex attribute by 2 bones // vec4(boneIndex0, weight0, boneIndex1, weight1) @@ -154,133 +70,305 @@ bool RigTransformHardware::createPalette(int nbVertexes, BoneMap boneMap, const // the idea is to use this format to have a granularity smaller // than the 4 bones using two vertex attributes // -RigTransformHardware::BoneWeightAttribList RigTransformHardware::createVertexAttribList() -{ - BoneWeightAttribList arrayList; - int nbArray = static_cast(ceilf(getNumBonesPerVertex() * 0.5)); - if (!nbArray) - return arrayList; - arrayList.resize(nbArray); - for (int i = 0; i < nbArray; i++) +typedef std::vector > PerVertexInfList; + +///create normalized a set of Vertex Attribs given a PerVertexInfList and return the max num bone per vertex +unsigned int createVertexAttribList(const PerVertexInfList & perVertexInfluences, + RigTransformHardware::BoneWeightAttribList& boneWeightAttribArrays) +{ + short boneIndexInVec4; + unsigned int vertid = 0, + boneIndexInList; + IndexWeightList::size_type maxBonePerVertex = 0; + ///build vertex attrib arrays + //get maxBonePerVertex + for(PerVertexInfList::const_iterator vertinfit = perVertexInfluences.begin(); vertinfit != perVertexInfluences.end(); ++vertinfit) + maxBonePerVertex = osg::maximum(maxBonePerVertex, vertinfit->size()); + + OSG_INFO << "RigTransformHardware::createVertexAttribList maximum number of bone per vertex is " << maxBonePerVertex << std::endl; + + unsigned int nbArray = static_cast(ceilf( ((float)maxBonePerVertex) * 0.5f)); + + if (!nbArray) + return 0; + + ///create vertex attrib arrays + boneWeightAttribArrays.reserve(nbArray); + boneWeightAttribArrays.resize(nbArray); + for(unsigned int j = 0; j< nbArray; ++j) { - osg::ref_ptr array = new osg::Vec4Array(osg::Array::BIND_PER_VERTEX); - arrayList[i] = array; - int nbVertexes = getNumVertexes(); - array->resize(nbVertexes); - for (int j = 0; j < nbVertexes; j++) + osg::Vec4Array* vecattr = new osg::Vec4Array(osg::Array::BIND_PER_VERTEX); + vecattr->reserve(perVertexInfluences.size()); + vecattr->resize(perVertexInfluences.size()); + boneWeightAttribArrays[j] = vecattr; + } + + ///populate vertex attrib arrays + for(PerVertexInfList::const_iterator vertinfit = perVertexInfluences.begin(); + vertinfit != perVertexInfluences.end(); + ++vertinfit, ++vertid) + { + //sum for normalization + float sum = 0; + for(IndexWeightList::const_iterator iwit = vertinfit->begin(); iwit != vertinfit->end(); ++iwit) + sum+=iwit->second; + + if(sum< 1e-4) { - for (int b = 0; b < 2; b++) + OSG_WARN << "RigTransformHardware::createVertexAttribList Warning: vertex with zero sum weights: " <(_vertexIndexMatrixWeightList[j].size())) + osg::Vec4& dest = (* boneWeightAttribArrays[j])[vertid]; + for (unsigned int b = 0; b < 2; ++b) { - float boneIndex = static_cast(_vertexIndexMatrixWeightList[j][boneIndexInList].getIndex()); - float boneWeight = _vertexIndexMatrixWeightList[j][boneIndexInList].getWeight(); - // fill the vec4 - (*array)[j][0 + boneIndexInVec4] = boneIndex; - (*array)[j][1 + boneIndexInVec4] = boneWeight; + boneIndexInVec4 = b*2; + boneIndexInList = j*2 + b; + if (boneIndexInList < (*vertinfit).size()) + { + float boneIndex = static_cast((*vertinfit)[boneIndexInList].first); + ///normalization here + float boneWeight = (*vertinfit)[boneIndexInList].second*sum; + dest[0 + boneIndexInVec4] = boneIndex; + dest[1 + boneIndexInVec4] = boneWeight; + } + else + { + dest[0 + boneIndexInVec4] = 0; + dest[1 + boneIndexInVec4] = 0; + } } } } } - return arrayList; + return maxBonePerVertex; } - -osg::Uniform* RigTransformHardware::createVertexUniform() +bool RigTransformHardware::prepareData(RigGeometry& rig) { - osg::Uniform* uniform = new osg::Uniform(osg::Uniform::FLOAT_MAT4, "matrixPalette", _bonePalette.size()); - return uniform; -} + _nbVertices = rig.getSourceGeometry()->getVertexArray()->getNumElements(); + const VertexInfluenceMap &vertexInfluenceMap = *rig.getInfluenceMap(); + _perVertexInfluences.reserve(_nbVertices); + _perVertexInfluences.resize(_nbVertices); - -void RigTransformHardware::setShader(osg::Shader* shader) -{ - _shader = shader; -} - -bool RigTransformHardware::init(RigGeometry& geom) -{ - osg::Geometry& source = *geom.getSourceGeometry(); - osg::Vec3Array* positionSrc = dynamic_cast(source.getVertexArray()); - if (!positionSrc) + unsigned int localboneid = 0; + for (VertexInfluenceMap::const_iterator boneinflistit = vertexInfluenceMap.begin(); + boneinflistit != vertexInfluenceMap.end(); + ++boneinflistit, ++localboneid) { - OSG_WARN << "RigTransformHardware no vertex array in the geometry " << geom.getName() << std::endl; + const IndexWeightList& boneinflist = boneinflistit->second; + const std::string& bonename = boneinflistit->first; + + for(IndexWeightList::const_iterator infit = boneinflist.begin(); infit!=boneinflist.end(); ++infit) + { + const VertexIndexWeight& iw = *infit; + const unsigned int &index = iw.first; + const float &weight = iw.second; + IndexWeightList & iwlist = _perVertexInfluences[index]; + + if(fabs(weight) > 1e-4) // don't use bone with weight too small + { + iwlist.push_back(VertexIndexWeight(localboneid,weight)); + } + else + { + OSG_WARN << "RigTransformHardware::prepareData Bone " << bonename << " has a weight " << weight << " for vertex " << index << " this bone will not be in the palette" << std::endl; + } + } + } + return true; +} + + +bool RigTransformHardware::buildPalette(const BoneMap& boneMap, const RigGeometry& rig) +{ + + typedef std::map BoneNameCountMap; + _boneWeightAttribArrays.resize(0); + _bonePalette.clear(); + _boneNameToPalette.clear(); + + IndexWeightList::size_type maxBonePerVertex = 0; + BoneNameCountMap boneNameCountMap; + + const VertexInfluenceMap &vertexInfluenceMap = *rig.getInfluenceMap(); + BoneNamePaletteIndex::iterator boneName2PaletteIndex; + + ///create local boneid to paletteindex + unsigned int paletteindex; + std::vector localid2bone; + localid2bone.reserve(vertexInfluenceMap.size()); + for (osgAnimation::VertexInfluenceMap::const_iterator perBoneinfit = vertexInfluenceMap.begin(); + perBoneinfit != vertexInfluenceMap.end(); + ++perBoneinfit) + { + const std::string& bonename = perBoneinfit->first; + + if (bonename.empty()) + { + OSG_WARN << "RigTransformHardware::VertexInfluenceMap contains unamed bone IndexWeightList" << std::endl; + } + BoneMap::const_iterator bmit = boneMap.find(bonename); + if (bmit == boneMap.end() ) + { + OSG_WARN << "RigTransformHardware Bone " << bonename << " not found, skip the influence group " << std::endl; + localid2bone.push_back(-1); + continue; + } + if ( (boneName2PaletteIndex = _boneNameToPalette.find(bonename)) != _boneNameToPalette.end()) + { + boneNameCountMap[bonename]++; + paletteindex = boneName2PaletteIndex->second ; + } + else + { + boneNameCountMap[bonename] = 1; // for stats + _boneNameToPalette[bonename] = _bonePalette.size() ; + paletteindex = _bonePalette.size() ; + _bonePalette.push_back(bmit->second); + } + localid2bone.push_back(paletteindex); + } + OSG_INFO << "RigTransformHardware::buildPalette matrix palette has " << boneNameCountMap.size() << " entries" << std::endl; + for (BoneNameCountMap::iterator it = boneNameCountMap.begin(); it != boneNameCountMap.end(); ++it) + { + OSG_INFO << "RigTransformHardware::buildPalette Bone " << it->first << " is used " << it->second << " times" << std::endl; + } + OSG_INFO << "RigTransformHardware::buildPalette will use " << boneNameCountMap.size() * 4 << " uniforms" << std::endl; + + ///set paletteindices + for( std::vector::iterator idwlistit = _perVertexInfluences.begin(); idwlistit!=_perVertexInfluences.end(); ++idwlistit) + { + for( IndexWeightList::iterator idwit = idwlistit->begin(); idwit!=idwlistit->end();) + { + if(localid2bone[idwit->first]<0) + idwit = idwlistit->erase(idwit); + else + { + idwit->first = localid2bone[idwit->first]; + ++idwit; + } + } + } + if( (_bonesPerVertex = createVertexAttribList(_perVertexInfluences, _boneWeightAttribArrays) ) < 1 ) + return false; + + _uniformMatrixPalette = new osg::Uniform(osg::Uniform::FLOAT_MAT4, "matrixPalette", _bonePalette.size()); + + _needInit = true; + return true; +} + +bool RigTransformHardware::init(RigGeometry& rig) +{ + if(_perVertexInfluences.empty()) + { + prepareData(rig); return false; } - - if (!geom.getSkeleton()) - { - OSG_WARN << "RigTransformHardware no skeleton set in geometry " << geom.getName() << std::endl; + if(!rig.getSkeleton()) return false; - } - - - // copy shallow from source geometry to rig - geom.copyFrom(source); - BoneMapVisitor mapVisitor; - geom.getSkeleton()->accept(mapVisitor); - BoneMap bm = mapVisitor.getBoneMap(); + rig.getSkeleton()->accept(mapVisitor); + BoneMap boneMap = mapVisitor.getBoneMap(); - if (!createPalette(positionSrc->size(),bm, geom.getVertexInfluenceSet().getVertexToBoneList())) + if (!buildPalette(boneMap,rig) ) return false; - osg::ref_ptr program = new osg::Program; - program->setName("HardwareSkinning"); - if (!_shader.valid()) - _shader = osg::Shader::readShaderFile(osg::Shader::VERTEX,"skinning.vert"); + osg::Geometry& source = *rig.getSourceGeometry(); + osg::Vec3Array* positionSrc = dynamic_cast(source.getVertexArray()); - if (!_shader.valid()) { + if (!positionSrc) + { + OSG_WARN << "RigTransformHardware no vertex array in the geometry " << rig.getName() << std::endl; + return false; + } + + // copy shallow from source geometry to rig + rig.copyFrom(source); + + osg::ref_ptr program ; + osg::ref_ptr vertexshader; + osg::ref_ptr stateset = rig.getOrCreateStateSet(); + + //grab geom source program and vertex shader if _shader is not setted + if(!_shader.valid() && (program = (osg::Program*)stateset->getAttribute(osg::StateAttribute::PROGRAM))) + { + for(unsigned int i = 0; igetNumShaders(); ++i) + if(program->getShader(i)->getType() == osg::Shader::VERTEX) + { + vertexshader = program->getShader(i); + program->removeShader(vertexshader); + } + } + else + { + program = new osg::Program; + program->setName("HardwareSkinning"); + } + //set default source if _shader is not user setted + if (!vertexshader.valid()) + { + if (!_shader.valid()) + vertexshader = osg::Shader::readShaderFile(osg::Shader::VERTEX,"skinning.vert"); + else vertexshader = _shader; + } + + if (!vertexshader.valid()) + { OSG_WARN << "RigTransformHardware can't load VertexShader" << std::endl; return false; } // replace max matrix by the value from uniform { - std::string str = _shader->getShaderSource(); - std::string toreplace = std::string("MAX_MATRIX"); - std::size_t start = str.find(toreplace); - if (std::string::npos != start) { - std::stringstream ss; - ss << getMatrixPaletteUniform()->getNumElements(); - str.replace(start, toreplace.size(), ss.str()); - _shader->setShaderSource(str); - } - else - { - OSG_WARN << "MAX_MATRIX not found in Shader! " << str << std::endl; - } - OSG_INFO << "Shader " << str << std::endl; + std::string str = vertexshader->getShaderSource(); + std::string toreplace = std::string("MAX_MATRIX"); + std::size_t start = str.find(toreplace); + if (std::string::npos != start) + { + std::stringstream ss; + ss << getMatrixPaletteUniform()->getNumElements(); + str.replace(start, toreplace.size(), ss.str()); + vertexshader->setShaderSource(str); + } + else + { + OSG_WARN<< "MAX_MATRIX not found in Shader! " << str << std::endl; + } + OSG_INFO << "Shader " << str << std::endl; } - int attribIndex = 11; - int nbAttribs = getNumVertexAttrib(); - for (int i = 0; i < nbAttribs; i++) + unsigned int nbAttribs = getNumVertexAttrib(); + for (unsigned int i = 0; i < nbAttribs; i++) { std::stringstream ss; ss << "boneWeight" << i; - program->addBindAttribLocation(ss.str(), attribIndex + i); - geom.setVertexAttribArray(attribIndex + i, getVertexAttrib(i)); + program->addBindAttribLocation(ss.str(), _minAttribIndex + i); + rig.setVertexAttribArray(_minAttribIndex + i, getVertexAttrib(i)); OSG_INFO << "set vertex attrib " << ss.str() << std::endl; } - program->addShader(_shader.get()); - osg::ref_ptr ss = geom.getOrCreateStateSet(); - ss->addUniform(getMatrixPaletteUniform()); - ss->addUniform(new osg::Uniform("nbBonesPerVertex", getNumBonesPerVertex())); - ss->setAttributeAndModes(program.get()); + program->addShader(vertexshader.get()); + + stateset->removeUniform("nbBonesPerVertex"); + stateset->addUniform(new osg::Uniform("nbBonesPerVertex",_bonesPerVertex)); + + stateset->removeUniform("matrixPalette"); + stateset->addUniform(_uniformMatrixPalette); + + stateset->setAttribute(program.get()); _needInit = false; return true; } + void RigTransformHardware::operator()(RigGeometry& geom) { if (_needInit) diff --git a/src/osgAnimation/RigTransformSoftware.cpp b/src/osgAnimation/RigTransformSoftware.cpp index b9537cf8d..56f6c6477 100644 --- a/src/osgAnimation/RigTransformSoftware.cpp +++ b/src/osgAnimation/RigTransformSoftware.cpp @@ -1,5 +1,6 @@ /* -*-c++-*- * Copyright (C) 2009 Cedric Pinson + * Copyright (C) 2017 Julien Valentin * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or @@ -18,6 +19,8 @@ #include #include +#include + using namespace osgAnimation; RigTransformSoftware::RigTransformSoftware() @@ -33,128 +36,231 @@ RigTransformSoftware::RigTransformSoftware(const RigTransformSoftware& rts,const } -bool RigTransformSoftware::init(RigGeometry& geom) +void RigTransformSoftware::buildMinimumUpdateSet( const RigGeometry&rig ) { - if (!geom.getSkeleton()) + ///1 Create Index2Vec + unsigned int nbVertices=rig.getSourceGeometry()->getVertexArray()->getNumElements(); + const VertexInfluenceMap &vertexInfluenceMap = *rig.getInfluenceMap(); + std::vector perVertexInfluences; + perVertexInfluences.reserve(nbVertices); + perVertexInfluences.resize(nbVertices); + + unsigned int vimapBoneID = 0; + for (osgAnimation::VertexInfluenceMap::const_iterator perBoneinfit = vertexInfluenceMap.begin(); + perBoneinfit != vertexInfluenceMap.end(); + ++perBoneinfit,++vimapBoneID) + { + const IndexWeightList& inflist = perBoneinfit->second; + const std::string& bonename = perBoneinfit->first; + + if (bonename.empty()) + { + OSG_WARN << "RigTransformSoftware::VertexInfluenceMap contains unamed bone IndexWeightList" << std::endl; + } + for(IndexWeightList::const_iterator infit = inflist.begin(); infit!=inflist.end(); ++infit) + { + const VertexIndexWeight &iw = *infit; + const unsigned int &index = iw.first; + float weight = iw.second; + perVertexInfluences[index].push_back(BonePtrWeight(vimapBoneID, weight)); + } + } + + ///2 Create inverse mapping Vec2Vec from previous built Index2Vec + ///in order to minimize weighted matrices computation on update + _uniqVertexGroupList.clear(); + + typedef std::map UnifyBoneGroup; + UnifyBoneGroup unifyBuffer; + unsigned int vertexID = 0; + for (std::vector::iterator perVertinfit = perVertexInfluences.begin(); + perVertinfit!=perVertexInfluences.end(); + ++perVertinfit,++vertexID) + { + BonePtrWeightList &boneinfs = *perVertinfit; + // sort the vector to have a consistent key + std::sort(boneinfs.begin(), boneinfs.end() ); + // we use the vector as key to differentiate group + UnifyBoneGroup::iterator result = unifyBuffer.find(boneinfs); + if (result != unifyBuffer.end()) + result->second.getVertices().push_back(vertexID); + else + { + VertexGroup& vg = unifyBuffer[boneinfs]; + vg.getBoneWeights() = boneinfs; + vg.getVertices().push_back(vertexID); + } + } + + _uniqVertexGroupList.reserve(unifyBuffer.size()); + for (UnifyBoneGroup::const_iterator it = unifyBuffer.begin(); it != unifyBuffer.end(); ++it) + { + _uniqVertexGroupList.push_back(it->second); + } + OSG_INFO << "uniq groups " << _uniqVertexGroupList.size() << " for " << rig.getName() << std::endl; +} + + +bool RigTransformSoftware::prepareData(RigGeometry&rig) +{ + ///set geom as it source + if (rig.getSourceGeometry()) + rig.copyFrom(*rig.getSourceGeometry()); + + osg::Vec3Array* normalSrc = dynamic_cast(rig.getSourceGeometry()->getNormalArray()); + osg::Vec3Array* positionSrc = dynamic_cast(rig.getSourceGeometry()->getVertexArray()); + + if(!(positionSrc) || positionSrc->empty() ) + return false; + if(normalSrc && normalSrc->size() != positionSrc->size()) return false; - BoneMapVisitor mapVisitor; - geom.getSkeleton()->accept(mapVisitor); - BoneMap bm = mapVisitor.getBoneMap(); - initVertexSetFromBones(bm, geom.getVertexInfluenceSet().getUniqVertexSetToBoneSetList()); + /// setup Vertex and Normal arrays with copy of sources + rig.setVertexArray(new osg::Vec3Array); + osg::Vec3Array* positionDst = new osg::Vec3Array; + rig.setVertexArray(positionDst); + *positionDst = *positionSrc; + positionDst->setDataVariance(osg::Object::DYNAMIC); - if (geom.getSourceGeometry()) - geom.copyFrom(*geom.getSourceGeometry()); - geom.setVertexArray(0); - geom.setNormalArray(0); + if(normalSrc) + { + osg::Vec3Array* normalDst = new osg::Vec3Array; + *normalDst = *normalSrc; + rig.setNormalArray(normalDst, osg::Array::BIND_PER_VERTEX); + normalDst->setDataVariance(osg::Object::DYNAMIC); + } + + /// build minimal set of VertexGroup + buildMinimumUpdateSet(rig); + + return true; +} + +bool RigTransformSoftware::init(RigGeometry&rig) +{ + ///test if dataprepared + if(_uniqVertexGroupList.empty()) + { + prepareData(rig); + return false; + } + + if(!rig.getSkeleton()) + return false; + + ///get bonemap from skeleton + BoneMapVisitor mapVisitor; + rig.getSkeleton()->accept(mapVisitor); + BoneMap boneMap = mapVisitor.getBoneMap(); + VertexInfluenceMap & vertexInfluenceMap = *rig.getInfluenceMap(); + + ///create local bonemap + std::vector localid2bone; + localid2bone.reserve(vertexInfluenceMap.size()); + for (osgAnimation::VertexInfluenceMap::const_iterator perBoneinfit = vertexInfluenceMap.begin(); + perBoneinfit != vertexInfluenceMap.end(); + ++perBoneinfit) + { + const std::string& bonename = perBoneinfit->first; + + if (bonename.empty()) + { + OSG_WARN << "RigTransformSoftware::VertexInfluenceMap contains unamed bone IndexWeightList" << std::endl; + } + BoneMap::const_iterator bmit = boneMap.find(bonename); + if (bmit == boneMap.end() ) + { + if (_invalidInfluence.find(bonename) == _invalidInfluence.end()) + { + _invalidInfluence[bonename] = true; + OSG_WARN << "RigTransformSoftware Bone " << bonename << " not found, skip the influence group " << std::endl; + } + + localid2bone.push_back(0); + continue; + } + + Bone* bone = bmit->second.get(); + localid2bone.push_back(bone); + } + + ///fill bone ptr in the _uniqVertexGroupList + for(VertexGroupList::iterator itvg = _uniqVertexGroupList.begin(); itvg != _uniqVertexGroupList.end(); ++itvg) + { + VertexGroup& uniq = *itvg; + for(BonePtrWeightList::iterator bwit = uniq.getBoneWeights().begin(); bwit != uniq.getBoneWeights().end(); ) + { + Bone * b = localid2bone[bwit->getBoneID()]; + if(!b) + bwit = uniq.getBoneWeights().erase(bwit); + else + bwit++->setBonePtr(b); + } + } + + for(VertexGroupList::iterator itvg = _uniqVertexGroupList.begin(); itvg != _uniqVertexGroupList.end(); ++itvg) + { + itvg->normalize(); + } _needInit = false; + return true; } +void RigTransformSoftware::VertexGroup::normalize() +{ + osg::Matrix::value_type sum=0; + for(BonePtrWeightList::iterator bwit = _boneweights.begin(); bwit != _boneweights.end(); ++bwit ) + { + sum += bwit->getWeight(); + } + + if (sum < 1e-4) + { + OSG_WARN << "RigTransformSoftware::VertexGroup: warning try to normalize a zero sum vertexgroup" << std::endl; + } + else + { + for(BonePtrWeightList::iterator bwit = _boneweights.begin(); bwit != _boneweights.end(); ++bwit ) + { + bwit->setWeight(bwit->getWeight()/sum); + } + } +} + void RigTransformSoftware::operator()(RigGeometry& geom) { - if (_needInit) - if (!init(geom)) - return; + if (_needInit && !init(geom)) return; - if (!geom.getSourceGeometry()) { + if (!geom.getSourceGeometry()) + { OSG_WARN << this << " RigTransformSoftware no source geometry found on RigGeometry" << std::endl; return; } + osg::Geometry& source = *geom.getSourceGeometry(); osg::Geometry& destination = geom; - osg::Vec3Array* positionSrc = dynamic_cast(source.getVertexArray()); - osg::Vec3Array* positionDst = dynamic_cast(destination.getVertexArray()); - if (positionSrc ) - { - if (!positionDst || (positionDst->size() != positionSrc->size()) ) - { - if (!positionDst) - { - positionDst = new osg::Vec3Array; - positionDst->setDataVariance(osg::Object::DYNAMIC); - destination.setVertexArray(positionDst); - } - *positionDst = *positionSrc; - } - if (!positionDst->empty()) - { - compute(geom.getMatrixFromSkeletonToGeometry(), - geom.getInvMatrixFromSkeletonToGeometry(), - &positionSrc->front(), - &positionDst->front()); - positionDst->dirty(); - } - - } - + osg::Vec3Array* positionSrc = static_cast(source.getVertexArray()); + osg::Vec3Array* positionDst = static_cast(destination.getVertexArray()); osg::Vec3Array* normalSrc = dynamic_cast(source.getNormalArray()); - osg::Vec3Array* normalDst = dynamic_cast(destination.getNormalArray()); + osg::Vec3Array* normalDst = static_cast(destination.getNormalArray()); + + + compute(geom.getMatrixFromSkeletonToGeometry(), + geom.getInvMatrixFromSkeletonToGeometry(), + &positionSrc->front(), + &positionDst->front()); + positionDst->dirty(); + if (normalSrc ) { - if (!normalDst || (normalDst->size() != normalSrc->size()) ) - { - if (!normalDst) - { - normalDst = new osg::Vec3Array; - normalDst->setDataVariance(osg::Object::DYNAMIC); - destination.setNormalArray(normalDst, osg::Array::BIND_PER_VERTEX); - } - *normalDst = *normalSrc; - } - if (!normalDst->empty()) - { - computeNormal(geom.getMatrixFromSkeletonToGeometry(), - geom.getInvMatrixFromSkeletonToGeometry(), - &normalSrc->front(), - &normalDst->front()); - normalDst->dirty(); - } + computeNormal(geom.getMatrixFromSkeletonToGeometry(), + geom.getInvMatrixFromSkeletonToGeometry(), + &normalSrc->front(), + &normalDst->front()); + normalDst->dirty(); } } - -void RigTransformSoftware::initVertexSetFromBones(const BoneMap& map, const VertexInfluenceSet::UniqVertexSetToBoneSetList& influence) -{ - _boneSetVertexSet.clear(); - - int size = influence.size(); - _boneSetVertexSet.resize(size); - for (int i = 0; i < size; i++) - { - const VertexInfluenceSet::UniqVertexSetToBoneSet& inf = influence[i]; - int nbBones = inf.getBones().size(); - BoneWeightList& boneList = _boneSetVertexSet[i].getBones(); - - double sumOfWeight = 0; - for (int b = 0; b < nbBones; b++) - { - const std::string& bname = inf.getBones()[b].getBoneName(); - float weight = inf.getBones()[b].getWeight(); - BoneMap::const_iterator it = map.find(bname); - if (it == map.end() ) - { - if (_invalidInfluence.find(bname) != _invalidInfluence.end()) { - _invalidInfluence[bname] = true; - OSG_WARN << "RigTransformSoftware Bone " << bname << " not found, skip the influence group " <second.get(); - boneList.push_back(BoneWeight(bone, weight)); - sumOfWeight += weight; - } - // if a bone referenced by a vertexinfluence is missed it can make the sum less than 1.0 - // so we check it and renormalize the all weight bone - const double threshold = 1e-4; - if (!_boneSetVertexSet[i].getBones().empty() && - (sumOfWeight < 1.0 - threshold || sumOfWeight > 1.0 + threshold)) - { - for (int b = 0; b < (int)boneList.size(); b++) - boneList[b].setWeight(boneList[b].getWeight() / sumOfWeight); - } - _boneSetVertexSet[i].getVertexes() = inf.getVertexes(); - } -} diff --git a/src/osgAnimation/VertexInfluence.cpp b/src/osgAnimation/VertexInfluence.cpp index ad1686277..75a7b22a6 100644 --- a/src/osgAnimation/VertexInfluence.cpp +++ b/src/osgAnimation/VertexInfluence.cpp @@ -1,5 +1,6 @@ /* -*-c++-*- * Copyright (C) 2008 Cedric Pinson + * Copyright (C) 2017 Julien Valentin * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or @@ -13,77 +14,162 @@ */ #include +#include +#include #include #include #include +#include using namespace osgAnimation; -void VertexInfluenceSet::addVertexInfluence(const VertexInfluence& v) { _bone2Vertexes.push_back(v); } -const VertexInfluenceSet::VertexIndexToBoneWeightMap& VertexInfluenceSet::getVertexToBoneList() const { return _vertex2Bones;} -// this class manage VertexInfluence database by mesh -// reference bones per vertex ... -void VertexInfluenceSet::buildVertex2BoneList() +struct invweight_ordered { - _vertex2Bones.clear(); - for (BoneToVertexList::const_iterator it = _bone2Vertexes.begin(); it != _bone2Vertexes.end(); ++it) + inline bool operator() (const BoneWeight& bw1, const BoneWeight& bw2) const { - const VertexInfluence& vi = (*it); - int size = vi.size(); - for (int i = 0; i < size; i++) + if (bw1.second > bw2.second)return true; + if (bw1.second < bw2.second)return false; + return(bw1.first < bw2.first); + } +}; + +void VertexInfluenceMap::normalize(unsigned int numvert) +{ + + typedef std::pair > PerVertWeights; + std::vector localstore; + localstore.resize(numvert); + for(VertexInfluenceMap::iterator mapit = this->begin(); mapit != this->end(); ++mapit) + { + IndexWeightList &curvecinf = mapit->second; + for(IndexWeightList::iterator curinf = curvecinf.begin(); curinf != curvecinf.end(); ++curinf) { - VertexIndexWeight viw = vi[i]; - int index = viw.first; - float weight = viw.second; - if (vi.getName().empty()) - OSG_WARN << "VertexInfluenceSet::buildVertex2BoneList warning vertex " << index << " is not assigned to a bone" << std::endl; - _vertex2Bones[index].push_back(BoneWeight(vi.getName(), weight)); + VertexIndexWeight& inf = *curinf; + localstore[inf.first].first += inf.second; + localstore[inf.first].second.push_back(&inf.second); } } - - // normalize weight per vertex - for (VertexIndexToBoneWeightMap::iterator it = _vertex2Bones.begin(); it != _vertex2Bones.end(); ++it) + + unsigned int vertid = 0; + for(std::vector::iterator itvert = localstore.begin(); + itvert != localstore.end(); + ++itvert, ++vertid) { - BoneWeightList& bones = it->second; - int size = bones.size(); - float sum = 0; - for (int i = 0; i < size; i++) - sum += bones[i].getWeight(); - if (sum < 1e-4) + PerVertWeights & weights = *itvert; + if(weights.first< 1e-4) { - OSG_WARN << "VertexInfluenceSet::buildVertex2BoneList warning the vertex " << it->first << " seems to have 0 weight, skip normalize for this vertex" << std::endl; + OSG_WARN << "VertexInfluenceMap::normalize warning the vertex " <::iterator itf = weights.second.begin(); itf != weights.second.end(); ++itf) + { + **itf *= mult; + } + } + } + +} +///remove weakest influences in order to fit targetted numbonepervertex +void VertexInfluenceMap::cullInfluenceCountPerVertex(unsigned int numbonepervertex,float minweight, bool renormalize) +{ + + typedef std::set BoneWeightOrdered; + std::map tempVec2Bones; + for(VertexInfluenceMap::iterator mapit = this->begin(); mapit != this->end(); ++mapit) + { + const std::string& bonename = mapit->first; + IndexWeightList &curvecinf = mapit->second; + for(IndexWeightList::iterator curinf = curvecinf.begin(); curinf != curvecinf.end(); ++curinf) + { + VertexIndexWeight& inf = *curinf; + if( bonename.empty()) + { + OSG_WARN << "VertexInfluenceSet::cullInfluenceCountPerVertex warning vertex " << inf.first << " is not assigned to a bone" << std::endl; + } + else if(inf.second>minweight) + { + tempVec2Bones[inf.first].insert(BoneWeight(bonename, inf.second)); + } + } + } + this->clear(); + for( std::map::iterator mapit = tempVec2Bones.begin(); mapit != tempVec2Bones.end(); ++mapit) + { + BoneWeightOrdered& bwset = mapit->second; + unsigned int newsize = numbonepervertexnewsize)bwset.erase(*bwset.rbegin()); + if(renormalize) + { + for(BoneWeightOrdered::iterator bwit = bwset.begin(); bwit != bwset.end(); ++bwit) + { + sum += bwit->second; + } + + if(sum > 1e-4) + { + sum = 1.0f/sum; + for(BoneWeightOrdered::iterator bwit = bwset.begin(); bwit != bwset.end(); ++bwit) + { + VertexInfluence & inf = (*this)[bwit->first]; + inf.push_back(VertexIndexWeight(mapit->first, bwit->second*sum)); + inf.setName(bwit->first); + } + } + } + else + { + for(BoneWeightOrdered::iterator bwit = bwset.begin(); bwit != bwset.end(); ++bwit) + { + VertexInfluence & inf = (*this)[bwit->first]; + inf.push_back(VertexIndexWeight(mapit->first,bwit->second)); + inf.setName(bwit->first); + } } } } - +void VertexInfluenceMap::computePerVertexInfluenceList(std::vector& vertex2Bones,unsigned int numvert)const +{ + vertex2Bones.resize(numvert); + for (osgAnimation::VertexInfluenceMap::const_iterator it = begin(); it != end(); ++it) + { + const IndexWeightList& inflist = it->second; + if (it->first.empty()) + { + OSG_WARN << "VertexInfluenceMap::computePerVertexInfluenceList contains unamed bone IndexWeightList" << std::endl; + } + + for(IndexWeightList::const_iterator infit = inflist.begin(); infit != inflist.end(); ++infit) + { + const VertexIndexWeight &iw = *infit; + const unsigned int &index = iw.first; + const float &weight = iw.second; + vertex2Bones[index].push_back(BoneWeight(it->first, weight));; + } + } +} // sort by name and weight -struct SortByNameAndWeight : public std::less +struct SortByNameAndWeight : public std::less { - bool operator()(const VertexInfluenceSet::BoneWeight& b0, - const VertexInfluenceSet::BoneWeight& b1) const + bool operator()(const BoneWeight& b0, const BoneWeight& b1) const { - if (b0.getBoneName() < b1.getBoneName()) + if (b0.first < b1.first) return true; - else if (b0.getBoneName() > b1.getBoneName()) + else if (b0.first > b1.first) return false; - if (b0.getWeight() < b1.getWeight()) - return true; - return false; + + return (b0.second < b1.second); } }; -struct SortByBoneWeightList : public std::less +struct SortByBoneWeightList : public std::less { - bool operator()(const VertexInfluenceSet::BoneWeightList& b0, - const VertexInfluenceSet::BoneWeightList& b1) const + bool operator()(const BoneWeightList& b0, + const BoneWeightList& b1) const { if (b0.size() < b1.size()) return true; @@ -93,8 +179,7 @@ struct SortByBoneWeightList : public std::less& uniqVertexGroupList, unsigned int numvert) const { - _bone2Vertexes.clear(); - _uniqVertexSetToBoneSet.clear(); -} - -void VertexInfluenceSet::buildUniqVertexSetToBoneSetList() -{ - _uniqVertexSetToBoneSet.clear(); - - typedef std::map UnifyBoneGroup; + uniqVertexGroupList.clear(); + std::vector vertex2Bones; + computePerVertexInfluenceList(vertex2Bones,numvert); + typedef std::map UnifyBoneGroup; UnifyBoneGroup unifyBuffer; - for (VertexIndexToBoneWeightMap::iterator it = _vertex2Bones.begin(); it != _vertex2Bones.end(); ++it) + unsigned int vertexID = 0; + for (std::vector::iterator it = vertex2Bones.begin(); it != vertex2Bones.end(); ++it,++vertexID) { - BoneWeightList bones = it->second; - int vertexIndex = it->first; - + BoneWeightList &boneweightlist = *it; // sort the vector to have a consistent key - std::sort(bones.begin(), bones.end(), SortByNameAndWeight()); - + std::sort(boneweightlist.begin(), boneweightlist.end(), SortByNameAndWeight()); + // we use the vector as key to differentiate group - UnifyBoneGroup::iterator result = unifyBuffer.find(bones); + UnifyBoneGroup::iterator result = unifyBuffer.find(boneweightlist); if (result == unifyBuffer.end()) - unifyBuffer[bones].setBones(bones); - unifyBuffer[bones].getVertexes().push_back(vertexIndex); + { + unifyBuffer[boneweightlist].setBoneWeights(boneweightlist); + } + + unifyBuffer[boneweightlist].vertIDs().push_back(vertexID); } - - _uniqVertexSetToBoneSet.reserve(unifyBuffer.size()); + + if(vertex2Bones.size() == unifyBuffer.size()) + { + OSG_WARN << "VertexInfluenceMap::computeMinimalVertexGroupList is useless no duplicate VertexGroup" << std::endl; + } + + uniqVertexGroupList.reserve(unifyBuffer.size()); for (UnifyBoneGroup::iterator it = unifyBuffer.begin(); it != unifyBuffer.end(); ++it) { - _uniqVertexSetToBoneSet.push_back(it->second); + uniqVertexGroupList.push_back(it->second); } } +//Experimental Bone removal stuff +typedef std::vector RigList; +class CollectRigVisitor : public osg::NodeVisitor +{ +public: + META_NodeVisitor(osgAnimation, CollectRigVisitor) + CollectRigVisitor(); + + void apply(osg::Geometry& node); + inline const RigList& getRigList() const{return _map;} + +protected: + RigList _map; +}; + +CollectRigVisitor::CollectRigVisitor() : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {} + +void CollectRigVisitor::apply(osg::Geometry& node) +{ + RigGeometry* rig = dynamic_cast(&node); + if ( rig ) + _map.push_back(rig); +} + +bool recursiveisUsefull( Bone* bone, std::set foundnames) +{ + for(unsigned int i=0; igetNumChildren(); ++i) + { + Bone* child = dynamic_cast< Bone* >(bone->getChild(i)); + if(child) + { + if( foundnames.find(child->getName()) != foundnames.end() ) + return true; + if( recursiveisUsefull(child,foundnames) ) + return true; + } + } + return false; +} + +void VertexInfluenceMap::removeUnexpressedBones(Skeleton &skel) const +{ + BoneMapVisitor mapVisitor; + skel.accept(mapVisitor); + + CollectRigVisitor rigvis; + skel.accept(rigvis); + + RigList rigs = rigvis.getRigList(); + BoneMap boneMap = mapVisitor.getBoneMap(); + + unsigned int removed=0; + Bone* child, *par; + + std::set usebones; + for(RigList::iterator rigit = rigs.begin(); rigit != rigs.end(); ++rigit) + { + for(VertexInfluenceMap::iterator mapit = (*rigit)->getInfluenceMap()->begin(); + mapit != (*rigit)->getInfluenceMap()->end(); + ++mapit) + { + usebones.insert((*mapit).first); + } + } + + for(BoneMap::iterator bmit = boneMap.begin(); bmit != boneMap.end();) + { + if(usebones.find(bmit->second->getName()) == usebones.end()) + { + if( !(par = bmit->second->getBoneParent()) ) + { + ++bmit; + continue; + } + + Bone * bone2rm = bmit->second.get(); + + if( recursiveisUsefull(bone2rm,usebones)) + { + ++bmit; + continue; + } + + ///Bone can be removed + ++ removed; + OSG_INFO<<"removing useless bone: "<< bone2rm->getName() <getNumChildren(); numchild++) + { + if( (child = dynamic_cast(bone2rm->getChild(numchild))) ) + { + if(par!=child &&child!=bone2rm) + { + par->addChild(child); + nodes.push_back(child); + } + } + } + + for(unsigned int i=0; iremoveChild(nodes[i]); + } + par->removeChild(bone2rm); + + ///rebuild bonemap after bone removal + BoneMapVisitor mapVis ; + skel.accept(mapVis); + boneMap = mapVis.getBoneMap(); + bmit = boneMap.begin(); + + } + else + { + ++bmit; + } + } + OSG_WARN<<"Number of bone removed "< #include #include - +namespace osgAnimation_AnimationManagerBaseWrapper +{ static bool checkAnimations( const osgAnimation::AnimationManagerBase& manager ) { return manager.getAnimationList().size()>0; @@ -13,7 +14,8 @@ static bool checkAnimations( const osgAnimation::AnimationManagerBase& manager ) static bool readAnimations( osgDB::InputStream& is, osgAnimation::AnimationManagerBase& manager ) { - unsigned int size = is.readSize(); is >> is.BEGIN_BRACKET; + unsigned int size = is.readSize(); + is >> is.BEGIN_BRACKET; for ( unsigned int i=0; i ani = is.readObjectOfType(); @@ -26,16 +28,48 @@ static bool readAnimations( osgDB::InputStream& is, osgAnimation::AnimationManag static bool writeAnimations( osgDB::OutputStream& os, const osgAnimation::AnimationManagerBase& manager ) { const osgAnimation::AnimationList& animations = manager.getAnimationList(); - os.writeSize(animations.size()); os << os.BEGIN_BRACKET << std::endl; + os.writeSize(animations.size()); + os << os.BEGIN_BRACKET << std::endl; for ( osgAnimation::AnimationList::const_iterator itr=animations.begin(); - itr!=animations.end(); ++itr ) + itr!=animations.end(); ++itr ) { os << itr->get(); } os << os.END_BRACKET << std::endl; return true; } +struct osgAnimation_AnimationManagerBasegetnumAnimations : public osgDB::MethodObject +{ + virtual bool run(void* objectPtr, osg::Parameters& inputParameters, osg::Parameters& outputParameters) const + { + osgAnimation::AnimationManagerBase* group = dynamic_cast(reinterpret_cast(objectPtr)); + outputParameters.push_back(new osg::UIntValueObject("return",group->getNumRegisteredAnimations())); + return true; + } +}; +struct osgAnimation_AnimationManagerBasegetAnimation : public osgDB::MethodObject +{ + virtual bool run(void* objectPtr, osg::Parameters& inputParameters, osg::Parameters& outputParameters) const + { + if (inputParameters.empty()) return false; + osg::Object* indexObject = inputParameters[0].get(); + + unsigned int index = 0; + osg::DoubleValueObject* dvo = dynamic_cast(indexObject); + if (dvo) index = static_cast(dvo->getValue()); + else + { + osg::UIntValueObject* uivo = dynamic_cast(indexObject); + if (uivo) index = uivo->getValue(); + } + osgAnimation::AnimationManagerBase* group = dynamic_cast(reinterpret_cast(objectPtr)); + outputParameters.push_back(group->getRegisteredAnimation(index)); + + + return true; + } +}; REGISTER_OBJECT_WRAPPER( osgAnimation_AnimationManagerBase, /*new osgAnimation::AnimationManagerBase*/NULL, osgAnimation::AnimationManagerBase, @@ -43,7 +77,14 @@ REGISTER_OBJECT_WRAPPER( osgAnimation_AnimationManagerBase, { ADD_USER_SERIALIZER( Animations ); // _animations ADD_BOOL_SERIALIZER( AutomaticLink, true ); // _automaticLink -} + + { + UPDATE_TO_VERSION_SCOPED( 152 ) + ADD_METHOD_OBJECT( "getRegisteredAnimation", osgAnimation_AnimationManagerBasegetAnimation ); + ADD_METHOD_OBJECT( "getNumRegisteredAnimations", osgAnimation_AnimationManagerBasegetnumAnimations ); + } +} +} #undef OBJECT_CAST #define OBJECT_CAST static_cast diff --git a/src/osgWrappers/serializers/osgAnimation/BasicAnimationManager.cpp b/src/osgWrappers/serializers/osgAnimation/BasicAnimationManager.cpp index 22b913a0c..8aef704c8 100644 --- a/src/osgWrappers/serializers/osgAnimation/BasicAnimationManager.cpp +++ b/src/osgWrappers/serializers/osgAnimation/BasicAnimationManager.cpp @@ -5,12 +5,71 @@ #include #include #include +namespace osgAnimation_BasicAnimationManagerWrapper{ +struct BasicAnimationManagerIsplaying : public osgDB::MethodObject +{ + virtual bool run(void* objectPtr, osg::Parameters& inputParameters, osg::Parameters& outputParameters) const + { + if (inputParameters.empty()) return false; + osgAnimation::Animation* child = dynamic_cast(inputParameters[0].get()); + if (!child) return false; + osgAnimation::BasicAnimationManager* group = dynamic_cast(reinterpret_cast(objectPtr)); + outputParameters.push_back(new osg::BoolValueObject("return", group->isPlaying(child))); + return true; + } +}; +struct BasicAnimationManagerfindAnimation : public osgDB::MethodObject +{ + virtual bool run(void* objectPtr, osg::Parameters& inputParameters, osg::Parameters& outputParameters) const + { + if (inputParameters.empty()) return false; + + osgAnimation::Animation* child = dynamic_cast(inputParameters[0].get()); + if (!child) return false; + osgAnimation::BasicAnimationManager* group = dynamic_cast(reinterpret_cast(objectPtr)); + outputParameters.push_back(new osg::BoolValueObject("return",group->findAnimation(child))); + return true; + } +}; +struct BasicAnimationManagerPlayanimation : public osgDB::MethodObject +{ + virtual bool run(void* objectPtr, osg::Parameters& inputParameters, osg::Parameters& outputParameters) const + { + if (inputParameters.empty()) return false; + + osgAnimation::Animation* child = dynamic_cast(inputParameters[0].get()); + if (!child) return false; + osgAnimation::BasicAnimationManager* group = dynamic_cast(reinterpret_cast(objectPtr)); + group->playAnimation(child); + return true; + } +}; +struct BasicAnimationManagerStopanimation : public osgDB::MethodObject +{ + virtual bool run(void* objectPtr, osg::Parameters& inputParameters, osg::Parameters& outputParameters) const + { + if (inputParameters.empty()) return false; + + osgAnimation::Animation* child = dynamic_cast(inputParameters[0].get()); + if (!child) return false; + osgAnimation::BasicAnimationManager* group = dynamic_cast(reinterpret_cast(objectPtr)); + group->stopAnimation(child); + return true; + } +}; REGISTER_OBJECT_WRAPPER( osgAnimation_BasicAnimationManager, new osgAnimation::BasicAnimationManager, osgAnimation::BasicAnimationManager, "osg::Object osg::NodeCallback osgAnimation::AnimationManagerBase osgAnimation::BasicAnimationManager" ) { + + ADD_METHOD_OBJECT( "isPlaying", BasicAnimationManagerIsplaying ); + ADD_METHOD_OBJECT( "findAnimation", BasicAnimationManagerfindAnimation ); + ADD_METHOD_OBJECT( "playAnimation", BasicAnimationManagerPlayanimation ); + ADD_METHOD_OBJECT( "stopAnimation", BasicAnimationManagerStopanimation ); + +} } #undef OBJECT_CAST diff --git a/src/osgWrappers/serializers/osgAnimation/MorphGeometry.cpp b/src/osgWrappers/serializers/osgAnimation/MorphGeometry.cpp index 8d3d3c58b..d2af644a9 100644 --- a/src/osgWrappers/serializers/osgAnimation/MorphGeometry.cpp +++ b/src/osgWrappers/serializers/osgAnimation/MorphGeometry.cpp @@ -36,10 +36,47 @@ static bool writeMorphTargets( osgDB::OutputStream& os, const osgAnimation::Morp return true; } +#define ADD_ARRAYDATA_FUNCTIONS( ORIGINAL_PROP, PROP ) \ + static bool check##ORIGINAL_PROP( const osgAnimation::MorphGeometry& geom ) \ + { return geom.get##PROP()!=0; } \ + static bool read##ORIGINAL_PROP( osgDB::InputStream& is, osgAnimation::MorphGeometry& geom ) { \ + is >> is.BEGIN_BRACKET; \ + osg::ref_ptr array = is.readArray(); \ + geom.set##PROP(dynamic_cast(array.get())); \ + is >> is.END_BRACKET; \ + return true; \ + } \ + static bool write##ORIGINAL_PROP( osgDB::OutputStream& os, const osgAnimation::MorphGeometry& geom ) { \ + os << os.BEGIN_BRACKET << std::endl; \ + os.writeArray( geom.get##PROP()); \ + os << os.END_BRACKET << std::endl; \ + return true; \ + } +ADD_ARRAYDATA_FUNCTIONS( VertexData, VertexSource ) +ADD_ARRAYDATA_FUNCTIONS( NormalData, NormalSource ) + +struct FinishedObjectReadFillSourceIfRequiredCallback : public osgDB::FinishedObjectReadCallback +{ + virtual void objectRead(osgDB::InputStream&, osg::Object& obj) + { + + osgAnimation::MorphGeometry& geometry = static_cast(obj); + if((!geometry.getVertexSource() ||geometry.getVertexSource()->getNumElements()==0) + && dynamic_cast(geometry.getVertexArray())){ + geometry.setVertexSource((osg::Vec3Array* )geometry.getVertexArray()->clone(osg::CopyOp::DEEP_COPY_ALL)); + } + if((!geometry.getNormalSource() ||geometry.getNormalSource()->getNumElements()==0) + && geometry.getNormalArray()){ + geometry.setNormalSource((osg::Vec3Array* )geometry.getNormalArray()->clone(osg::CopyOp::DEEP_COPY_ALL)); + } +} + +}; + REGISTER_OBJECT_WRAPPER( osgAnimation_MorphGeometry, new osgAnimation::MorphGeometry, osgAnimation::MorphGeometry, - "osg::Object osg::Drawable osg::Geometry osgAnimation::MorphGeometry" ) + "osg::Object osg::Node osg::Drawable osg::Geometry osgAnimation::MorphGeometry" ) { BEGIN_ENUM_SERIALIZER( Method, NORMALIZED ); ADD_ENUM_VALUE( NORMALIZED ); @@ -48,4 +85,14 @@ REGISTER_OBJECT_WRAPPER( osgAnimation_MorphGeometry, ADD_USER_SERIALIZER( MorphTargets ); // _morphTargets ADD_BOOL_SERIALIZER( MorphNormals, true ); // _morphNormals + ADD_USER_SERIALIZER( VertexData ); // VertexSource + ADD_USER_SERIALIZER( NormalData ); // NormalSource + + + { + UPDATE_TO_VERSION_SCOPED( 147 ) + ADD_OBJECT_SERIALIZER( MorphTransformImplementation, osgAnimation::MorphTransform, NULL ); // _geometry + } + + wrapper->addFinishedObjectReadCallback( new FinishedObjectReadFillSourceIfRequiredCallback() ); } diff --git a/src/osgWrappers/serializers/osgAnimation/RigGeometry.cpp b/src/osgWrappers/serializers/osgAnimation/RigGeometry.cpp index 2787ec91c..be47b97b1 100644 --- a/src/osgWrappers/serializers/osgAnimation/RigGeometry.cpp +++ b/src/osgWrappers/serializers/osgAnimation/RigGeometry.cpp @@ -3,6 +3,7 @@ #include #include +namespace wrap_osgAnimationRigGeometry{ static bool checkInfluenceMap( const osgAnimation::RigGeometry& geom ) { return geom.getInfluenceMap()->size()>0; @@ -14,14 +15,13 @@ static bool readInfluenceMap( osgDB::InputStream& is, osgAnimation::RigGeometry& unsigned int size = is.readSize(); is >> is.BEGIN_BRACKET; for ( unsigned int i=0; i> is.PROPERTY("VertexInfluence"); - is.readWrappedString(name); + is.readWrappedString(bonename); viSize = is.readSize(); is >> is.BEGIN_BRACKET; - osgAnimation::VertexInfluence vi; - vi.setName( name ); + vi.setName( bonename ); vi.reserve( viSize ); for ( unsigned int j=0; j> index >> weight; vi.push_back( osgAnimation::VertexIndexWeight(index, weight) ); } - (*map)[name] = vi; + (*map)[bonename] = vi; is >> is.END_BRACKET; } is >> is.END_BRACKET; @@ -78,3 +78,4 @@ REGISTER_OBJECT_WRAPPER( osgAnimation_RigGeometry, ADD_OBJECT_SERIALIZER( RigTransformImplementation, osgAnimation::RigTransform, NULL ); // _geometry } } +} diff --git a/src/osgWrappers/serializers/osgAnimation/RigTransform.cpp b/src/osgWrappers/serializers/osgAnimation/RigTransform.cpp index 82ba7b05d..61d4fbd88 100644 --- a/src/osgWrappers/serializers/osgAnimation/RigTransform.cpp +++ b/src/osgWrappers/serializers/osgAnimation/RigTransform.cpp @@ -1,25 +1,70 @@ #include #include -#include +#include + #include + #include #include #include -namespace wrap_osgAnimationRigTransform{ - REGISTER_OBJECT_WRAPPER( osgAnimation_RigTransform, +namespace wrap_osgAnimationRigTransform +{ + REGISTER_OBJECT_WRAPPER( osgAnimation_RigTransform, NULL, osgAnimation::RigTransform, "osg::Object osgAnimation::RigTransform" ){} } -namespace wrap_osgAnimationRigTransformSoftWare{ - REGISTER_OBJECT_WRAPPER( osgAnimation_RigTransformSoftware, + +namespace wrap_osgAnimationRigTransformSoftWare +{ + REGISTER_OBJECT_WRAPPER( osgAnimation_RigTransformSoftware, new osgAnimation::RigTransformSoftware, osgAnimation::RigTransformSoftware, "osg::Object osgAnimation::RigTransform osgAnimation::RigTransformSoftware" ){} } -namespace wrap_osgAnimationRigTransformHardWare{ - REGISTER_OBJECT_WRAPPER( osgAnimation_RigTransformHardware, + +namespace wrap_osgAnimationRigTransformHardWare +{ + REGISTER_OBJECT_WRAPPER( osgAnimation_RigTransformHardware, new osgAnimation::RigTransformHardware, osgAnimation::RigTransformHardware, - "osg::Object osgAnimation::RigTransform osgAnimation::RigTransformHardware" ){} + "osg::Object osgAnimation::RigTransform osgAnimation::RigTransformHardware" ) + { + { + UPDATE_TO_VERSION_SCOPED(152) + ADD_OBJECT_SERIALIZER(Shader, osg::Shader, NULL); + ADD_UINT_SERIALIZER(FirstVertexAttributeTarget, RIGTRANSHW_DEFAULT_FIRST_VERTATTRIB_TARGETTED); + } + } +} + +namespace wrap_osgAnimationMorphTransform +{ + REGISTER_OBJECT_WRAPPER( osgAnimation_MorphTransform, + NULL, + osgAnimation::MorphTransform, + "osg::Object osgAnimation::MorphTransform" ){} +} + +namespace wrap_osgAnimationMorphTransformSoftWare +{ + REGISTER_OBJECT_WRAPPER( osgAnimation_MorphTransformSoftware, + new osgAnimation::MorphTransformSoftware, + osgAnimation::MorphTransformSoftware, + "osg::Object osgAnimation::MorphTransform osgAnimation::MorphTransformSoftware" ){} +} + +namespace wrap_osgAnimationMorphTransformHardware +{ + REGISTER_OBJECT_WRAPPER( osgAnimation_MorphTransformHardware, + new osgAnimation::MorphTransformHardware, + osgAnimation::MorphTransformHardware, + "osg::Object osgAnimation::MorphTransform osgAnimation::MorphTransformHardware") + { + { + UPDATE_TO_VERSION_SCOPED(152) + ADD_OBJECT_SERIALIZER(Shader, osg::Shader, NULL); + ADD_UINT_SERIALIZER(ReservedTextureUnit, MORPHTRANSHW_DEFAULTMORPHTEXTUREUNIT); + } + } }