diff --git a/src/osgPlugins/CMakeLists.txt b/src/osgPlugins/CMakeLists.txt index 8142d16bc..86dd99fc2 100644 --- a/src/osgPlugins/CMakeLists.txt +++ b/src/osgPlugins/CMakeLists.txt @@ -103,14 +103,6 @@ IF(GDAL_FOUND) ADD_SUBDIRECTORY(ogr) ENDIF(GDAL_FOUND) -IF(CURL_FOUND) - ADD_SUBDIRECTORY(curl) -ENDIF(CURL_FOUND) - -IF(ZLIB_FOUND) - ADD_SUBDIRECTORY(gz) -ENDIF(ZLIB_FOUND) - IF(XUL_FOUND) IF (WIN32 OR APPLE OR GTK_FOUND) ADD_SUBDIRECTORY(gecko) @@ -119,7 +111,6 @@ ENDIF(XUL_FOUND) - ############################################################ # # 3D Image plugins @@ -136,6 +127,15 @@ ENDIF(ITK_FOUND OR DCMTK_FOUND) ADD_SUBDIRECTORY(3dc) +IF(CURL_FOUND) + ADD_SUBDIRECTORY(curl) +ENDIF(CURL_FOUND) + +IF(ZLIB_FOUND) + ADD_SUBDIRECTORY(gz) +ENDIF(ZLIB_FOUND) + + IF(INVENTOR_FOUND) ADD_SUBDIRECTORY(Inventor) ENDIF(INVENTOR_FOUND) @@ -149,6 +149,7 @@ IF(COLLADA_FOUND) ENDIF(COLLADA_FOUND) ADD_SUBDIRECTORY(lwo) +ADD_SUBDIRECTORY(bvh) ADD_SUBDIRECTORY(x) ADD_SUBDIRECTORY(dw) ADD_SUBDIRECTORY(dxf) diff --git a/src/osgPlugins/bvh/CMakeLists.txt b/src/osgPlugins/bvh/CMakeLists.txt new file mode 100644 index 000000000..a00c8c5c4 --- /dev/null +++ b/src/osgPlugins/bvh/CMakeLists.txt @@ -0,0 +1,8 @@ +#this file is automatically generated + + +SET(TARGET_SRC ReaderWriterBVH.cpp ) + +SET(TARGET_ADDED_LIBRARIES osgAnimation ) +#### end var setup ### +SETUP_PLUGIN(bvh) diff --git a/src/osgPlugins/bvh/ReaderWriterBVH.cpp b/src/osgPlugins/bvh/ReaderWriterBVH.cpp new file mode 100644 index 000000000..54b57883e --- /dev/null +++ b/src/osgPlugins/bvh/ReaderWriterBVH.cpp @@ -0,0 +1,368 @@ +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +class BvhMotionBuilder : public osg::Referenced +{ +public: + typedef std::pair, int> JointNode; + typedef std::vector JointList; + + BvhMotionBuilder() : _drawingFlag(0) {} + virtual ~BvhMotionBuilder() {} + + static BvhMotionBuilder* instance() + { + static osg::ref_ptr s_library = new BvhMotionBuilder; + return s_library.get(); + } + + void buildHierarchy( osgDB::Input& fr, int level, osgAnimation::Bone* parent ) + { + bool isRecognized = false; + if ( !parent ) return; + + if ( fr.matchSequence("OFFSET %f %f %f") ) + { + isRecognized = true; + ++fr; + + osg::Vec3 offset; + if ( fr.readSequence(offset) ) + { + // Process OFFSET section + parent->setBindMatrixInBoneSpace( osg::Matrix::translate(offset) ); + if ( _drawingFlag && parent->getNumParents() && level>0 ) + parent->getParent(0)->addChild( createRefGeometry(offset, 0.5).get() ); + } + } + + if ( fr.matchSequence("CHANNELS %i") ) + { + isRecognized = true; + + // Process CHANNELS section + int noChannels; + fr[1].getInt( noChannels ); + fr += 2; + + for ( int i=0; i bone = new osgAnimation::Bone( parent->getName()+"End" ); + bone->setBindMatrixInBoneSpace( osg::Matrix::translate(offsetEndSite) ); + bone->setDataVariance( osg::Object::DYNAMIC ); + parent->addChild( bone ); + + if ( _drawingFlag ) + parent->addChild( createRefGeometry(offsetEndSite, 0.5).get() ); + } + } + fr.advanceOverCurrentFieldOrBlock(); + } + + if ( fr.matchSequence("ROOT %w {") || fr.matchSequence("JOINT %w {") ) + { + isRecognized = true; + + // Process JOINT section + osg::ref_ptr bone = new osgAnimation::Bone( fr[1].getStr() ); + bone->setDataVariance( osg::Object::DYNAMIC ); + osgAnimation::AnimationUpdateCallback* cb = + dynamic_cast( bone->getUpdateCallback() ); + if ( cb ) cb->setName( bone->getName() ); + parent->addChild( bone ); + _joints.push_back( JointNode(bone, 0) ); + + int entry = fr[1].getNoNestedBrackets(); + fr += 3; + while ( !fr.eof() && fr[0].getNoNestedBrackets()>entry ) + buildHierarchy( fr, entry, bone.get() ); + fr.advanceOverCurrentFieldOrBlock(); + } + + if ( !isRecognized ) + { + osg::notify(osg::WARN) << "BVH Reader: Unrecognized symbol " << fr[0].getStr() + << ". Ignore current field or block." << std::endl; + fr.advanceOverCurrentFieldOrBlock(); + } + } + + void buildMotion( osgDB::Input& fr, osgAnimation::Animation* anim ) + { + int i=0, frames=0; + float frameTime=0.033f; + + if ( !fr.readSequence("Frames:", frames) ) + { + osg::notify(osg::WARN) << "BVH Reader: Frame number setting not found, but an unexpected " + << fr[0].getStr() << ". Set to 1." << std::endl; + } + + ++fr; + if ( !fr.readSequence("Time:", frameTime) ) + { + osg::notify(osg::WARN) << "BVH Reader: Frame time setting not found, but an unexpected " + << fr[0].getStr() << ". Set to 0.033 (30FPS)." << std::endl; + } + + // Each joint has a position animating channel and an euler animating channel + std::vector< osg::ref_ptr > posChannels; + std::vector< osg::ref_ptr > rotChannels; + for ( JointList::iterator itr=_joints.begin(); itr!=_joints.end(); ++itr ) + { + std::string name = itr->first->getName(); + + osg::ref_ptr posKey = new osgAnimation::Vec3KeyframeContainer; + osg::ref_ptr posSampler = new osgAnimation::Vec3LinearSampler; + osg::ref_ptr posChannel = new osgAnimation::Vec3LinearChannel( posSampler.get() ); + posSampler->setKeyframeContainer( posKey.get() ); + posChannel->setName( "position" ); + posChannel->setTargetName( name ); + + osg::ref_ptr rotKey = new osgAnimation::QuatKeyframeContainer; + osg::ref_ptr rotSampler = new osgAnimation::QuatSphericalLinearSampler; + osg::ref_ptr rotChannel = new osgAnimation::QuatSphericalLinearChannel( rotSampler.get() ); + rotSampler->setKeyframeContainer( rotKey.get() ); + rotChannel->setName( "quaternion" ); + rotChannel->setTargetName( name ); + + posChannels.push_back( posChannel ); + rotChannels.push_back( rotChannel ); + } + + // Obtain motion data from the stream + while ( !fr.eof() && i(posChannel->getSampler()->getKeyframeContainer()), + dynamic_cast(rotChannel->getSampler()->getKeyframeContainer()) ); + } + + i++; + } + + // Add valid channels to the animate object + for ( unsigned int n=0; n<_joints.size(); ++n ) + { + if ( posChannels[n]->getOrCreateSampler()->getOrCreateKeyframeContainer()->size()>0 ) + anim->addChannel( posChannels[n].get() ); + if ( rotChannels[n]->getOrCreateSampler()->getOrCreateKeyframeContainer()->size()>0 ) + anim->addChannel( rotChannels[n].get() ); + } + } + + osgAnimation::AnimationManager* buildBVH( std::istream& stream, const osgDB::ReaderWriter::Options* options ) + { + if ( options ) + { + if ( options->getOptionString().find("contours")!=std::string::npos ) _drawingFlag = 1; + else if ( options->getOptionString().find("solids")!=std::string::npos ) _drawingFlag = 2; + } + + osgDB::Input fr; + fr.attach( &stream ); + + osg::ref_ptr skelroot = new osgAnimation::Skeleton; + osg::ref_ptr anim = new osgAnimation::Animation; + + while( !fr.eof() ) + { + if ( fr.matchSequence("HIERARCHY") ) + { + ++fr; + buildHierarchy( fr, 0, skelroot.get() ); + } + else if ( fr.matchSequence("MOTION") ) + { + ++fr; + buildMotion( fr, anim.get() ); + } + else + { + if ( fr[0].getStr()==NULL ) continue; + + osg::notify(osg::WARN) << "BVH Reader: Unexpected beginning " << fr[0].getStr() + << ", neither HIERARCHY nor MOTION. Stopped." << std::endl; + break; + } + } + +#if 0 + std::cout << "Bone number: " << _joints.size() << "\n"; + for ( unsigned int i=0; i<_joints.size(); ++i ) + { + JointNode node = _joints[i]; + std::cout << "Bone" << i << " " << node.first->getName() << ": "; + std::cout << std::hex << node.second << std::dec << "; "; + if ( node.first->getNumParents() ) + std::cout << "Parent: " << node.first->getParent(0)->getName(); + std::cout << "\n"; + } +#endif + + osgAnimation::AnimationManager* manager = new osgAnimation::AnimationManager; + manager->addChild( skelroot.get() ); + manager->registerAnimation( anim.get() ); + manager->buildTargetReference(); + manager->playAnimation( anim.get() ); + + _joints.clear(); + return manager; + } + +protected: + void alterChannel( std::string name, int& value ) + { + if ( name=="Xposition" ) value |= 0x01; + else if ( name=="Yposition" ) value |= 0x02; + else if ( name=="Zposition" ) value |= 0x04; + else if ( name=="Zrotation" ) value |= 0x08; + else if ( name=="Xrotation" ) value |= 0x10; + else if ( name=="Yrotation" ) value |= 0x20; + } + + void setKeyframe( osgDB::Input& fr, int ch, double time, + osgAnimation::Vec3KeyframeContainer* posKey, + osgAnimation::QuatKeyframeContainer* rotKey ) + { + if ( ch&0x07 && posKey ) // Position keyframe + { + float keyValue[3] = { 0.0f }; + if ( ch&0x01 ) fr.readSequence( keyValue[0] ); + if ( ch&0x02 ) fr.readSequence( keyValue[1] ); + if ( ch&0x04 ) fr.readSequence( keyValue[2] ); + + osg::Vec3 vec( keyValue[0], keyValue[1], keyValue[2] ); + posKey->push_back( osgAnimation::Vec3Keyframe(time, vec) ); + } + + if ( ch&0x38 && rotKey ) // Rotation keyframe + { + float keyValue[3] = { 0.0f }; + if ( ch&0x08 ) fr.readSequence( keyValue[2] ); + if ( ch&0x10 ) fr.readSequence( keyValue[0] ); + if ( ch&0x20 ) fr.readSequence( keyValue[1] ); + + // Note that BVH data need to concatenate the rotating matrices as Y*X*Z + // So we should not create Quat directly from input values, which makes a wrong X*Y*Z + osg::Matrix rotMat = osg::Matrix::rotate(osg::DegreesToRadians(keyValue[1]), osg::Vec3(0.0,1.0,0.0)) + * osg::Matrix::rotate(osg::DegreesToRadians(keyValue[0]), osg::Vec3(1.0,0.0,0.0)) + * osg::Matrix::rotate(osg::DegreesToRadians(keyValue[2]), osg::Vec3(0.0,0.0,1.0)); + osg::Quat quat = rotMat.getRotate(); + rotKey->push_back( osgAnimation::QuatKeyframe(time, quat) ); + } + } + + osg::ref_ptr createRefGeometry( osg::Vec3 p, double len ) + { + osg::ref_ptr geode = new osg::Geode; + + if ( _drawingFlag==1 ) + { + osg::ref_ptr geometry = new osg::Geometry; + osg::ref_ptr vertices = new osg::Vec3Array; + + // Joint + vertices->push_back( osg::Vec3(-len, 0.0, 0.0) ); + vertices->push_back( osg::Vec3( len, 0.0, 0.0) ); + vertices->push_back( osg::Vec3( 0.0,-len, 0.0) ); + vertices->push_back( osg::Vec3( 0.0, len, 0.0) ); + vertices->push_back( osg::Vec3( 0.0, 0.0,-len) ); + vertices->push_back( osg::Vec3( 0.0, 0.0, len) ); + + // Bone + vertices->push_back( osg::Vec3( 0.0, 0.0, 0.0) ); + vertices->push_back( p ); + + geometry->addPrimitiveSet( new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, 8) ); + geometry->setVertexArray( vertices.get() ); + + geode->addDrawable( geometry.get() ); + } + else if ( _drawingFlag==2 ) + { + osg::Quat quat; + osg::ref_ptr box = new osg::Box( p*0.5, p.length(), len, len ); + quat.makeRotate( osg::Vec3(1.0,0.0,0.0), p ); + box->setRotation( quat ); + + geode->addDrawable( new osg::ShapeDrawable(box.get()) ); + } + + return geode; + } + + int _drawingFlag; + JointList _joints; +}; + +class ReaderWriterBVH : public osgDB::ReaderWriter +{ +public: + ReaderWriterBVH() + { + supportsExtension( "bvh", "Biovision motion hierarchical file" ); + + supportsOption( "contours","Show the skeleton with lines." ); + supportsOption( "solids","Show the skeleton with solid boxes." ); + } + + virtual const char* className() const + { return "BVH Motion Reader"; } + + virtual ReadResult readNode(std::istream& stream, const osgDB::ReaderWriter::Options* options) const + { + ReadResult rr = BvhMotionBuilder::instance()->buildBVH( stream, options ); + return rr; + } + + virtual ReadResult readNode(const std::string& file, const osgDB::ReaderWriter::Options* options) const + { + std::string ext = osgDB::getLowerCaseFileExtension( file ); + if ( !acceptsExtension(ext) ) return ReadResult::FILE_NOT_HANDLED; + + std::string fileName = osgDB::findDataFile( file, options ); + if ( fileName.empty() ) return ReadResult::FILE_NOT_FOUND; + + std::ifstream stream( fileName.c_str(), std::ios::in|std::ios::binary ); + if( !stream ) return ReadResult::ERROR_IN_READING_FILE; + return readNode( stream, options ); + } +}; + +// Now register with Registry to instantiate the above reader/writer. +REGISTER_OSGPLUGIN( bvh, ReaderWriterBVH )