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