1 /* 2 Copyright (c) 2017-2022 Timur Gafarov 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 Permission is hereby granted, free of charge, to any person or organization 6 obtaining a copy of the software and accompanying documentation covered by 7 this license (the "Software") to use, reproduce, display, distribute, 8 execute, and transmit the Software, and to prepare derivative works of the 9 Software, and to permit third-parties to whom the Software is furnished to 10 do so, all subject to the following: 11 12 The copyright notices in the Software and this entire statement, including 13 the above license grant, this restriction and the following disclaimer, 14 must be included in all copies of the Software, in whole or in part, and 15 all derivative works of the Software, unless such copies or derivative 16 works are solely in the form of machine-executable object code generated by 17 a source language processor. 18 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 DEALINGS IN THE SOFTWARE. 26 */ 27 28 module dagon.ext.iqm; 29 30 import std.stdio; 31 import std.math; 32 import std.string; 33 import std.path; 34 35 import dlib.core.memory; 36 import dlib.core.ownership; 37 import dlib.core.stream; 38 import dlib.container.array; 39 import dlib.container.dict; 40 import dlib.filesystem.filesystem; 41 import dlib.filesystem.stdfs; 42 import dlib.math.vector; 43 import dlib.math.matrix; 44 import dlib.math.quaternion; 45 import dlib.math.interpolation; 46 47 import dagon.core.bindings; 48 import dagon.graphics.drawable; 49 import dagon.graphics.texture; 50 import dagon.graphics.mesh; 51 import dagon.resource.asset; 52 import dagon.resource.texture; 53 import dagon.resource.scene; 54 import dagon.ext.serialization; 55 56 interface AnimatedModel 57 { 58 void calcBindPose(AnimationFrameData* data); 59 void calcFrame(uint f1, uint f2, float t, AnimationFrameData* data); 60 void blendFrame(uint f1, uint f2, float t, AnimationFrameData* data, float blendFactor); 61 Vector3f[] getVertices(); 62 Vector3f[] getNormals(); 63 Vector2f[] getTexcoords(); 64 uint[3][] getTriangles(); 65 AnimationFacegroup[] getFacegroups(); 66 size_t numBones(); 67 bool getAnimation(string name, AnimationData* data); 68 uint numAnimationFrames(); 69 } 70 71 struct AnimationFacegroup 72 { 73 size_t firstTriangle; 74 size_t numTriangles; 75 Texture texture; 76 string textureName; 77 } 78 79 struct AnimationData 80 { 81 uint firstFrame; 82 uint numFrames; 83 float framerate; 84 } 85 86 struct AnimationFrameData 87 { 88 Vector3f[] vertices; 89 Vector3f[] normals; 90 Vector2f[] texcoords; 91 uint[3][] tris; 92 Matrix4x4f[] frame; 93 } 94 95 struct ActorState 96 { 97 uint currentFrame = 0; 98 uint nextFrame = 1; 99 float t = 0.0f; 100 } 101 102 class Actor: Owner, Drawable 103 { 104 AnimatedModel model; 105 AnimationFrameData frameData; 106 AnimationData animation; 107 AnimationData nextAnimation; 108 bool hasNextAnimation = false; 109 float blendFactor = 0.0f; 110 ActorState state; 111 ActorState nextState; 112 bool playing = false; 113 float defaultFramerate = 24.0f; 114 float speed = 1.0f; 115 float blendSpeed = 8.0f; 116 117 GLuint vao = 0; 118 GLuint vbo = 0; 119 GLuint nbo = 0; 120 GLuint tbo = 0; 121 GLuint eao = 0; 122 123 this(AnimatedModel m, Owner owner) 124 { 125 super(owner); 126 model = m; 127 128 if (model.getVertices().length) 129 frameData.vertices = New!(Vector3f[])(model.getVertices().length); 130 if (model.getNormals().length) 131 frameData.normals = New!(Vector3f[])(model.getNormals().length); 132 if (model.getTexcoords().length) 133 frameData.texcoords = model.getTexcoords(); // no need to make a copy, texcoords don't change frame to frame 134 if (model.getTriangles().length) 135 frameData.tris = model.getTriangles(); // no need to make a copy, indices don't change frame to frame 136 if (model.numBones()) 137 frameData.frame = New!(Matrix4x4f[])(model.numBones()); 138 139 model.calcBindPose(&frameData); 140 141 switchToFullSequence(); 142 143 foreach(ref v; frameData.vertices) 144 v = Vector3f(0, 0, 0); 145 146 foreach(ref n; frameData.normals) 147 n = Vector3f(0, 0, 0); 148 149 glGenBuffers(1, &vbo); 150 glBindBuffer(GL_ARRAY_BUFFER, vbo); 151 glBufferData(GL_ARRAY_BUFFER, frameData.vertices.length * float.sizeof * 3, frameData.vertices.ptr, GL_DYNAMIC_DRAW); 152 153 glGenBuffers(1, &nbo); 154 glBindBuffer(GL_ARRAY_BUFFER, nbo); 155 glBufferData(GL_ARRAY_BUFFER, frameData.normals.length * float.sizeof * 3, frameData.normals.ptr, GL_DYNAMIC_DRAW); 156 157 glGenBuffers(1, &tbo); 158 glBindBuffer(GL_ARRAY_BUFFER, tbo); 159 glBufferData(GL_ARRAY_BUFFER, frameData.texcoords.length * float.sizeof * 2, frameData.texcoords.ptr, GL_DYNAMIC_DRAW); 160 161 glGenBuffers(1, &eao); 162 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eao); 163 glBufferData(GL_ELEMENT_ARRAY_BUFFER, frameData.tris.length * uint.sizeof * 3, frameData.tris.ptr, GL_DYNAMIC_DRAW); 164 165 glGenVertexArrays(1, &vao); 166 glBindVertexArray(vao); 167 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eao); 168 169 glEnableVertexAttribArray(VertexAttrib.Vertices); 170 glBindBuffer(GL_ARRAY_BUFFER, vbo); 171 glVertexAttribPointer(VertexAttrib.Vertices, 3, GL_FLOAT, GL_FALSE, 0, null); 172 173 glEnableVertexAttribArray(VertexAttrib.Normals); 174 glBindBuffer(GL_ARRAY_BUFFER, nbo); 175 glVertexAttribPointer(VertexAttrib.Normals, 3, GL_FLOAT, GL_FALSE, 0, null); 176 177 glEnableVertexAttribArray(VertexAttrib.Texcoords); 178 glBindBuffer(GL_ARRAY_BUFFER, tbo); 179 glVertexAttribPointer(VertexAttrib.Texcoords, 2, GL_FLOAT, GL_FALSE, 0, null); 180 181 glBindBuffer(GL_ARRAY_BUFFER, 0); 182 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 183 184 glBindVertexArray(0); 185 } 186 187 ~this() 188 { 189 if (frameData.vertices.length) Delete(frameData.vertices); 190 if (frameData.normals.length) Delete(frameData.normals); 191 if (frameData.frame.length) Delete(frameData.frame); 192 } 193 194 void switchToBindPose() 195 { 196 model.calcBindPose(&frameData); 197 playing = false; 198 } 199 200 void switchToAnimation(string name) 201 { 202 model.getAnimation(name, &animation); 203 state.currentFrame = animation.firstFrame; 204 state.nextFrame = state.currentFrame + 1; 205 state.t = 0.0f; 206 } 207 208 void switchToAnimationSmooth(string name, float smooth) 209 { 210 model.getAnimation(name, &nextAnimation); 211 hasNextAnimation = true; 212 nextState.currentFrame = nextAnimation.firstFrame; 213 nextState.nextFrame = nextState.currentFrame + 1; 214 nextState.t = 0.0f; 215 blendSpeed = smooth; 216 } 217 218 void switchToSequence(uint startFrame, uint endFrame) 219 { 220 uint numFrames = endFrame - startFrame; 221 if (animation.firstFrame != startFrame && animation.numFrames != numFrames) 222 { 223 animation.firstFrame = startFrame; 224 animation.numFrames = numFrames; 225 state.currentFrame = animation.firstFrame; 226 state.nextFrame = state.currentFrame + 1; 227 state.t = 0.0f; 228 } 229 } 230 231 void switchToSequenceSmooth(uint startFrame, uint endFrame, float smooth) 232 { 233 uint numFrames = endFrame - startFrame; 234 if (animation.firstFrame != startFrame && animation.numFrames != numFrames) 235 { 236 nextAnimation.firstFrame = startFrame; 237 nextAnimation.numFrames = numFrames; 238 hasNextAnimation = true; 239 nextState.currentFrame = nextAnimation.firstFrame; 240 nextState.nextFrame = nextState.currentFrame + 1; 241 nextState.t = 0.0f; 242 blendSpeed = smooth; 243 } 244 } 245 246 void switchToFullSequence() 247 { 248 switchToAnimation(""); 249 animation.framerate = defaultFramerate; 250 state.currentFrame = animation.firstFrame; 251 state.nextFrame = state.currentFrame + 1; 252 state.t = 0.0f; 253 } 254 255 void play() 256 { 257 playing = true; 258 } 259 260 void pause() 261 { 262 playing = false; 263 } 264 265 void update(double dt) 266 { 267 if (!playing) 268 return; 269 270 model.calcFrame(state.currentFrame, state.nextFrame, state.t, &frameData); 271 272 state.t += defaultFramerate * dt * speed; //animation.framerate 273 274 if (state.t >= 1.0f) 275 { 276 state.t = 0.0f; 277 state.currentFrame++; 278 state.nextFrame++; 279 280 if (state.currentFrame == animation.firstFrame + animation.numFrames - 1) 281 { 282 state.nextFrame = animation.firstFrame; 283 } 284 else if (state.currentFrame == animation.firstFrame + animation.numFrames) 285 { 286 state.currentFrame = animation.firstFrame; 287 state.nextFrame = state.currentFrame + 1; 288 } 289 } 290 291 if (hasNextAnimation) 292 { 293 model.blendFrame(nextState.currentFrame, nextState.nextFrame, nextState.t, &frameData, blendFactor); 294 nextState.t += defaultFramerate * dt * speed; //nextAnimation.framerate 295 blendFactor += blendSpeed * dt; 296 297 if (nextState.t >= 1.0f) 298 { 299 nextState.t = 0.0f; 300 nextState.currentFrame++; 301 nextState.nextFrame++; 302 303 if (nextState.currentFrame == nextAnimation.numFrames - 1) 304 { 305 nextState.nextFrame = nextAnimation.firstFrame; 306 } 307 else if (nextState.currentFrame == nextAnimation.numFrames) 308 { 309 nextState.currentFrame = nextAnimation.firstFrame; 310 nextState.nextFrame = nextState.currentFrame + 1; 311 } 312 } 313 314 if (blendFactor >= 1.0f) 315 { 316 blendFactor = 0.0f; 317 hasNextAnimation = false; 318 animation = nextAnimation; 319 state = nextState; 320 } 321 } 322 323 glBindBuffer(GL_ARRAY_BUFFER, vbo); 324 glBufferSubData(GL_ARRAY_BUFFER, 0, frameData.vertices.length * float.sizeof * 3, frameData.vertices.ptr); 325 glBindBuffer(GL_ARRAY_BUFFER, nbo); 326 glBufferSubData(GL_ARRAY_BUFFER, 0, frameData.normals.length * float.sizeof * 3, frameData.normals.ptr); 327 glBindBuffer(GL_ARRAY_BUFFER, 0); 328 } 329 330 void render(GraphicsState* state) 331 { 332 glBindVertexArray(vao); 333 foreach(ref fg; model.getFacegroups) 334 { 335 glActiveTexture(GL_TEXTURE0); 336 if (fg.texture) 337 fg.texture.bind(); 338 glDrawElements(GL_TRIANGLES, cast(uint)(3 * fg.numTriangles), GL_UNSIGNED_INT, &frameData.tris[fg.firstTriangle]); 339 if (fg.texture) 340 fg.texture.unbind(); 341 } 342 glBindVertexArray(0); 343 } 344 } 345 346 enum IQM_VERSION = 2; 347 348 struct IQMHeader 349 { 350 ubyte[16] magic; 351 uint ver; 352 uint filesize; 353 uint flags; 354 uint numText, ofsText; 355 uint numMeshes, ofsMeshes; 356 uint numVertexArrays, numVertices, ofsVertexArrays; 357 uint numTriangles, ofsTriangles, ofsAdjacency; 358 uint numJoints, ofsJoints; 359 uint numPoses, ofsPoses; 360 uint numAnims, ofsAnims; 361 uint numFrames, numFrameChannels, ofsFrames, ofsBounds; 362 uint numComment, ofsComment; 363 uint numExtensions, ofsExtensions; 364 } 365 366 struct IQMVertexArray 367 { 368 uint type; 369 uint flags; 370 uint format; 371 uint size; 372 uint offset; 373 } 374 375 alias uint[3] IQMTriangle; 376 377 struct IQMJoint 378 { 379 uint name; 380 int parent; 381 Vector3f translation; 382 Quaternionf rotation; 383 Vector3f scaling; 384 } 385 386 struct IQMMesh 387 { 388 uint name; 389 uint material; 390 uint firstVertex, numVertices; 391 uint firstTriangle, numTriangles; 392 } 393 394 alias ubyte[4] IQMBlendIndex; 395 alias ubyte[4] IQMBlendWeight; 396 397 enum 398 { 399 IQM_POSITION = 0, 400 IQM_TEXCOORD = 1, 401 IQM_NORMAL = 2, 402 IQM_TANGENT = 3, 403 IQM_BLENDINDEXES = 4, 404 IQM_BLENDWEIGHTS = 5, 405 IQM_COLOR = 6, 406 IQM_CUSTOM = 0x10 407 } 408 409 struct IQMPose 410 { 411 int parent; 412 uint mask; 413 float[10] channelOffset; 414 float[10] channelScale; 415 } 416 417 struct IQMAnim 418 { 419 uint name; 420 uint firstFrame; 421 uint numFrames; 422 float framerate; 423 uint flags; 424 } 425 426 //version = IQMDebug; 427 428 class IQMModel: AnimatedModel 429 { 430 Array!Vector3f vertices; 431 Array!Vector3f normals; 432 Array!Vector2f texcoords; 433 434 Array!IQMBlendIndex blendIndices; 435 Array!IQMBlendWeight blendWeights; 436 437 IQMTriangle[] tris; 438 IQMVertexArray[] vas; 439 IQMMesh[] meshes; 440 AnimationFacegroup[] facegroups; 441 442 Array!IQMJoint joints; 443 444 Matrix4x4f[] baseFrame; 445 Matrix4x4f[] invBaseFrame; 446 447 Matrix4x4f[] frames; 448 449 uint numFrames; 450 451 ubyte[] textBuffer; 452 453 Dict!(IQMAnim, string) animations; 454 455 this(InputStream istrm, ReadOnlyFileSystem rofs, AssetManager mngr) 456 { 457 load(istrm, rofs, mngr); 458 } 459 460 ~this() 461 { 462 release(); 463 } 464 465 void release() 466 { 467 vertices.free(); 468 normals.free(); 469 texcoords.free(); 470 471 blendIndices.free(); 472 blendWeights.free(); 473 474 if (tris.length) Delete(tris); 475 if (vas.length) Delete(vas); 476 if (meshes.length) Delete(meshes); 477 478 joints.free(); 479 480 if (baseFrame.length) Delete(baseFrame); 481 if (invBaseFrame.length) Delete(invBaseFrame); 482 if (frames.length) Delete(frames); 483 484 if (textBuffer.length) Delete(textBuffer); 485 486 if (animations) Delete(animations); 487 488 if (facegroups) Delete(facegroups); 489 } 490 491 void load(InputStream istrm, ReadOnlyFileSystem rofs, AssetManager mngr) 492 { 493 // Header part 494 IQMHeader hdr = istrm.read!(IQMHeader, true); 495 496 version(IQMDebug) 497 { 498 writefln("hdr.magic: %s", cast(string)hdr.magic); 499 writefln("hdr.ver: %s", hdr.ver); 500 } 501 assert(cast(string)hdr.magic == "INTERQUAKEMODEL\0"); 502 assert(hdr.ver == IQM_VERSION); 503 504 version(IQMDebug) 505 { 506 writefln("hdr.numText: %s", hdr.numText); 507 writefln("hdr.ofsText: %s", hdr.ofsText); 508 } 509 510 istrm.setPosition(hdr.ofsText); 511 textBuffer = New!(ubyte[])(hdr.numText); 512 istrm.fillArray(textBuffer); 513 version(IQMDebug) 514 writefln("text:\n%s", cast(string)textBuffer); 515 516 // Vertex data part 517 version(IQMDebug) 518 { 519 writefln("hdr.numVertexArrays: %s", hdr.numVertexArrays); 520 writefln("hdr.ofsVertexArrays: %s", hdr.ofsVertexArrays); 521 } 522 523 vas = New!(IQMVertexArray[])(hdr.numVertexArrays); 524 istrm.setPosition(hdr.ofsVertexArrays); 525 foreach(i; 0..hdr.numVertexArrays) 526 { 527 vas[i] = istrm.read!(IQMVertexArray, true); 528 } 529 530 // FIXME: 531 //Delete(vas); 532 533 foreach(i, va; vas) 534 { 535 version(IQMDebug) 536 { 537 writefln("Vertex array %s:", i); 538 writefln("va.type: %s", va.type); 539 writefln("va.flags: %s", va.flags); 540 writefln("va.format: %s", va.format); 541 writefln("va.size: %s", va.size); 542 writefln("va.offset: %s", va.offset); 543 writeln("---------------"); 544 } 545 546 if (va.type == IQM_POSITION) 547 { 548 assert(va.size == 3); 549 // TODO: format asserion 550 auto verts = New!(Vector3f[])(hdr.numVertices); 551 istrm.setPosition(va.offset); 552 istrm.fillArray(verts); 553 vertices.append(verts); 554 Delete(verts); 555 } 556 else if (va.type == IQM_NORMAL) 557 { 558 assert(va.size == 3); 559 // TODO: format asserion 560 auto norms = New!(Vector3f[])(hdr.numVertices); 561 istrm.setPosition(va.offset); 562 istrm.fillArray(norms); 563 normals.append(norms); 564 Delete(norms); 565 } 566 else if (va.type == IQM_TEXCOORD) 567 { 568 assert(va.size == 2); 569 // TODO: format asserion 570 auto texs = New!(Vector2f[])(hdr.numVertices); 571 istrm.setPosition(va.offset); 572 istrm.fillArray(texs); 573 texcoords.append(texs); 574 Delete(texs); 575 } 576 /* TODO: IQM_TANGENT */ 577 else if (va.type == IQM_BLENDINDEXES) 578 { 579 assert(va.size == 4); 580 // TODO: format asserion 581 auto bi = New!(IQMBlendIndex[])(hdr.numVertices); 582 istrm.setPosition(va.offset); 583 istrm.fillArray(bi); 584 blendIndices.append(bi); 585 Delete(bi); 586 } 587 else if (va.type == IQM_BLENDWEIGHTS) 588 { 589 assert(va.size == 4); 590 // TODO: format asserion 591 auto bw = New!(IQMBlendWeight[])(hdr.numVertices); 592 istrm.setPosition(va.offset); 593 istrm.fillArray(bw); 594 blendWeights.append(bw); 595 Delete(bw); 596 } 597 } 598 599 version(IQMDebug) 600 { 601 writefln("numVertices: %s", vertices.length); 602 writefln("numNormals: %s", normals.length); 603 writefln("numTexcoords: %s", texcoords.length); 604 605 writefln("hdr.numTriangles: %s", hdr.numTriangles); 606 writefln("hdr.ofsTriangles: %s", hdr.ofsTriangles); 607 } 608 609 tris = New!(IQMTriangle[])(hdr.numTriangles); 610 istrm.setPosition(hdr.ofsTriangles); 611 foreach(i; 0..hdr.numTriangles) 612 { 613 tris[i] = istrm.read!IQMTriangle; 614 uint tmp = tris[i][0]; 615 tris[i][0] = tris[i][2]; 616 tris[i][2] = tmp; 617 } 618 619 version(IQMDebug) 620 writefln("hdr.ofsAdjacency: %s", hdr.ofsAdjacency); 621 622 // Skeleton part 623 version(IQMDebug) 624 { 625 writefln("hdr.numJoints: %s", hdr.numJoints); 626 writefln("hdr.ofsJoints: %s", hdr.ofsJoints); 627 } 628 629 baseFrame = New!(Matrix4x4f[])(hdr.numJoints); 630 invBaseFrame = New!(Matrix4x4f[])(hdr.numJoints); 631 istrm.setPosition(hdr.ofsJoints); 632 foreach(i; 0..hdr.numJoints) 633 { 634 IQMJoint j = istrm.read!(IQMJoint, true); 635 636 j.rotation.normalize(); 637 baseFrame[i] = transformationMatrix(j.rotation, j.translation, j.scaling); 638 invBaseFrame[i] = baseFrame[i].inverse; 639 640 if (j.parent >= 0) 641 { 642 baseFrame[i] = baseFrame[j.parent] * baseFrame[i]; 643 invBaseFrame[i] = invBaseFrame[i] * invBaseFrame[j.parent]; 644 } 645 646 assert(validMatrix(baseFrame[i])); 647 assert(validMatrix(invBaseFrame[i])); 648 649 assert(baseFrame[i].isAffine); 650 assert(invBaseFrame[i].isAffine); 651 652 joints.append(j); 653 } 654 655 // Meshes part 656 version(IQMDebug) 657 { 658 writefln("hdr.numMeshes: %s", hdr.numMeshes); 659 writefln("hdr.ofsMeshes: %s", hdr.ofsMeshes); 660 } 661 meshes = New!(IQMMesh[])(hdr.numMeshes); 662 663 facegroups = New!(AnimationFacegroup[])(meshes.length); 664 665 istrm.setPosition(hdr.ofsMeshes); 666 foreach(i; 0..hdr.numMeshes) 667 { 668 meshes[i] = istrm.read!(IQMMesh, true); 669 670 // Load texture 671 uint matIndex = meshes[i].material; 672 version(IQMDebug) 673 writefln("matIndex: %s", matIndex); 674 675 if (matIndex > 0) 676 { 677 char* texFilenamePtr = cast(char*)&textBuffer[matIndex]; 678 string texFilename = cast(string)fromStringz(texFilenamePtr); 679 version(IQMDebug) 680 writefln("material: %s", texFilename); 681 682 facegroups[i].firstTriangle = meshes[i].firstTriangle; 683 facegroups[i].numTriangles = meshes[i].numTriangles; 684 facegroups[i].textureName = texFilename; 685 686 if (!mngr.assetExists(texFilename)) 687 { 688 auto texAsset = New!TextureAsset(mngr); 689 mngr.addAsset(texAsset, texFilename); 690 texAsset.threadSafePartLoaded = mngr.loadAssetThreadSafePart(texAsset, texFilename); 691 facegroups[i].texture = texAsset.texture; 692 } 693 else 694 facegroups[i].texture = (cast(TextureAsset)mngr.getAsset(texFilename)).texture; 695 } 696 } 697 698 // Animation part 699 700 // Number of poses should be the same as bindpose joints 701 version(IQMDebug) 702 writefln("hdr.numPoses: %s", hdr.numPoses); 703 assert(hdr.numPoses == hdr.numJoints); 704 705 version(IQMDebug) 706 { 707 writefln("hdr.numFrames: %s", hdr.numFrames); 708 writefln("hdr.numFrameChannels: %s", hdr.numFrameChannels); 709 } 710 711 // Read poses 712 istrm.setPosition(hdr.ofsPoses); 713 IQMPose[] poses = New!(IQMPose[])(hdr.numPoses); 714 foreach(i; 0..hdr.numPoses) 715 { 716 poses[i] = istrm.read!(IQMPose, true); 717 } 718 719 // Read frames 720 numFrames = hdr.numFrames; 721 frames = New!(Matrix4x4f[])(hdr.numFrames * hdr.numPoses); 722 istrm.setPosition(hdr.ofsFrames); 723 uint fi = 0; 724 foreach(i; 0..hdr.numFrames) 725 foreach(j; 0..hdr.numPoses) 726 { 727 auto p = &poses[j]; 728 729 Vector3f trans, scale; 730 Quaternionf rot; 731 trans.x = p.channelOffset[0]; if (p.mask & 0x01) trans.x += istrm.read!(ushort, true) * p.channelScale[0]; 732 trans.y = p.channelOffset[1]; if (p.mask & 0x02) trans.y += istrm.read!(ushort, true) * p.channelScale[1]; 733 trans.z = p.channelOffset[2]; if (p.mask & 0x04) trans.z += istrm.read!(ushort, true) * p.channelScale[2]; 734 rot.x = p.channelOffset[3]; if(p.mask&0x08) rot.x += istrm.read!(ushort, true) * p.channelScale[3]; 735 rot.y = p.channelOffset[4]; if(p.mask&0x10) rot.y += istrm.read!(ushort, true) * p.channelScale[4]; 736 rot.z = p.channelOffset[5]; if(p.mask&0x20) rot.z += istrm.read!(ushort, true) * p.channelScale[5]; 737 rot.w = p.channelOffset[6]; if(p.mask&0x40) rot.w += istrm.read!(ushort, true) * p.channelScale[6]; 738 scale.x = p.channelOffset[7]; if(p.mask&0x80) scale.x += istrm.read!(ushort, true) * p.channelScale[7]; 739 scale.y = p.channelOffset[8]; if(p.mask&0x100) scale.y += istrm.read!(ushort, true) * p.channelScale[8]; 740 scale.z = p.channelOffset[9]; if(p.mask&0x200) scale.z += istrm.read!(ushort, true) * p.channelScale[9]; 741 742 rot.normalize(); 743 Matrix4x4f m = transformationMatrix(rot, trans, scale); 744 assert(validMatrix(m)); 745 746 // Concatenate each pose with the inverse base pose to avoid doing this at animation time. 747 // If the joint has a parent, then it needs to be pre-concatenated with its parent's base pose. 748 if (p.parent >= 0) 749 frames[i * hdr.numPoses + j] = 750 baseFrame[p.parent] * m * invBaseFrame[j]; 751 else 752 frames[i * hdr.numPoses + j] = m * invBaseFrame[j]; 753 } 754 755 // Read animations 756 animations = New!(Dict!(IQMAnim, string)); 757 istrm.setPosition(hdr.ofsAnims); 758 foreach(i; 0..hdr.numAnims) 759 { 760 IQMAnim anim = istrm.read!(IQMAnim, true); 761 762 char* namePtr = cast(char*)&textBuffer[anim.name]; 763 string name = cast(string)fromStringz(namePtr); 764 version(IQMDebug) 765 { 766 writefln("anim.name: %s", name); 767 } 768 769 animations[name] = anim; 770 771 version(IQMDebug) 772 { 773 writefln("anim.firstFrame: %s", anim.firstFrame); 774 writefln("anim.numFrames: %s", anim.numFrames); 775 } 776 } 777 778 if (poses.length) Delete(poses); 779 } 780 781 Vector3f[] getVertices() 782 { 783 return vertices.data; 784 } 785 786 Vector3f[] getNormals() 787 { 788 return normals.data; 789 } 790 791 Vector2f[] getTexcoords() 792 { 793 return texcoords.data; 794 } 795 796 uint[3][] getTriangles() 797 { 798 return tris; 799 } 800 801 size_t numBones() 802 { 803 return joints.length; 804 } 805 806 AnimationFacegroup[] getFacegroups() 807 { 808 return facegroups; 809 } 810 811 uint numAnimationFrames() 812 { 813 return numFrames; 814 } 815 816 void calcBindPose(AnimationFrameData* data) 817 { 818 foreach(i, ref j; joints) 819 { 820 data.frame[i] = baseFrame[i] * invBaseFrame[i]; 821 } 822 823 foreach(i, v; vertices) 824 { 825 auto bi = blendIndices[i]; 826 auto bw = blendWeights[i]; 827 828 float w = (cast(float)bw[0])/255.0f; 829 Matrix4x4f mat = multScalarAffine(data.frame[bi[0]], w); 830 831 for (uint j = 1; j < 4 && bw[j] > 0.0; j++) 832 { 833 w = (cast(float)bw[j])/255.0f; 834 auto tmp = multScalarAffine(data.frame[bi[j]], w); 835 mat = addMatrixAffine(mat, tmp); 836 } 837 838 assert(validMatrix(mat)); 839 assert(mat.isAffine); 840 841 data.vertices[i] = vertices[i] * mat; 842 data.normals[i] = normals[i] * matrix4x4to3x3(mat); 843 data.normals[i].normalize(); 844 } 845 } 846 847 void calcFrame( 848 uint f1, 849 uint f2, 850 float t, 851 AnimationFrameData* data) 852 { 853 Matrix4x4f* mat1 = &frames[f1 * joints.length]; 854 Matrix4x4f* mat2 = &frames[f2 * joints.length]; 855 856 // Interpolate between two frames 857 foreach(i, ref j; joints) 858 { 859 Matrix4x4f mat = mat1[i] * (1.0f - t) + mat2[i] * t; 860 if (j.parent >= 0) 861 data.frame[i] = data.frame[j.parent] * mat; 862 else 863 data.frame[i] = mat; 864 } 865 866 // Update vertex data 867 foreach(i, v; vertices) 868 { 869 auto bi = blendIndices[i]; 870 auto bw = blendWeights[i]; 871 872 float w = (cast(float)bw[0])/255.0f; 873 Matrix4x4f mat = multScalarAffine(data.frame[bi[0]], w); 874 875 for (uint j = 1; j < 4 && bw[j] > 0.0; j++) 876 { 877 w = (cast(float)bw[j])/255.0f; 878 auto tmp = multScalarAffine(data.frame[bi[j]], w); 879 mat = addMatrixAffine(mat, tmp); 880 } 881 882 assert(validMatrix(mat)); 883 assert(mat.isAffine); 884 885 data.vertices[i] = vertices[i] * mat; 886 data.normals[i] = normals[i] * matrix4x4to3x3(mat); 887 data.normals[i].normalize(); 888 } 889 } 890 891 void blendFrame( 892 uint f1, 893 uint f2, 894 float t, 895 AnimationFrameData* data, 896 float blendFactor) 897 { 898 Matrix4x4f* mat1 = &frames[f1 * joints.length]; 899 Matrix4x4f* mat2 = &frames[f2 * joints.length]; 900 901 // Interpolate between two frames 902 foreach(i, ref j; joints) 903 { 904 Matrix4x4f mat = mat1[i] * (1.0f - t) + mat2[i] * t; 905 if (j.parent >= 0) 906 data.frame[i] = data.frame[j.parent] * mat; 907 else 908 data.frame[i] = mat; 909 } 910 911 // Update vertex data 912 foreach(i, v; vertices) 913 { 914 auto bi = blendIndices[i]; 915 auto bw = blendWeights[i]; 916 917 float w = (cast(float)bw[0])/255.0f; 918 Matrix4x4f mat = multScalarAffine(data.frame[bi[0]], w); 919 920 for (uint j = 1; j < 4 && bw[j] > 0.0; j++) 921 { 922 w = (cast(float)bw[j])/255.0f; 923 auto tmp = multScalarAffine(data.frame[bi[j]], w); 924 mat = addMatrixAffine(mat, tmp); 925 } 926 927 assert(validMatrix(mat)); 928 assert(mat.isAffine); 929 930 data.vertices[i] = lerp(data.vertices[i], vertices[i] * mat, blendFactor); 931 data.normals[i] = lerp(data.normals[i], normals[i] * matrix4x4to3x3(mat), blendFactor); 932 //data.normals[i].normalize(); 933 } 934 } 935 936 bool getAnimation(string name, AnimationData* data) 937 { 938 if (!name.length) 939 { 940 data.firstFrame = 0; 941 data.numFrames = numFrames; 942 return true; 943 } 944 945 if (!(name in animations)) 946 return false; 947 948 auto anim = animations[name]; 949 data.firstFrame = anim.firstFrame; 950 data.numFrames = anim.numFrames; 951 data.framerate = anim.framerate; 952 return true; 953 } 954 } 955 956 class IQMAsset: Asset 957 { 958 IQMModel model; 959 960 this(Owner o) 961 { 962 super(o); 963 } 964 965 ~this() 966 { 967 if (model) 968 Delete(model); 969 } 970 971 override bool loadThreadSafePart(string filename, InputStream istrm, ReadOnlyFileSystem fs, AssetManager mngr) 972 { 973 model = New!IQMModel(istrm, fs, mngr); 974 return true; 975 } 976 977 override bool loadThreadUnsafePart() 978 { 979 return true; 980 } 981 982 override void release() 983 { 984 if (model) 985 model.release(); 986 } 987 } 988 989 IQMAsset addIQMAsset(Scene scene, string filename, bool preload = false) 990 { 991 IQMAsset iqmAsset; 992 if (scene.assetManager.assetExists(filename)) 993 iqmAsset = cast(IQMAsset)scene.assetManager.getAsset(filename); 994 else 995 { 996 iqmAsset = New!IQMAsset(scene.assetManager); 997 scene.addAsset(iqmAsset, filename, preload); 998 } 999 return iqmAsset; 1000 } 1001 1002 bool fileExists(ReadOnlyFileSystem rofs, string filename) 1003 { 1004 FileStat stat; 1005 return rofs.stat(filename, stat); 1006 } 1007 1008 Matrix4x4f transformationMatrix(Quaternionf r, Vector3f t, Vector3f s) 1009 { 1010 Matrix4x4f res = Matrix4x4f.identity; 1011 Matrix3x3f rm = r.toMatrix3x3; 1012 res.a11 = rm.a11 * s.x; res.a12 = rm.a12 * s.x; res.a13 = rm.a13 * s.x; 1013 res.a21 = rm.a21 * s.y; res.a22 = rm.a22 * s.y; res.a23 = rm.a23 * s.y; 1014 res.a31 = rm.a31 * s.z; res.a32 = rm.a32 * s.z; res.a33 = rm.a33 * s.z; 1015 res.a14 = t.x; 1016 res.a24 = t.y; 1017 res.a34 = t.z; 1018 return res; 1019 } 1020 1021 Matrix4x4f multScalarAffine(Matrix4x4f m, float s) 1022 { 1023 Matrix4x4f res = m; 1024 res.a11 *= s; res.a12 *= s; res.a13 *= s; 1025 res.a21 *= s; res.a22 *= s; res.a23 *= s; 1026 res.a31 *= s; res.a32 *= s; res.a33 *= s; 1027 res.a14 *= s; 1028 res.a24 *= s; 1029 res.a34 *= s; 1030 return res; 1031 } 1032 1033 Matrix4x4f addMatrixAffine(Matrix4x4f m1, Matrix4x4f m2) 1034 { 1035 Matrix4x4f res = m1; 1036 res.a11 += m2.a11; res.a12 += m2.a12; res.a13 += m2.a13; 1037 res.a21 += m2.a21; res.a22 += m2.a22; res.a23 += m2.a23; 1038 res.a31 += m2.a31; res.a32 += m2.a32; res.a33 += m2.a33; 1039 res.a14 += m2.a14; 1040 res.a24 += m2.a24; 1041 res.a34 += m2.a34; 1042 return res; 1043 } 1044 1045 bool validMatrix(T, size_t N)(Matrix!(T, N) m) 1046 { 1047 foreach (v; m.arrayof) 1048 if (isNaN(v)) 1049 return false; 1050 return true; 1051 } 1052 1053 bool validVector(T, size_t N)(Vector!(T, N) vec) 1054 { 1055 foreach (v; vec.arrayof) 1056 if (isNaN(v)) 1057 return false; 1058 return true; 1059 }