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 }