diff --git a/include/osgAnimation/MorphTransformHardware b/include/osgAnimation/MorphTransformHardware new file mode 100644 index 000000000..e75114a74 --- /dev/null +++ b/include/osgAnimation/MorphTransformHardware @@ -0,0 +1,60 @@ +/* -*-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_MORPH_TRANSFORM_HARDWARE +#define OSGANIMATION_MORPH_TRANSFORM_HARDWARE 1 + +#include +#include +#include +#include +#include +#include + +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&); + void setShader(osg::Shader*); + + + + protected: + + bool init(MorphGeometry&); + + + osg::ref_ptr _uniformTargetsWeight; + osg::ref_ptr _shader; + + bool _needInit; + }; +} + +#endif diff --git a/src/osgAnimation/CMakeLists.txt b/src/osgAnimation/CMakeLists.txt index dc199ef9c..7a79b914d 100644 --- a/src/osgAnimation/CMakeLists.txt +++ b/src/osgAnimation/CMakeLists.txt @@ -34,6 +34,7 @@ SET(TARGET_H ${HEADER_PATH}/RigTransform ${HEADER_PATH}/RigTransformHardware ${HEADER_PATH}/RigTransformSoftware + ${HEADER_PATH}/MorphTransformHardware ${HEADER_PATH}/MorphTransformSoftware ${HEADER_PATH}/Sampler ${HEADER_PATH}/Skeleton @@ -76,6 +77,7 @@ SET(TARGET_SRC RigGeometry.cpp RigTransformHardware.cpp RigTransformSoftware.cpp + MorphTransformHardware.cpp MorphTransformSoftware.cpp Skeleton.cpp StackedMatrixElement.cpp diff --git a/src/osgAnimation/MorphTransformHardware.cpp b/src/osgAnimation/MorphTransformHardware.cpp new file mode 100644 index 000000000..be37a470a --- /dev/null +++ b/src/osgAnimation/MorphTransformHardware.cpp @@ -0,0 +1,197 @@ +/* -*-c++-*- + * Copyleft 2016 Valentin Julien + * + * 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 + +///texture unit reserved for morphtarget TBO +#define MORPHTEXTUREUNIT 7 + +using namespace osgAnimation; + +MorphTransformHardware::MorphTransformHardware() +{ + _needInit = true; + +} + +MorphTransformHardware::MorphTransformHardware(const MorphTransformHardware& rth, const osg::CopyOp& copyop): + MorphTransform(rth, copyop), + _uniformTargetsWeight(rth._uniformTargetsWeight), + _shader(rth._shader), + _needInit(rth._needInit) +{ +} + +void MorphTransformHardware::setShader(osg::Shader* shader) +{ + _shader = shader; +} + +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) + { + ///check if source is setted correctly + if (!vertexSource|| vertexSource->size() != pos->size()) + { + vertexSource =(static_cast( pos->clone(osg::CopyOp::DEEP_COPY_ARRAYS)));//osg::Vec3Array(pos->begin(),pos->end()); + pos->setDataVariance(osg::Object::DYNAMIC); + } + osg::Vec3Array* normal = dynamic_cast(morphGeometry.getNormalArray()); + bool normalmorphable = morphGeometry.getMorphNormals() && normal; + if(!normalmorphable) { + OSG_WARN << "MorphTransformHardware::morph geometry "<size() != normal->size())) + { + normalSource =(static_cast( normal->clone(osg::CopyOp::DEEP_COPY_ARRAYS)));//osg::Vec3Array(normal->begin(),normal->end()); + normal->setDataVariance(osg::Object::DYNAMIC); + } + } + ///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(MORPHTEXTUREUNIT); + + //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); + } + }else { + + } 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(MORPHTEXTUREUNIT,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) + if (!init(geom)) + return; + + ///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(); +}