| 235 | | CafuModelT::AnimT Anim; |
| 236 | | |
| 237 | | Anim.Name="Anim"; |
| 238 | | Anim.FPS =24.0f; |
| 239 | | Anim.Next=-1; |
| 240 | | |
| 241 | | while (!TP.IsAtEOF()) |
| 242 | | { |
| 243 | | const std::string Token=TP.GetNextToken(); |
| 244 | | |
| 245 | | if (Token=="MD5Version" ) { if (TP.GetNextToken()!="10") throw LoadErrorT("MD5Version is not 10."); } |
| 246 | | else if (Token=="commandline") TP.GetNextToken(); // Ignore the command line. |
| 247 | | else if (Token=="frameRate" ) Anim.FPS=TP.GetNextTokenAsFloat(); |
| 248 | | else if (Token=="numFrames" ) |
| 249 | | { |
| 250 | | // Be stricter with numFrames than with some num* variables in the md5mesh file. |
| 251 | | // This is required because for example there must be as many bounding boxes as frames. |
| 252 | | if (Anim.Frames.Size()>0) throw LoadErrorT("Anim.Frames.Size() > 0"); |
| 253 | | |
| 254 | | Anim.Frames.PushBackEmpty(TP.GetNextTokenAsInt()); |
| 255 | | } |
| 256 | | else if (Token=="numJoints") |
| 257 | | { |
| 258 | | // The numJoints here MUST match the numJoints in the md5mesh file! |
| 259 | | const unsigned long numJoints=TP.GetNextTokenAsInt(); |
| 260 | | |
| 261 | | if (numJoints!=Joints.Size()) { printf("%lu joints in md5anim file, %lu joints in md5mesh.\n", numJoints, Joints.Size()); throw LoadErrorT("Number of joints in the md5anim file does not match number of bones in the md5mesh."); } |
| 262 | | if (Anim.AnimJoints.Size()>0) { printf("Anim.AnimJoints.Size()==%lu\n", Anim.AnimJoints.Size()); throw LoadErrorT("Anim.AnimJoints.Size() > 0"); } |
| 263 | | |
| 264 | | Anim.AnimJoints.PushBackEmpty(numJoints); |
| 265 | | } |
| 266 | | else if (Token=="numAnimatedComponents") |
| 267 | | { |
| 268 | | const unsigned long numAnimatedComponents=TP.GetNextTokenAsInt(); |
| 269 | | |
| 270 | | // This is the number of components that is animated in the frames (and thus so many values are stored with each frame). |
| 271 | | // Therefore, the number of frames must have been specified already, so we can allocate all the memory here. |
| 272 | | if (Anim.Frames.Size()==0) throw LoadErrorT("Anim.Frames.Size() == 0"); |
| 273 | | |
| 274 | | for (unsigned long FrameNr=0; FrameNr<Anim.Frames.Size(); FrameNr++) |
| 275 | | Anim.Frames[FrameNr].AnimData.PushBackEmpty(numAnimatedComponents); |
| 276 | | } |
| 277 | | else if (Token=="hierarchy") |
| 278 | | { |
| 279 | | TP.AssertAndSkipToken("{"); |
| 280 | | |
| 281 | | for (unsigned long JointNr=0; JointNr<Anim.AnimJoints.Size(); JointNr++) |
| 282 | | { |
| 283 | | // Make sure that the name and parent are identical with the joint from the md5mesh file. |
| 284 | | if (Joints[JointNr].Name !=TP.GetNextToken()) throw LoadErrorT("Mismatching joint name."); |
| 285 | | if (Joints[JointNr].Parent!=TP.GetNextTokenAsInt()) throw LoadErrorT("Mismatching joint parent."); |
| 286 | | |
| 287 | | Anim.AnimJoints[JointNr].Flags =TP.GetNextTokenAsInt(); |
| 288 | | Anim.AnimJoints[JointNr].FirstDataIdx=TP.GetNextTokenAsInt(); |
| 289 | | } |
| 290 | | |
| 291 | | TP.AssertAndSkipToken("}"); |
| 292 | | } |
| 293 | | else if (Token=="bounds") |
| 294 | | { |
| 295 | | TP.AssertAndSkipToken("{"); |
| 296 | | |
| 297 | | for (unsigned long FrameNr=0; FrameNr<Anim.Frames.Size(); FrameNr++) |
| 298 | | { |
| 299 | | TP.AssertAndSkipToken("("); |
| 300 | | Anim.Frames[FrameNr].BB.Min.x=TP.GetNextTokenAsFloat(); |
| 301 | | Anim.Frames[FrameNr].BB.Min.y=TP.GetNextTokenAsFloat(); |
| 302 | | Anim.Frames[FrameNr].BB.Min.z=TP.GetNextTokenAsFloat(); |
| 303 | | TP.AssertAndSkipToken(")"); |
| 304 | | TP.AssertAndSkipToken("("); |
| 305 | | Anim.Frames[FrameNr].BB.Max.x=TP.GetNextTokenAsFloat(); |
| 306 | | Anim.Frames[FrameNr].BB.Max.y=TP.GetNextTokenAsFloat(); |
| 307 | | Anim.Frames[FrameNr].BB.Max.z=TP.GetNextTokenAsFloat(); |
| 308 | | TP.AssertAndSkipToken(")"); |
| 309 | | } |
| 310 | | |
| 311 | | TP.AssertAndSkipToken("}"); |
| 312 | | } |
| 313 | | else if (Token=="baseframe") |
| 314 | | { |
| 315 | | TP.AssertAndSkipToken("{"); |
| 316 | | |
| 317 | | for (unsigned long JointNr=0; JointNr<Anim.AnimJoints.Size(); JointNr++) |
| 318 | | { |
| 319 | | TP.AssertAndSkipToken("("); |
| 320 | | Anim.AnimJoints[JointNr].DefaultPos.x=TP.GetNextTokenAsFloat(); |
| 321 | | Anim.AnimJoints[JointNr].DefaultPos.y=TP.GetNextTokenAsFloat(); |
| 322 | | Anim.AnimJoints[JointNr].DefaultPos.z=TP.GetNextTokenAsFloat(); |
| 323 | | TP.AssertAndSkipToken(")"); |
| 324 | | TP.AssertAndSkipToken("("); |
| 325 | | Anim.AnimJoints[JointNr].DefaultQtr.x=TP.GetNextTokenAsFloat(); |
| 326 | | Anim.AnimJoints[JointNr].DefaultQtr.y=TP.GetNextTokenAsFloat(); |
| 327 | | Anim.AnimJoints[JointNr].DefaultQtr.z=TP.GetNextTokenAsFloat(); |
| 328 | | TP.AssertAndSkipToken(")"); |
| 329 | | Anim.AnimJoints[JointNr].DefaultScale=Vector3fT(1.0f, 1.0f, 1.0f); |
| 330 | | } |
| 331 | | |
| 332 | | TP.AssertAndSkipToken("}"); |
| 333 | | } |
| 334 | | else if (Token=="frame") |
| 335 | | { |
| 336 | | const unsigned long FrameNr=TP.GetNextTokenAsInt(); |
| 337 | | |
| 338 | | TP.AssertAndSkipToken("{"); |
| 339 | | |
| 340 | | for (unsigned long ComponentNr=0; ComponentNr<Anim.Frames[FrameNr].AnimData.Size(); ComponentNr++) |
| 341 | | Anim.Frames[FrameNr].AnimData[ComponentNr]=TP.GetNextTokenAsFloat(); |
| 342 | | |
| 343 | | TP.AssertAndSkipToken("}"); |
| 344 | | } |
| 345 | | else |
| 346 | | { |
| 347 | | // Unknown token! |
| 348 | | // If the next token is a block start, skip the block. |
| 349 | | // Otherwise it was something else that we just throw away. |
| 350 | | printf("Unknown token \"%s\", skipping...\n", Token.c_str()); |
| 351 | | if (TP.GetNextToken()=="{") TP.SkipBlock("{", "}", true); |
| 352 | | } |
| 353 | | } |
| 354 | | |
| 355 | | Anims.PushBack(Anim); |
| 356 | | } |
| 357 | | catch (const TextParserT::ParseError&) |
| 358 | | { |
| 359 | | // Loading this animation sequence failed, but as the base mesh (the md5mesh file) |
| 360 | | // loaded properly, that is not reason enough to abort loading the entire model. |
| 361 | | CafuModelT::AnimT InvalidAnim; |
| 362 | | |
| 363 | | InvalidAnim.FPS=-1.0f; // Use a negative FPS to flags this animation as invalid. |
| 364 | | Anims.PushBack(InvalidAnim); // Note that InvalidAnim.Frames.Size()==0, too. |
| 365 | | |
| 366 | | printf("WARNING: Loading animation sequence file %s failed just before input byte %lu!\n", ComponentFiles[FileNr].c_str(), TP.GetReadPosByte()); |
| | 233 | ImporterMd5AnimT Importer(ComponentFiles[FileNr]); |
| | 234 | |
| | 235 | Anims.PushBack(Importer.Import(Joints, Meshes)); |
| | 251 | |
| | 252 | |
| | 253 | ImporterMd5AnimT::ImporterMd5AnimT(const std::string& FileName) |
| | 254 | : AnimImporterT(FileName) |
| | 255 | { |
| | 256 | } |
| | 257 | |
| | 258 | |
| | 259 | ArrayT<CafuModelT::AnimT> ImporterMd5AnimT::Import(const ArrayT<CafuModelT::JointT>& Joints, const ArrayT<CafuModelT::MeshT>& Meshes) |
| | 260 | { |
| | 261 | TextParserT TP(m_FileName.c_str()); |
| | 262 | |
| | 263 | try |
| | 264 | { |
| | 265 | CafuModelT::AnimT Anim; |
| | 266 | |
| | 267 | Anim.Name="Anim"; |
| | 268 | Anim.FPS =24.0f; |
| | 269 | Anim.Next=-1; |
| | 270 | |
| | 271 | bool GotHierarchy=false; |
| | 272 | bool GotBounds =false; |
| | 273 | bool GotBaseframe=false; |
| | 274 | ArrayT<bool> GotFrames; |
| | 275 | |
| | 276 | while (!TP.IsAtEOF()) |
| | 277 | { |
| | 278 | const std::string Token=TP.GetNextToken(); |
| | 279 | |
| | 280 | if (Token=="MD5Version" ) { if (TP.GetNextToken()!="10") throw ModelLoaderT::LoadErrorT("MD5Version is not 10."); } |
| | 281 | else if (Token=="commandline") TP.GetNextToken(); // Ignore the command line. |
| | 282 | else if (Token=="frameRate" ) Anim.FPS=TP.GetNextTokenAsFloat(); |
| | 283 | else if (Token=="numFrames" ) |
| | 284 | { |
| | 285 | // Be stricter with numFrames than with some num* variables in the md5mesh file. |
| | 286 | // This is required because for example there must be as many bounding boxes as frames. |
| | 287 | if (Anim.Frames.Size()>0) throw ModelLoaderT::LoadErrorT("Anim.Frames.Size() > 0"); |
| | 288 | |
| | 289 | Anim.Frames.PushBackEmpty(TP.GetNextTokenAsInt()); |
| | 290 | |
| | 291 | for (unsigned long FrameNr=0; FrameNr<Anim.Frames.Size(); FrameNr++) |
| | 292 | GotFrames.PushBack(false); |
| | 293 | } |
| | 294 | else if (Token=="numJoints") |
| | 295 | { |
| | 296 | // The numJoints here MUST match the numJoints in the md5mesh file! |
| | 297 | const unsigned long numJoints=TP.GetNextTokenAsInt(); |
| | 298 | |
| | 299 | if (numJoints!=Joints.Size()) { printf("%lu joints in md5anim file, %lu joints in the model.\n", numJoints, Joints.Size()); throw ModelLoaderT::LoadErrorT("The number of joints in the md5anim file does not match number of bones in the model."); } |
| | 300 | if (Anim.AnimJoints.Size()>0) { printf("Anim.AnimJoints.Size()==%lu\n", Anim.AnimJoints.Size()); throw ModelLoaderT::LoadErrorT("Anim.AnimJoints.Size() > 0"); } |
| | 301 | |
| | 302 | Anim.AnimJoints.PushBackEmpty(numJoints); |
| | 303 | } |
| | 304 | else if (Token=="numAnimatedComponents") |
| | 305 | { |
| | 306 | const unsigned long numAnimatedComponents=TP.GetNextTokenAsInt(); |
| | 307 | |
| | 308 | // This is the number of components that is animated in the frames (and thus so many values are stored with each frame). |
| | 309 | // Therefore, the number of frames must have been specified already, so we can allocate all the memory here. |
| | 310 | if (Anim.Frames.Size()==0) throw ModelLoaderT::LoadErrorT("Anim.Frames.Size() == 0"); |
| | 311 | |
| | 312 | for (unsigned long FrameNr=0; FrameNr<Anim.Frames.Size(); FrameNr++) |
| | 313 | Anim.Frames[FrameNr].AnimData.PushBackEmpty(numAnimatedComponents); |
| | 314 | } |
| | 315 | else if (Token=="hierarchy") |
| | 316 | { |
| | 317 | TP.AssertAndSkipToken("{"); |
| | 318 | |
| | 319 | for (unsigned long JointNr=0; JointNr<Anim.AnimJoints.Size(); JointNr++) |
| | 320 | { |
| | 321 | // Make sure that the name and parent are identical with the joint from the md5mesh file. |
| | 322 | if (Joints[JointNr].Name !=TP.GetNextToken()) throw ModelLoaderT::LoadErrorT("Mismatching joint name."); |
| | 323 | if (Joints[JointNr].Parent!=TP.GetNextTokenAsInt()) throw ModelLoaderT::LoadErrorT("Mismatching joint parent."); |
| | 324 | |
| | 325 | Anim.AnimJoints[JointNr].Flags =TP.GetNextTokenAsInt(); |
| | 326 | Anim.AnimJoints[JointNr].FirstDataIdx=TP.GetNextTokenAsInt(); |
| | 327 | } |
| | 328 | |
| | 329 | TP.AssertAndSkipToken("}"); |
| | 330 | GotHierarchy=true; |
| | 331 | } |
| | 332 | else if (Token=="bounds") |
| | 333 | { |
| | 334 | TP.AssertAndSkipToken("{"); |
| | 335 | |
| | 336 | for (unsigned long FrameNr=0; FrameNr<Anim.Frames.Size(); FrameNr++) |
| | 337 | { |
| | 338 | TP.AssertAndSkipToken("("); |
| | 339 | Anim.Frames[FrameNr].BB.Min.x=TP.GetNextTokenAsFloat(); |
| | 340 | Anim.Frames[FrameNr].BB.Min.y=TP.GetNextTokenAsFloat(); |
| | 341 | Anim.Frames[FrameNr].BB.Min.z=TP.GetNextTokenAsFloat(); |
| | 342 | TP.AssertAndSkipToken(")"); |
| | 343 | TP.AssertAndSkipToken("("); |
| | 344 | Anim.Frames[FrameNr].BB.Max.x=TP.GetNextTokenAsFloat(); |
| | 345 | Anim.Frames[FrameNr].BB.Max.y=TP.GetNextTokenAsFloat(); |
| | 346 | Anim.Frames[FrameNr].BB.Max.z=TP.GetNextTokenAsFloat(); |
| | 347 | TP.AssertAndSkipToken(")"); |
| | 348 | } |
| | 349 | |
| | 350 | TP.AssertAndSkipToken("}"); |
| | 351 | GotBounds=true; |
| | 352 | } |
| | 353 | else if (Token=="baseframe") |
| | 354 | { |
| | 355 | TP.AssertAndSkipToken("{"); |
| | 356 | |
| | 357 | for (unsigned long JointNr=0; JointNr<Anim.AnimJoints.Size(); JointNr++) |
| | 358 | { |
| | 359 | TP.AssertAndSkipToken("("); |
| | 360 | Anim.AnimJoints[JointNr].DefaultPos.x=TP.GetNextTokenAsFloat(); |
| | 361 | Anim.AnimJoints[JointNr].DefaultPos.y=TP.GetNextTokenAsFloat(); |
| | 362 | Anim.AnimJoints[JointNr].DefaultPos.z=TP.GetNextTokenAsFloat(); |
| | 363 | TP.AssertAndSkipToken(")"); |
| | 364 | TP.AssertAndSkipToken("("); |
| | 365 | Anim.AnimJoints[JointNr].DefaultQtr.x=TP.GetNextTokenAsFloat(); |
| | 366 | Anim.AnimJoints[JointNr].DefaultQtr.y=TP.GetNextTokenAsFloat(); |
| | 367 | Anim.AnimJoints[JointNr].DefaultQtr.z=TP.GetNextTokenAsFloat(); |
| | 368 | TP.AssertAndSkipToken(")"); |
| | 369 | Anim.AnimJoints[JointNr].DefaultScale=Vector3fT(1.0f, 1.0f, 1.0f); |
| | 370 | } |
| | 371 | |
| | 372 | TP.AssertAndSkipToken("}"); |
| | 373 | GotBaseframe=true; |
| | 374 | } |
| | 375 | else if (Token=="frame") |
| | 376 | { |
| | 377 | const unsigned long FrameNr=TP.GetNextTokenAsInt(); |
| | 378 | |
| | 379 | TP.AssertAndSkipToken("{"); |
| | 380 | |
| | 381 | for (unsigned long ComponentNr=0; ComponentNr<Anim.Frames[FrameNr].AnimData.Size(); ComponentNr++) |
| | 382 | Anim.Frames[FrameNr].AnimData[ComponentNr]=TP.GetNextTokenAsFloat(); |
| | 383 | |
| | 384 | TP.AssertAndSkipToken("}"); |
| | 385 | GotFrames[FrameNr]=true; |
| | 386 | } |
| | 387 | else |
| | 388 | { |
| | 389 | // Unknown token! |
| | 390 | // If the next token is a block start, skip the block. |
| | 391 | // Otherwise it was something else that we just throw away. |
| | 392 | printf("Unknown token \"%s\", skipping...\n", Token.c_str()); |
| | 393 | if (TP.GetNextToken()=="{") TP.SkipBlock("{", "}", true); |
| | 394 | } |
| | 395 | } |
| | 396 | |
| | 397 | |
| | 398 | // Make sure that the imported animation sequence is valid. |
| | 399 | if (Anim.AnimJoints.Size()!=Joints.Size()) throw ModelLoaderT::LoadErrorT("The number of joints in the md5anim file does not match number of bones in the model."); |
| | 400 | if (!GotHierarchy) throw ModelLoaderT::LoadErrorT("There seems to be no joints hierarchy defined in this file."); |
| | 401 | if (!GotBounds) throw ModelLoaderT::LoadErrorT("There seem to be no bounds in this file."); |
| | 402 | if (!GotBaseframe) throw ModelLoaderT::LoadErrorT("There seems to be no baseframe data in this file."); |
| | 403 | if (Anim.Frames.Size()==0) throw ModelLoaderT::LoadErrorT("The animation sequence in this file has no frames."); |
| | 404 | |
| | 405 | for (unsigned long FrameNr=0; FrameNr<Anim.Frames.Size(); FrameNr++) |
| | 406 | if (!GotFrames[FrameNr]) |
| | 407 | throw ModelLoaderT::LoadErrorT("Some frames in the sequence are not defined."); |
| | 408 | |
| | 409 | |
| | 410 | // The animation seems ok, return it. |
| | 411 | ArrayT<CafuModelT::AnimT> Anims; |
| | 412 | Anims.PushBack(Anim); |
| | 413 | return Anims; |
| | 414 | } |
| | 415 | catch (const TextParserT::ParseError&) |
| | 416 | { |
| | 417 | printf("WARNING: Loading animation sequence file %s failed just before input byte %lu!\n", m_FileName.c_str(), TP.GetReadPosByte()); |
| | 418 | |
| | 419 | throw ModelLoaderT::LoadErrorT("Could not parse the file."); |
| | 420 | } |
| | 421 | } |