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 }