1 #include <osg/Notify> 2 #include <osg/Geometry> 3 #include <osg/ShapeDrawable> 4 5 #include <osgDB/FileNameUtils> 6 #include <osgDB/FileUtils> 7 #include <osgDB/Registry> 8 9 #include <osgAnimation/Bone> 10 #include <osgAnimation/Skeleton> 11 #include <osgAnimation/UpdateBone> 12 #include <osgAnimation/StackedTransform> 13 #include <osgAnimation/StackedTranslateElement> 14 #include <osgAnimation/StackedQuaternionElement> 15 #include <osgAnimation/BasicAnimationManager> 16 17 class BvhMotionBuilder : public osg::Referenced 18 { 19 public: 20 typedef std::pair<osg::ref_ptr<osgAnimation::Bone>, int> JointNode; 21 typedef std::vector<JointNode> JointList; 22 BvhMotionBuilder()23 BvhMotionBuilder() : _drawingFlag(0) {} ~BvhMotionBuilder()24 virtual ~BvhMotionBuilder() {} 25 instance()26 static BvhMotionBuilder* instance() 27 { 28 static osg::ref_ptr<BvhMotionBuilder> s_library = new BvhMotionBuilder; 29 return s_library.get(); 30 } 31 buildHierarchy(osgDB::Input & fr,int level,osgAnimation::Bone * parent)32 void buildHierarchy( osgDB::Input& fr, int level, osgAnimation::Bone* parent ) 33 { 34 bool isRecognized = false; 35 if ( !parent ) return; 36 37 if ( fr.matchSequence("OFFSET %f %f %f") ) 38 { 39 isRecognized = true; 40 ++fr; 41 42 osg::Vec3 offset; 43 if ( fr.readSequence(offset) ) 44 { 45 // Process OFFSET section 46 parent->setMatrixInSkeletonSpace( osg::Matrix::translate(offset) * 47 parent->getMatrixInSkeletonSpace() ); 48 osgAnimation::UpdateBone* updateBone = 49 dynamic_cast<osgAnimation::UpdateBone*>( parent->getUpdateCallback() ); 50 if ( updateBone ) 51 { 52 osgAnimation::StackedTransform& stack = updateBone->getStackedTransforms(); 53 stack.push_back( new osgAnimation::StackedTranslateElement("position", offset) ); 54 stack.push_back( new osgAnimation::StackedQuaternionElement("quaternion", osg::Quat()) ); 55 } 56 57 if ( _drawingFlag && parent->getNumParents() && level>0 ) 58 parent->getParent(0)->addChild( createRefGeometry(offset, 0.5).get() ); 59 } 60 } 61 62 if ( fr.matchSequence("CHANNELS %i") ) 63 { 64 isRecognized = true; 65 66 // Process CHANNELS section 67 int noChannels; 68 fr[1].getInt( noChannels ); 69 fr += 2; 70 71 for ( int i=0; i<noChannels; ++i ) 72 { 73 // Process each channel 74 std::string channelName; 75 fr.readSequence( channelName ); 76 alterChannel( channelName, _joints.back().second ); 77 } 78 } 79 80 if ( fr.matchSequence("End Site {") ) 81 { 82 isRecognized = true; 83 fr += 3; 84 85 if ( fr.matchSequence("OFFSET %f %f %f") ) 86 { 87 ++fr; 88 89 osg::Vec3 offsetEndSite; 90 if ( fr.readSequence(offsetEndSite) ) 91 { 92 // Process End Site section 93 osg::ref_ptr<osgAnimation::Bone> bone = new osgAnimation::Bone( parent->getName()+"End" ); 94 bone->setMatrixInSkeletonSpace( osg::Matrix::translate(offsetEndSite) * 95 bone->getMatrixInSkeletonSpace() ); 96 bone->setDataVariance( osg::Object::DYNAMIC ); 97 parent->insertChild( 0, bone.get() ); 98 99 if ( _drawingFlag ) 100 parent->addChild( createRefGeometry(offsetEndSite, 0.5).get() ); 101 } 102 } 103 fr.advanceOverCurrentFieldOrBlock(); 104 } 105 106 if ( fr.matchSequence("ROOT %w {") || fr.matchSequence("JOINT %w {") ) 107 { 108 isRecognized = true; 109 110 // Process JOINT section 111 osg::ref_ptr<osgAnimation::Bone> bone = new osgAnimation::Bone( fr[1].getStr() ); 112 bone->setDataVariance( osg::Object::DYNAMIC ); 113 bone->setDefaultUpdateCallback(); 114 parent->insertChild( 0, bone.get() ); 115 _joints.push_back( JointNode(bone, 0) ); 116 117 int entry = fr[1].getNoNestedBrackets(); 118 fr += 3; 119 while ( !fr.eof() && fr[0].getNoNestedBrackets()>entry ) 120 buildHierarchy( fr, entry, bone.get() ); 121 fr.advanceOverCurrentFieldOrBlock(); 122 } 123 124 if ( !isRecognized ) 125 { 126 OSG_WARN << "BVH Reader: Unrecognized symbol " << fr[0].getStr() 127 << ". Ignore current field or block." << std::endl; 128 fr.advanceOverCurrentFieldOrBlock(); 129 } 130 } 131 buildMotion(osgDB::Input & fr,osgAnimation::Animation * anim)132 void buildMotion( osgDB::Input& fr, osgAnimation::Animation* anim ) 133 { 134 int i=0, frames=0; 135 float frameTime=0.033f; 136 137 if ( !fr.readSequence("Frames:", frames) ) 138 { 139 OSG_WARN << "BVH Reader: Frame number setting not found, but an unexpected " 140 << fr[0].getStr() << ". Set to 1." << std::endl; 141 } 142 143 ++fr; 144 if ( !fr.readSequence("Time:", frameTime) ) 145 { 146 OSG_WARN << "BVH Reader: Frame time setting not found, but an unexpected " 147 << fr[0].getStr() << ". Set to 0.033 (30FPS)." << std::endl; 148 } 149 150 // Each joint has a position animating channel and an euler animating channel 151 std::vector< osg::ref_ptr<osgAnimation::Vec3LinearChannel> > posChannels; 152 std::vector< osg::ref_ptr<osgAnimation::QuatSphericalLinearChannel> > rotChannels; 153 for ( JointList::iterator itr=_joints.begin(); itr!=_joints.end(); ++itr ) 154 { 155 std::string name = itr->first->getName(); 156 157 osg::ref_ptr<osgAnimation::Vec3KeyframeContainer> posKey = new osgAnimation::Vec3KeyframeContainer; 158 osg::ref_ptr<osgAnimation::Vec3LinearSampler> posSampler = new osgAnimation::Vec3LinearSampler; 159 osg::ref_ptr<osgAnimation::Vec3LinearChannel> posChannel = new osgAnimation::Vec3LinearChannel( posSampler.get() ); 160 posSampler->setKeyframeContainer( posKey.get() ); 161 posChannel->setName( "position" ); 162 posChannel->setTargetName( name ); 163 164 osg::ref_ptr<osgAnimation::QuatKeyframeContainer> rotKey = new osgAnimation::QuatKeyframeContainer; 165 osg::ref_ptr<osgAnimation::QuatSphericalLinearSampler> rotSampler = new osgAnimation::QuatSphericalLinearSampler; 166 osg::ref_ptr<osgAnimation::QuatSphericalLinearChannel> rotChannel = new osgAnimation::QuatSphericalLinearChannel( rotSampler.get() ); 167 rotSampler->setKeyframeContainer( rotKey.get() ); 168 rotChannel->setName( "quaternion" ); 169 rotChannel->setTargetName( name ); 170 171 posChannels.push_back( posChannel ); 172 rotChannels.push_back( rotChannel ); 173 } 174 175 // Obtain motion data from the stream 176 while ( !fr.eof() && i<frames ) 177 { 178 for ( unsigned int n=0; n<_joints.size(); ++n ) 179 { 180 osgAnimation::Vec3LinearChannel* posChannel = posChannels[n].get(); 181 osgAnimation::QuatSphericalLinearChannel* rotChannel = rotChannels[n].get(); 182 183 setKeyframe( fr, _joints[n].second, frameTime*(double)i, 184 dynamic_cast<osgAnimation::Vec3KeyframeContainer*>(posChannel->getSampler()->getKeyframeContainer()), 185 dynamic_cast<osgAnimation::QuatKeyframeContainer*>(rotChannel->getSampler()->getKeyframeContainer()) ); 186 } 187 188 i++; 189 } 190 191 // Add valid channels to the animate object 192 for ( unsigned int n=0; n<_joints.size(); ++n ) 193 { 194 if ( posChannels[n]->getOrCreateSampler()->getOrCreateKeyframeContainer()->size()>0 ) 195 anim->addChannel( posChannels[n].get() ); 196 if ( rotChannels[n]->getOrCreateSampler()->getOrCreateKeyframeContainer()->size()>0 ) 197 anim->addChannel( rotChannels[n].get() ); 198 } 199 } 200 buildBVH(std::istream & stream,const osgDB::ReaderWriter::Options * options)201 osg::Group* buildBVH( std::istream& stream, const osgDB::ReaderWriter::Options* options ) 202 { 203 if ( options ) 204 { 205 if ( options->getOptionString().find("contours")!=std::string::npos ) _drawingFlag = 1; 206 else if ( options->getOptionString().find("solids")!=std::string::npos ) _drawingFlag = 2; 207 } 208 209 osgDB::Input fr; 210 fr.attach( &stream ); 211 212 osg::ref_ptr<osgAnimation::Bone> boneroot = new osgAnimation::Bone( "Root" ); 213 boneroot->setDefaultUpdateCallback(); 214 215 osg::ref_ptr<osgAnimation::Skeleton> skelroot = new osgAnimation::Skeleton; 216 skelroot->setDefaultUpdateCallback(); 217 skelroot->insertChild( 0, boneroot.get() ); 218 219 osg::ref_ptr<osgAnimation::Animation> anim = new osgAnimation::Animation; 220 221 while( !fr.eof() ) 222 { 223 if ( fr.matchSequence("HIERARCHY") ) 224 { 225 ++fr; 226 buildHierarchy( fr, 0, boneroot.get() ); 227 } 228 else if ( fr.matchSequence("MOTION") ) 229 { 230 ++fr; 231 buildMotion( fr, anim.get() ); 232 } 233 else 234 { 235 if ( fr[0].getStr()==NULL ) continue; 236 237 OSG_WARN << "BVH Reader: Unexpected beginning " << fr[0].getStr() 238 << ", neither HIERARCHY nor MOTION. Stopped." << std::endl; 239 break; 240 } 241 } 242 243 #if 0 244 std::cout << "Bone number: " << _joints.size() << "\n"; 245 for ( unsigned int i=0; i<_joints.size(); ++i ) 246 { 247 JointNode node = _joints[i]; 248 std::cout << "Bone" << i << " " << node.first->getName() << ": "; 249 std::cout << std::hex << node.second << std::dec << "; "; 250 if ( node.first->getNumParents() ) 251 std::cout << "Parent: " << node.first->getParent(0)->getName(); 252 std::cout << "\n"; 253 } 254 #endif 255 256 osg::Group* root = new osg::Group; 257 osgAnimation::BasicAnimationManager* manager = new osgAnimation::BasicAnimationManager; 258 root->addChild( skelroot.get() ); 259 root->setUpdateCallback(manager); 260 manager->registerAnimation( anim.get() ); 261 manager->buildTargetReference(); 262 manager->playAnimation( anim.get() ); 263 264 _joints.clear(); 265 return root; 266 } 267 268 protected: alterChannel(std::string name,int & value)269 void alterChannel( std::string name, int& value ) 270 { 271 if ( name=="Xposition" ) value |= 0x01; 272 else if ( name=="Yposition" ) value |= 0x02; 273 else if ( name=="Zposition" ) value |= 0x04; 274 else if ( name=="Zrotation" ) value |= 0x08; 275 else if ( name=="Xrotation" ) value |= 0x10; 276 else if ( name=="Yrotation" ) value |= 0x20; 277 } 278 setKeyframe(osgDB::Input & fr,int ch,double time,osgAnimation::Vec3KeyframeContainer * posKey,osgAnimation::QuatKeyframeContainer * rotKey)279 void setKeyframe( osgDB::Input& fr, int ch, double time, 280 osgAnimation::Vec3KeyframeContainer* posKey, 281 osgAnimation::QuatKeyframeContainer* rotKey ) 282 { 283 if ( ch&0x07 && posKey ) // Position keyframe 284 { 285 float keyValue[3] = { 0.0f }; 286 if ( ch&0x01 ) fr.readSequence( keyValue[0] ); 287 if ( ch&0x02 ) fr.readSequence( keyValue[1] ); 288 if ( ch&0x04 ) fr.readSequence( keyValue[2] ); 289 290 osg::Vec3 vec( keyValue[0], keyValue[1], keyValue[2] ); 291 posKey->push_back( osgAnimation::Vec3Keyframe(time, vec) ); 292 } 293 294 if ( ch&0x38 && rotKey ) // Rotation keyframe 295 { 296 float keyValue[3] = { 0.0f }; 297 if ( ch&0x08 ) fr.readSequence( keyValue[2] ); 298 if ( ch&0x10 ) fr.readSequence( keyValue[0] ); 299 if ( ch&0x20 ) fr.readSequence( keyValue[1] ); 300 301 // Note that BVH data need to concatenate the rotating matrices as Y*X*Z 302 // So we should not create Quat directly from input values, which makes a wrong X*Y*Z 303 osg::Matrix rotMat = osg::Matrix::rotate(osg::DegreesToRadians(keyValue[1]), osg::Vec3(0.0,1.0,0.0)) 304 * osg::Matrix::rotate(osg::DegreesToRadians(keyValue[0]), osg::Vec3(1.0,0.0,0.0)) 305 * osg::Matrix::rotate(osg::DegreesToRadians(keyValue[2]), osg::Vec3(0.0,0.0,1.0)); 306 osg::Quat quat = rotMat.getRotate(); 307 rotKey->push_back( osgAnimation::QuatKeyframe(time, quat) ); 308 } 309 } 310 createRefGeometry(osg::Vec3 p,double len)311 osg::ref_ptr<osg::Geode> createRefGeometry( osg::Vec3 p, double len ) 312 { 313 osg::ref_ptr<osg::Geode> geode = new osg::Geode; 314 315 if ( _drawingFlag==1 ) 316 { 317 osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry; 318 osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array; 319 320 // Joint 321 vertices->push_back( osg::Vec3(-len, 0.0, 0.0) ); 322 vertices->push_back( osg::Vec3( len, 0.0, 0.0) ); 323 vertices->push_back( osg::Vec3( 0.0,-len, 0.0) ); 324 vertices->push_back( osg::Vec3( 0.0, len, 0.0) ); 325 vertices->push_back( osg::Vec3( 0.0, 0.0,-len) ); 326 vertices->push_back( osg::Vec3( 0.0, 0.0, len) ); 327 328 // Bone 329 vertices->push_back( osg::Vec3( 0.0, 0.0, 0.0) ); 330 vertices->push_back( p ); 331 332 geometry->addPrimitiveSet( new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, 8) ); 333 geometry->setVertexArray( vertices.get() ); 334 335 geode->addDrawable( geometry.get() ); 336 } 337 else if ( _drawingFlag==2 ) 338 { 339 osg::Quat quat; 340 osg::ref_ptr<osg::Box> box = new osg::Box( p*0.5, p.length(), len, len ); 341 quat.makeRotate( osg::Vec3(1.0,0.0,0.0), p ); 342 box->setRotation( quat ); 343 344 geode->addDrawable( new osg::ShapeDrawable(box.get()) ); 345 } 346 347 return geode; 348 } 349 350 int _drawingFlag; 351 JointList _joints; 352 }; 353 354 class ReaderWriterBVH : public osgDB::ReaderWriter 355 { 356 public: ReaderWriterBVH()357 ReaderWriterBVH() 358 { 359 supportsExtension( "bvh", "Biovision motion hierarchical file" ); 360 361 supportsOption( "contours","Show the skeleton with lines." ); 362 supportsOption( "solids","Show the skeleton with solid boxes." ); 363 } 364 className() const365 virtual const char* className() const 366 { return "BVH Motion Reader"; } 367 readObject(std::istream & stream,const osgDB::ReaderWriter::Options * options) const368 virtual ReadResult readObject(std::istream& stream, const osgDB::ReaderWriter::Options* options) const 369 { 370 return readNode(stream, options); 371 } 372 readNode(std::istream & stream,const osgDB::ReaderWriter::Options * options) const373 virtual ReadResult readNode(std::istream& stream, const osgDB::ReaderWriter::Options* options) const 374 { 375 ReadResult rr = BvhMotionBuilder::instance()->buildBVH( stream, options ); 376 return rr; 377 } 378 readObject(const std::string & fileName,const osgDB::ReaderWriter::Options * options) const379 virtual ReadResult readObject(const std::string& fileName, const osgDB::ReaderWriter::Options* options) const 380 { 381 return readNode(fileName, options); 382 } 383 readNode(const std::string & file,const osgDB::ReaderWriter::Options * options) const384 virtual ReadResult readNode(const std::string& file, const osgDB::ReaderWriter::Options* options) const 385 { 386 std::string ext = osgDB::getLowerCaseFileExtension( file ); 387 if ( !acceptsExtension(ext) ) return ReadResult::FILE_NOT_HANDLED; 388 389 std::string fileName = osgDB::findDataFile( file, options ); 390 if ( fileName.empty() ) return ReadResult::FILE_NOT_FOUND; 391 392 osgDB::ifstream stream( fileName.c_str(), std::ios::in|std::ios::binary ); 393 if( !stream ) return ReadResult::ERROR_IN_READING_FILE; 394 return readNode( stream, options ); 395 } 396 }; 397 398 // Now register with Registry to instantiate the above reader/writer. 399 REGISTER_OSGPLUGIN( bvh, ReaderWriterBVH ) 400