Remove a duplicate definition of qglGetPointerv.
[xonotic/darkplaces.git] / model_alias.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22 #include "image.h"
23 #include "r_shadow.h"
24 #include "mod_skeletal_animatevertices_generic.h"
25 #ifdef SSE_POSSIBLE
26 #include "mod_skeletal_animatevertices_sse.h"
27 #endif
28
29 #ifdef SSE_POSSIBLE
30 static qboolean r_skeletal_use_sse_defined = false;
31 cvar_t r_skeletal_use_sse = {0, "r_skeletal_use_sse", "1", "use SSE for skeletal model animation"};
32 #endif
33 cvar_t r_skeletal_debugbone = {0, "r_skeletal_debugbone", "-1", "development cvar for testing skeletal model code"};
34 cvar_t r_skeletal_debugbonecomponent = {0, "r_skeletal_debugbonecomponent", "3", "development cvar for testing skeletal model code"};
35 cvar_t r_skeletal_debugbonevalue = {0, "r_skeletal_debugbonevalue", "100", "development cvar for testing skeletal model code"};
36 cvar_t r_skeletal_debugtranslatex = {0, "r_skeletal_debugtranslatex", "1", "development cvar for testing skeletal model code"};
37 cvar_t r_skeletal_debugtranslatey = {0, "r_skeletal_debugtranslatey", "1", "development cvar for testing skeletal model code"};
38 cvar_t r_skeletal_debugtranslatez = {0, "r_skeletal_debugtranslatez", "1", "development cvar for testing skeletal model code"};
39 cvar_t mod_alias_supporttagscale = {0, "mod_alias_supporttagscale", "1", "support scaling factors in bone/tag attachment matrices as supported by MD3"};
40 cvar_t mod_alias_force_animated = {0, "mod_alias_force_animated", "", "if set to an non-empty string, overrides the is-animated flag of any alias models (for benchmarking)"};
41
42 float mod_md3_sin[320];
43
44 static size_t Mod_Skeletal_AnimateVertices_maxbonepose = 0;
45 static void *Mod_Skeletal_AnimateVertices_bonepose = NULL;
46 void Mod_Skeletal_FreeBuffers(void)
47 {
48         if(Mod_Skeletal_AnimateVertices_bonepose)
49                 Mem_Free(Mod_Skeletal_AnimateVertices_bonepose);
50         Mod_Skeletal_AnimateVertices_maxbonepose = 0;
51         Mod_Skeletal_AnimateVertices_bonepose = NULL;
52 }
53 void *Mod_Skeletal_AnimateVertices_AllocBuffers(size_t nbytes)
54 {
55         if(Mod_Skeletal_AnimateVertices_maxbonepose < nbytes)
56         {
57                 if(Mod_Skeletal_AnimateVertices_bonepose)
58                         Mem_Free(Mod_Skeletal_AnimateVertices_bonepose);
59                 Mod_Skeletal_AnimateVertices_bonepose = Z_Malloc(nbytes);
60                 Mod_Skeletal_AnimateVertices_maxbonepose = nbytes;
61         }
62         return Mod_Skeletal_AnimateVertices_bonepose;
63 }
64
65 void Mod_Skeletal_BuildTransforms(const dp_model_t * RESTRICT model, const frameblend_t * RESTRICT frameblend, const skeleton_t *skeleton, float * RESTRICT bonepose, float * RESTRICT boneposerelative)
66 {
67         int i, blends;
68         float m[12];
69
70         if (!bonepose)
71                 bonepose = (float * RESTRICT) Mod_Skeletal_AnimateVertices_AllocBuffers(sizeof(float[12]) * model->num_bones);
72                 
73         if (skeleton && !skeleton->relativetransforms)
74                 skeleton = NULL;
75
76         // interpolate matrices
77         if (skeleton)
78         {
79                 for (i = 0;i < model->num_bones;i++)
80                 {
81                         Matrix4x4_ToArray12FloatD3D(&skeleton->relativetransforms[i], m);
82                         if (model->data_bones[i].parent >= 0)
83                                 R_ConcatTransforms(bonepose + model->data_bones[i].parent * 12, m, bonepose + i * 12);
84                         else
85                                 memcpy(bonepose + i * 12, m, sizeof(m));
86
87                         // create a relative deformation matrix to describe displacement
88                         // from the base mesh, which is used by the actual weighting
89                         R_ConcatTransforms(bonepose + i * 12, model->data_baseboneposeinverse + i * 12, boneposerelative + i * 12);
90                 }
91         }
92         else
93         {
94                 for (i = 0;i < model->num_bones;i++)
95                 {
96                         // blend by transform each quaternion/translation into a dual-quaternion first, then blending
97                         const short * RESTRICT firstpose7s = model->data_poses7s + 7 * (frameblend[0].subframe * model->num_bones + i);
98                         float firstlerp = frameblend[0].lerp,
99                                 firsttx = firstpose7s[0], firstty = firstpose7s[1], firsttz = firstpose7s[2],
100                                 rx = firstpose7s[3] * firstlerp,
101                                 ry = firstpose7s[4] * firstlerp,
102                                 rz = firstpose7s[5] * firstlerp,
103                                 rw = firstpose7s[6] * firstlerp,
104                                 dx = firsttx*rw + firstty*rz - firsttz*ry,
105                                 dy = -firsttx*rz + firstty*rw + firsttz*rx,
106                                 dz = firsttx*ry - firstty*rx + firsttz*rw,
107                                 dw = -firsttx*rx - firstty*ry - firsttz*rz,
108                                 scale, sx, sy, sz, sw;
109                         for (blends = 1;blends < MAX_FRAMEBLENDS && frameblend[blends].lerp > 0;blends++)
110                         {
111                                 const short * RESTRICT blendpose7s = model->data_poses7s + 7 * (frameblend[blends].subframe * model->num_bones + i);
112                                 float blendlerp = frameblend[blends].lerp,
113                                         blendtx = blendpose7s[0], blendty = blendpose7s[1], blendtz = blendpose7s[2],
114                                         qx = blendpose7s[3], qy = blendpose7s[4], qz = blendpose7s[5], qw = blendpose7s[6];
115                                 if(rx*qx + ry*qy + rz*qz + rw*qw < 0) blendlerp = -blendlerp;
116                                 qx *= blendlerp;
117                                 qy *= blendlerp;
118                                 qz *= blendlerp;
119                                 qw *= blendlerp;
120                                 rx += qx;
121                                 ry += qy;
122                                 rz += qz;
123                                 rw += qw;
124                                 dx += blendtx*qw + blendty*qz - blendtz*qy;
125                                 dy += -blendtx*qz + blendty*qw + blendtz*qx;
126                                 dz += blendtx*qy - blendty*qx + blendtz*qw;
127                                 dw += -blendtx*qx - blendty*qy - blendtz*qz;
128                         }
129                         // generate a matrix from the dual-quaternion, implicitly normalizing it in the process
130                         scale = 1.0f / (rx*rx + ry*ry + rz*rz + rw*rw);
131                         sx = rx * scale;
132                         sy = ry * scale;
133                         sz = rz * scale;
134                         sw = rw * scale;
135                         m[0] = sw*rw + sx*rx - sy*ry - sz*rz;
136                         m[1] = 2*(sx*ry - sw*rz);
137                         m[2] = 2*(sx*rz + sw*ry);
138                         m[3] = model->num_posescale*(dx*sw - dy*sz + dz*sy - dw*sx);
139                         m[4] = 2*(sx*ry + sw*rz);
140                         m[5] = sw*rw + sy*ry - sx*rx - sz*rz;
141                         m[6] = 2*(sy*rz - sw*rx);
142                         m[7] = model->num_posescale*(dx*sz + dy*sw - dz*sx - dw*sy);
143                         m[8] = 2*(sx*rz - sw*ry);
144                         m[9] = 2*(sy*rz + sw*rx);
145                         m[10] = sw*rw + sz*rz - sx*rx - sy*ry;
146                         m[11] = model->num_posescale*(dy*sx + dz*sw - dx*sy - dw*sz);
147                         if (i == r_skeletal_debugbone.integer)
148                                 m[r_skeletal_debugbonecomponent.integer % 12] += r_skeletal_debugbonevalue.value;
149                         m[3] *= r_skeletal_debugtranslatex.value;
150                         m[7] *= r_skeletal_debugtranslatey.value;
151                         m[11] *= r_skeletal_debugtranslatez.value;
152                         if (model->data_bones[i].parent >= 0)
153                                 R_ConcatTransforms(bonepose + model->data_bones[i].parent * 12, m, bonepose + i * 12);
154                         else
155                                 memcpy(bonepose + i * 12, m, sizeof(m));
156                         // create a relative deformation matrix to describe displacement
157                         // from the base mesh, which is used by the actual weighting
158                         R_ConcatTransforms(bonepose + i * 12, model->data_baseboneposeinverse + i * 12, boneposerelative + i * 12);
159                 }
160         }
161 }
162
163 static void Mod_Skeletal_AnimateVertices(const dp_model_t * RESTRICT model, const frameblend_t * RESTRICT frameblend, const skeleton_t *skeleton, float * RESTRICT vertex3f, float * RESTRICT normal3f, float * RESTRICT svector3f, float * RESTRICT tvector3f)
164 {
165
166         if (!model->surfmesh.num_vertices)
167                 return;
168
169         if (!model->num_bones)
170         {
171                 if (vertex3f) memcpy(vertex3f, model->surfmesh.data_vertex3f, model->surfmesh.num_vertices*sizeof(float[3]));
172                 if (normal3f) memcpy(normal3f, model->surfmesh.data_normal3f, model->surfmesh.num_vertices*sizeof(float[3]));
173                 if (svector3f) memcpy(svector3f, model->surfmesh.data_svector3f, model->surfmesh.num_vertices*sizeof(float[3]));
174                 if (tvector3f) memcpy(tvector3f, model->surfmesh.data_tvector3f, model->surfmesh.num_vertices*sizeof(float[3]));
175                 return;
176         }
177
178 #ifdef SSE_POSSIBLE
179         if(r_skeletal_use_sse_defined)
180                 if(r_skeletal_use_sse.integer)
181                 {
182                         Mod_Skeletal_AnimateVertices_SSE(model, frameblend, skeleton, vertex3f, normal3f, svector3f, tvector3f);
183                         return;
184                 }
185 #endif
186         Mod_Skeletal_AnimateVertices_Generic(model, frameblend, skeleton, vertex3f, normal3f, svector3f, tvector3f);
187 }
188
189 void Mod_AliasInit (void)
190 {
191         int i;
192         Cvar_RegisterVariable(&r_skeletal_debugbone);
193         Cvar_RegisterVariable(&r_skeletal_debugbonecomponent);
194         Cvar_RegisterVariable(&r_skeletal_debugbonevalue);
195         Cvar_RegisterVariable(&r_skeletal_debugtranslatex);
196         Cvar_RegisterVariable(&r_skeletal_debugtranslatey);
197         Cvar_RegisterVariable(&r_skeletal_debugtranslatez);
198         Cvar_RegisterVariable(&mod_alias_supporttagscale);
199         Cvar_RegisterVariable(&mod_alias_force_animated);
200         for (i = 0;i < 320;i++)
201                 mod_md3_sin[i] = sin(i * M_PI * 2.0f / 256.0);
202 #ifdef SSE_POSSIBLE
203         if(Sys_HaveSSE())
204         {
205                 Con_Printf("Skeletal animation uses SSE code path\n");
206                 r_skeletal_use_sse_defined = true;
207                 Cvar_RegisterVariable(&r_skeletal_use_sse);
208         }
209         else
210                 Con_Printf("Skeletal animation uses generic code path (SSE disabled or not detected)\n");
211 #else
212         Con_Printf("Skeletal animation uses generic code path (SSE not compiled in)\n");
213 #endif
214 }
215
216 static int Mod_Skeletal_AddBlend(dp_model_t *model, const blendweights_t *newweights)
217 {
218         int i;
219         blendweights_t *weights;
220         if(!newweights->influence[1])
221                 return newweights->index[0];
222         weights = model->surfmesh.data_blendweights;
223         for (i = 0;i < model->surfmesh.num_blends;i++, weights++)
224         {
225                 if (!memcmp(weights, newweights, sizeof(blendweights_t)))
226                         return model->num_bones + i;
227         }
228         model->surfmesh.num_blends++;
229         memcpy(weights, newweights, sizeof(blendweights_t));
230         return model->num_bones + i;
231 }
232
233 static int Mod_Skeletal_CompressBlend(dp_model_t *model, const int *newindex, const float *newinfluence)
234 {
235         int i, total;
236         float scale;
237         blendweights_t newweights;
238         if(!newinfluence[1])
239                 return newindex[0];
240         scale = 0;
241         for (i = 0;i < 4;i++)
242                 scale += newinfluence[i];
243         scale = 255.0f / scale;
244         total = 0;
245         for (i = 0;i < 4;i++)
246         {
247                 newweights.index[i] = newindex[i];
248                 newweights.influence[i] = (unsigned char)(newinfluence[i] * scale);
249                 total += newweights.influence[i];
250         }       
251         while (total > 255)
252         {
253                 for (i = 0;i < 4;i++)
254                 {
255                         if(newweights.influence[i] > 0 && total > 255) 
256                         { 
257                                 newweights.influence[i]--;
258                                 total--; 
259                         }
260                 }
261         }
262         while (total < 255)
263         {
264                 for (i = 0; i < 4;i++)
265                 {
266                         if(newweights.influence[i] < 255 && total < 255) 
267                         { 
268                                 newweights.influence[i]++; 
269                                 total++; 
270                         }
271                 }
272         }
273         return Mod_Skeletal_AddBlend(model, &newweights);
274 }
275
276 static void Mod_MD3_AnimateVertices(const dp_model_t * RESTRICT model, const frameblend_t * RESTRICT frameblend, const skeleton_t *skeleton, float * RESTRICT vertex3f, float * RESTRICT normal3f, float * RESTRICT svector3f, float * RESTRICT tvector3f)
277 {
278         // vertex morph
279         int i, numblends, blendnum;
280         int numverts = model->surfmesh.num_vertices;
281         numblends = 0;
282         for (blendnum = 0;blendnum < MAX_FRAMEBLENDS;blendnum++)
283         {
284                 //VectorMA(translate, model->surfmesh.num_morphmdlframetranslate, frameblend[blendnum].lerp, translate);
285                 if (frameblend[blendnum].lerp > 0)
286                         numblends = blendnum + 1;
287         }
288         // special case for the first blend because it avoids some adds and the need to memset the arrays first
289         for (blendnum = 0;blendnum < numblends;blendnum++)
290         {
291                 const md3vertex_t *verts = model->surfmesh.data_morphmd3vertex + numverts * frameblend[blendnum].subframe;
292                 if (vertex3f)
293                 {
294                         float scale = frameblend[blendnum].lerp * (1.0f / 64.0f);
295                         if (blendnum == 0)
296                         {
297                                 for (i = 0;i < numverts;i++)
298                                 {
299                                         vertex3f[i * 3 + 0] = verts[i].origin[0] * scale;
300                                         vertex3f[i * 3 + 1] = verts[i].origin[1] * scale;
301                                         vertex3f[i * 3 + 2] = verts[i].origin[2] * scale;
302                                 }
303                         }
304                         else
305                         {
306                                 for (i = 0;i < numverts;i++)
307                                 {
308                                         vertex3f[i * 3 + 0] += verts[i].origin[0] * scale;
309                                         vertex3f[i * 3 + 1] += verts[i].origin[1] * scale;
310                                         vertex3f[i * 3 + 2] += verts[i].origin[2] * scale;
311                                 }
312                         }
313                 }
314                 // the yaw and pitch stored in md3 models are 8bit quantized angles
315                 // (0-255), and as such a lookup table is very well suited to
316                 // decoding them, and since cosine is equivalent to sine with an
317                 // extra 45 degree rotation, this uses one lookup table for both
318                 // sine and cosine with a +64 bias to get cosine.
319                 if (normal3f)
320                 {
321                         float lerp = frameblend[blendnum].lerp;
322                         if (blendnum == 0)
323                         {
324                                 for (i = 0;i < numverts;i++)
325                                 {
326                                         normal3f[i * 3 + 0] = mod_md3_sin[verts[i].yaw + 64] * mod_md3_sin[verts[i].pitch     ] * lerp;
327                                         normal3f[i * 3 + 1] = mod_md3_sin[verts[i].yaw     ] * mod_md3_sin[verts[i].pitch     ] * lerp;
328                                         normal3f[i * 3 + 2] =                                  mod_md3_sin[verts[i].pitch + 64] * lerp;
329                                 }
330                         }
331                         else
332                         {
333                                 for (i = 0;i < numverts;i++)
334                                 {
335                                         normal3f[i * 3 + 0] += mod_md3_sin[verts[i].yaw + 64] * mod_md3_sin[verts[i].pitch     ] * lerp;
336                                         normal3f[i * 3 + 1] += mod_md3_sin[verts[i].yaw     ] * mod_md3_sin[verts[i].pitch     ] * lerp;
337                                         normal3f[i * 3 + 2] +=                                  mod_md3_sin[verts[i].pitch + 64] * lerp;
338                                 }
339                         }
340                 }
341                 if (svector3f)
342                 {
343                         const texvecvertex_t *texvecvert = model->surfmesh.data_morphtexvecvertex + numverts * frameblend[blendnum].subframe;
344                         float f = frameblend[blendnum].lerp * (1.0f / 127.0f);
345                         if (blendnum == 0)
346                         {
347                                 for (i = 0;i < numverts;i++, texvecvert++)
348                                 {
349                                         VectorScale(texvecvert->svec, f, svector3f + i*3);
350                                         VectorScale(texvecvert->tvec, f, tvector3f + i*3);
351                                 }
352                         }
353                         else
354                         {
355                                 for (i = 0;i < numverts;i++, texvecvert++)
356                                 {
357                                         VectorMA(svector3f + i*3, f, texvecvert->svec, svector3f + i*3);
358                                         VectorMA(tvector3f + i*3, f, texvecvert->tvec, tvector3f + i*3);
359                                 }
360                         }
361                 }
362         }
363 }
364 static void Mod_MDL_AnimateVertices(const dp_model_t * RESTRICT model, const frameblend_t * RESTRICT frameblend, const skeleton_t *skeleton, float * RESTRICT vertex3f, float * RESTRICT normal3f, float * RESTRICT svector3f, float * RESTRICT tvector3f)
365 {
366         // vertex morph
367         int i, numblends, blendnum;
368         int numverts = model->surfmesh.num_vertices;
369         float translate[3];
370         VectorClear(translate);
371         numblends = 0;
372         // blend the frame translates to avoid redundantly doing so on each vertex
373         // (a bit of a brain twister but it works)
374         for (blendnum = 0;blendnum < MAX_FRAMEBLENDS;blendnum++)
375         {
376                 if (model->surfmesh.data_morphmd2framesize6f)
377                         VectorMA(translate, frameblend[blendnum].lerp, model->surfmesh.data_morphmd2framesize6f + frameblend[blendnum].subframe * 6 + 3, translate);
378                 else
379                         VectorMA(translate, frameblend[blendnum].lerp, model->surfmesh.num_morphmdlframetranslate, translate);
380                 if (frameblend[blendnum].lerp > 0)
381                         numblends = blendnum + 1;
382         }
383         // special case for the first blend because it avoids some adds and the need to memset the arrays first
384         for (blendnum = 0;blendnum < numblends;blendnum++)
385         {
386                 const trivertx_t *verts = model->surfmesh.data_morphmdlvertex + numverts * frameblend[blendnum].subframe;
387                 if (vertex3f)
388                 {
389                         float scale[3];
390                         if (model->surfmesh.data_morphmd2framesize6f)
391                                 VectorScale(model->surfmesh.data_morphmd2framesize6f + frameblend[blendnum].subframe * 6, frameblend[blendnum].lerp, scale);
392                         else
393                                 VectorScale(model->surfmesh.num_morphmdlframescale, frameblend[blendnum].lerp, scale);
394                         if (blendnum == 0)
395                         {
396                                 for (i = 0;i < numverts;i++)
397                                 {
398                                         vertex3f[i * 3 + 0] = translate[0] + verts[i].v[0] * scale[0];
399                                         vertex3f[i * 3 + 1] = translate[1] + verts[i].v[1] * scale[1];
400                                         vertex3f[i * 3 + 2] = translate[2] + verts[i].v[2] * scale[2];
401                                 }
402                         }
403                         else
404                         {
405                                 for (i = 0;i < numverts;i++)
406                                 {
407                                         vertex3f[i * 3 + 0] += verts[i].v[0] * scale[0];
408                                         vertex3f[i * 3 + 1] += verts[i].v[1] * scale[1];
409                                         vertex3f[i * 3 + 2] += verts[i].v[2] * scale[2];
410                                 }
411                         }
412                 }
413                 // the vertex normals in mdl models are an index into a table of
414                 // 162 unique values, this very crude quantization reduces the
415                 // vertex normal to only one byte, which saves a lot of space but
416                 // also makes lighting pretty coarse
417                 if (normal3f)
418                 {
419                         float lerp = frameblend[blendnum].lerp;
420                         if (blendnum == 0)
421                         {
422                                 for (i = 0;i < numverts;i++)
423                                 {
424                                         const float *vn = m_bytenormals[verts[i].lightnormalindex];
425                                         VectorScale(vn, lerp, normal3f + i*3);
426                                 }
427                         }
428                         else
429                         {
430                                 for (i = 0;i < numverts;i++)
431                                 {
432                                         const float *vn = m_bytenormals[verts[i].lightnormalindex];
433                                         VectorMA(normal3f + i*3, lerp, vn, normal3f + i*3);
434                                 }
435                         }
436                 }
437                 if (svector3f)
438                 {
439                         const texvecvertex_t *texvecvert = model->surfmesh.data_morphtexvecvertex + numverts * frameblend[blendnum].subframe;
440                         float f = frameblend[blendnum].lerp * (1.0f / 127.0f);
441                         if (blendnum == 0)
442                         {
443                                 for (i = 0;i < numverts;i++, texvecvert++)
444                                 {
445                                         VectorScale(texvecvert->svec, f, svector3f + i*3);
446                                         VectorScale(texvecvert->tvec, f, tvector3f + i*3);
447                                 }
448                         }
449                         else
450                         {
451                                 for (i = 0;i < numverts;i++, texvecvert++)
452                                 {
453                                         VectorMA(svector3f + i*3, f, texvecvert->svec, svector3f + i*3);
454                                         VectorMA(tvector3f + i*3, f, texvecvert->tvec, tvector3f + i*3);
455                                 }
456                         }
457                 }
458         }
459 }
460
461 int Mod_Alias_GetTagMatrix(const dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, int tagindex, matrix4x4_t *outmatrix)
462 {
463         matrix4x4_t temp;
464         matrix4x4_t parentbonematrix;
465         matrix4x4_t tempbonematrix;
466         matrix4x4_t bonematrix;
467         matrix4x4_t blendmatrix;
468         int blendindex;
469         int parenttagindex;
470         int k;
471         float lerp;
472         const float *input;
473         float blendtag[12];
474         *outmatrix = identitymatrix;
475         if (skeleton && skeleton->relativetransforms)
476         {
477                 if (tagindex < 0 || tagindex >= skeleton->model->num_bones)
478                         return 4;
479                 *outmatrix = skeleton->relativetransforms[tagindex];
480                 while ((tagindex = model->data_bones[tagindex].parent) >= 0)
481                 {
482                         temp = *outmatrix;
483                         Matrix4x4_Concat(outmatrix, &skeleton->relativetransforms[tagindex], &temp);
484                 }
485         }
486         else if (model->num_bones)
487         {
488                 if (tagindex < 0 || tagindex >= model->num_bones)
489                         return 4;
490                 Matrix4x4_Clear(&blendmatrix);
491                 for (blendindex = 0;blendindex < MAX_FRAMEBLENDS && frameblend[blendindex].lerp > 0;blendindex++)
492                 {
493                         lerp = frameblend[blendindex].lerp;
494                         Matrix4x4_FromBonePose7s(&bonematrix, model->num_posescale, model->data_poses7s + 7 * (frameblend[blendindex].subframe * model->num_bones + tagindex));
495                         parenttagindex = tagindex;
496                         while ((parenttagindex = model->data_bones[parenttagindex].parent) >= 0)
497                         {
498                                 Matrix4x4_FromBonePose7s(&parentbonematrix, model->num_posescale, model->data_poses7s + 7 * (frameblend[blendindex].subframe * model->num_bones + parenttagindex));
499                                 tempbonematrix = bonematrix;
500                                 Matrix4x4_Concat(&bonematrix, &parentbonematrix, &tempbonematrix);
501                         }
502                         Matrix4x4_Accumulate(&blendmatrix, &bonematrix, lerp);
503                 }
504                 *outmatrix = blendmatrix;
505         }
506         else if (model->num_tags)
507         {
508                 if (tagindex < 0 || tagindex >= model->num_tags)
509                         return 4;
510                 for (k = 0;k < 12;k++)
511                         blendtag[k] = 0;
512                 for (blendindex = 0;blendindex < MAX_FRAMEBLENDS && frameblend[blendindex].lerp > 0;blendindex++)
513                 {
514                         lerp = frameblend[blendindex].lerp;
515                         input = model->data_tags[frameblend[blendindex].subframe * model->num_tags + tagindex].matrixgl;
516                         for (k = 0;k < 12;k++)
517                                 blendtag[k] += input[k] * lerp;
518                 }
519                 Matrix4x4_FromArray12FloatGL(outmatrix, blendtag);
520         }
521
522         if(!mod_alias_supporttagscale.integer)
523                 Matrix4x4_Normalize3(outmatrix, outmatrix);
524
525         return 0;
526 }
527
528 int Mod_Alias_GetExtendedTagInfoForIndex(const dp_model_t *model, unsigned int skin, const frameblend_t *frameblend, const skeleton_t *skeleton, int tagindex, int *parentindex, const char **tagname, matrix4x4_t *tag_localmatrix)
529 {
530         int blendindex;
531         int k;
532         float lerp;
533         matrix4x4_t bonematrix;
534         matrix4x4_t blendmatrix;
535         const float *input;
536         float blendtag[12];
537
538         if (skeleton && skeleton->relativetransforms)
539         {
540                 if (tagindex < 0 || tagindex >= skeleton->model->num_bones)
541                         return 1;
542                 *parentindex = skeleton->model->data_bones[tagindex].parent;
543                 *tagname = skeleton->model->data_bones[tagindex].name;
544                 *tag_localmatrix = skeleton->relativetransforms[tagindex];
545                 return 0;
546         }
547         else if (model->num_bones)
548         {
549                 if (tagindex < 0 || tagindex >= model->num_bones)
550                         return 1;
551                 *parentindex = model->data_bones[tagindex].parent;
552                 *tagname = model->data_bones[tagindex].name;
553                 Matrix4x4_Clear(&blendmatrix);
554                 for (blendindex = 0;blendindex < MAX_FRAMEBLENDS && frameblend[blendindex].lerp > 0;blendindex++)
555                 {
556                         lerp = frameblend[blendindex].lerp;
557                         Matrix4x4_FromBonePose7s(&bonematrix, model->num_posescale, model->data_poses7s + 7 * (frameblend[blendindex].subframe * model->num_bones + tagindex));
558                         Matrix4x4_Accumulate(&blendmatrix, &bonematrix, lerp);
559                 }
560                 *tag_localmatrix = blendmatrix;
561                 return 0;
562         }
563         else if (model->num_tags)
564         {
565                 if (tagindex < 0 || tagindex >= model->num_tags)
566                         return 1;
567                 *parentindex = -1;
568                 *tagname = model->data_tags[tagindex].name;
569                 for (k = 0;k < 12;k++)
570                         blendtag[k] = 0;
571                 for (blendindex = 0;blendindex < MAX_FRAMEBLENDS && frameblend[blendindex].lerp > 0;blendindex++)
572                 {
573                         lerp = frameblend[blendindex].lerp;
574                         input = model->data_tags[frameblend[blendindex].subframe * model->num_tags + tagindex].matrixgl;
575                         for (k = 0;k < 12;k++)
576                                 blendtag[k] += input[k] * lerp;
577                 }
578                 Matrix4x4_FromArray12FloatGL(tag_localmatrix, blendtag);
579                 return 0;
580         }
581
582         return 2;
583 }
584
585 int Mod_Alias_GetTagIndexForName(const dp_model_t *model, unsigned int skin, const char *tagname)
586 {
587         int i;
588         if(skin >= (unsigned int)model->numskins)
589                 skin = 0;
590         if (model->num_bones)
591                 for (i = 0;i < model->num_bones;i++)
592                         if (!strcasecmp(tagname, model->data_bones[i].name))
593                                 return i + 1;
594         if (model->num_tags)
595                 for (i = 0;i < model->num_tags;i++)
596                         if (!strcasecmp(tagname, model->data_tags[i].name))
597                                 return i + 1;
598         return 0;
599 }
600
601 static void Mod_BuildBaseBonePoses(void)
602 {
603         int boneindex;
604         matrix4x4_t *basebonepose;
605         float *outinvmatrix = loadmodel->data_baseboneposeinverse;
606         matrix4x4_t bonematrix;
607         matrix4x4_t tempbonematrix;
608         if (!loadmodel->num_bones)
609                 return;
610         basebonepose = (matrix4x4_t *)Mem_Alloc(tempmempool, loadmodel->num_bones * sizeof(matrix4x4_t));
611         for (boneindex = 0;boneindex < loadmodel->num_bones;boneindex++)
612         {
613                 Matrix4x4_FromBonePose7s(&bonematrix, loadmodel->num_posescale, loadmodel->data_poses7s + 7 * boneindex);
614                 if (loadmodel->data_bones[boneindex].parent >= 0)
615                 {
616                         tempbonematrix = bonematrix;
617                         Matrix4x4_Concat(&bonematrix, basebonepose + loadmodel->data_bones[boneindex].parent, &tempbonematrix);
618                 }
619                 basebonepose[boneindex] = bonematrix;
620                 Matrix4x4_Invert_Simple(&tempbonematrix, basebonepose + boneindex);
621                 Matrix4x4_ToArray12FloatD3D(&tempbonematrix, outinvmatrix + 12*boneindex);
622         }
623         Mem_Free(basebonepose);
624 }
625
626 static qboolean Mod_Alias_CalculateBoundingBox(void)
627 {
628         int vnum;
629         qboolean firstvertex = true;
630         float dist, yawradius, radius;
631         float *v;
632         qboolean isanimated = false;
633         VectorClear(loadmodel->normalmins);
634         VectorClear(loadmodel->normalmaxs);
635         yawradius = 0;
636         radius = 0;
637         if (loadmodel->AnimateVertices)
638         {
639                 float *vertex3f, *refvertex3f;
640                 frameblend_t frameblend[MAX_FRAMEBLENDS];
641                 memset(frameblend, 0, sizeof(frameblend));
642                 frameblend[0].lerp = 1;
643                 vertex3f = (float *) Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_vertices * sizeof(float[3]) * 2);
644                 refvertex3f = NULL;
645                 for (frameblend[0].subframe = 0;frameblend[0].subframe < loadmodel->num_poses;frameblend[0].subframe++)
646                 {
647                         loadmodel->AnimateVertices(loadmodel, frameblend, NULL, vertex3f, NULL, NULL, NULL);
648                         if (!refvertex3f)
649                         {
650                                 // make a copy of the first frame for comparing all others
651                                 refvertex3f = vertex3f + loadmodel->surfmesh.num_vertices * 3;
652                                 memcpy(refvertex3f, vertex3f, loadmodel->surfmesh.num_vertices * sizeof(float[3]));
653                         }
654                         else
655                         {
656                                 if (!isanimated && memcmp(refvertex3f, vertex3f, loadmodel->surfmesh.num_vertices * sizeof(float[3])))
657                                         isanimated = true;
658                         }
659                         for (vnum = 0, v = vertex3f;vnum < loadmodel->surfmesh.num_vertices;vnum++, v += 3)
660                         {
661                                 if (firstvertex)
662                                 {
663                                         firstvertex = false;
664                                         VectorCopy(v, loadmodel->normalmins);
665                                         VectorCopy(v, loadmodel->normalmaxs);
666                                 }
667                                 else
668                                 {
669                                         if (loadmodel->normalmins[0] > v[0]) loadmodel->normalmins[0] = v[0];
670                                         if (loadmodel->normalmins[1] > v[1]) loadmodel->normalmins[1] = v[1];
671                                         if (loadmodel->normalmins[2] > v[2]) loadmodel->normalmins[2] = v[2];
672                                         if (loadmodel->normalmaxs[0] < v[0]) loadmodel->normalmaxs[0] = v[0];
673                                         if (loadmodel->normalmaxs[1] < v[1]) loadmodel->normalmaxs[1] = v[1];
674                                         if (loadmodel->normalmaxs[2] < v[2]) loadmodel->normalmaxs[2] = v[2];
675                                 }
676                                 dist = v[0] * v[0] + v[1] * v[1];
677                                 if (yawradius < dist)
678                                         yawradius = dist;
679                                 dist += v[2] * v[2];
680                                 if (radius < dist)
681                                         radius = dist;
682                         }
683                 }
684                 if (vertex3f)
685                         Mem_Free(vertex3f);
686         }
687         else
688         {
689                 for (vnum = 0, v = loadmodel->surfmesh.data_vertex3f;vnum < loadmodel->surfmesh.num_vertices;vnum++, v += 3)
690                 {
691                         if (firstvertex)
692                         {
693                                 firstvertex = false;
694                                 VectorCopy(v, loadmodel->normalmins);
695                                 VectorCopy(v, loadmodel->normalmaxs);
696                         }
697                         else
698                         {
699                                 if (loadmodel->normalmins[0] > v[0]) loadmodel->normalmins[0] = v[0];
700                                 if (loadmodel->normalmins[1] > v[1]) loadmodel->normalmins[1] = v[1];
701                                 if (loadmodel->normalmins[2] > v[2]) loadmodel->normalmins[2] = v[2];
702                                 if (loadmodel->normalmaxs[0] < v[0]) loadmodel->normalmaxs[0] = v[0];
703                                 if (loadmodel->normalmaxs[1] < v[1]) loadmodel->normalmaxs[1] = v[1];
704                                 if (loadmodel->normalmaxs[2] < v[2]) loadmodel->normalmaxs[2] = v[2];
705                         }
706                         dist = v[0] * v[0] + v[1] * v[1];
707                         if (yawradius < dist)
708                                 yawradius = dist;
709                         dist += v[2] * v[2];
710                         if (radius < dist)
711                                 radius = dist;
712                 }
713         }
714         radius = sqrt(radius);
715         yawradius = sqrt(yawradius);
716         loadmodel->yawmins[0] = loadmodel->yawmins[1] = -yawradius;
717         loadmodel->yawmaxs[0] = loadmodel->yawmaxs[1] = yawradius;
718         loadmodel->yawmins[2] = loadmodel->normalmins[2];
719         loadmodel->yawmaxs[2] = loadmodel->normalmaxs[2];
720         loadmodel->rotatedmins[0] = loadmodel->rotatedmins[1] = loadmodel->rotatedmins[2] = -radius;
721         loadmodel->rotatedmaxs[0] = loadmodel->rotatedmaxs[1] = loadmodel->rotatedmaxs[2] = radius;
722         loadmodel->radius = radius;
723         loadmodel->radius2 = radius * radius;
724         return isanimated;
725 }
726
727 static void Mod_Alias_MorphMesh_CompileFrames(void)
728 {
729         int i, j;
730         frameblend_t frameblend[MAX_FRAMEBLENDS];
731         unsigned char *datapointer;
732         memset(frameblend, 0, sizeof(frameblend));
733         frameblend[0].lerp = 1;
734         datapointer = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_vertices * (sizeof(float[3]) * 4 + loadmodel->surfmesh.num_morphframes * sizeof(texvecvertex_t)));
735         loadmodel->surfmesh.data_vertex3f = (float *)datapointer;datapointer += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
736         loadmodel->surfmesh.data_svector3f = (float *)datapointer;datapointer += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
737         loadmodel->surfmesh.data_tvector3f = (float *)datapointer;datapointer += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
738         loadmodel->surfmesh.data_normal3f = (float *)datapointer;datapointer += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
739         loadmodel->surfmesh.data_morphtexvecvertex = (texvecvertex_t *)datapointer;datapointer += loadmodel->surfmesh.num_morphframes * loadmodel->surfmesh.num_vertices * sizeof(texvecvertex_t);
740         // this counts down from the last frame to the first so that the final data in surfmesh is for frame zero (which is what the renderer expects to be there)
741         for (i = loadmodel->surfmesh.num_morphframes-1;i >= 0;i--)
742         {
743                 frameblend[0].subframe = i;
744                 loadmodel->AnimateVertices(loadmodel, frameblend, NULL, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_normal3f, NULL, NULL);
745                 Mod_BuildTextureVectorsFromNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_texcoordtexture2f, loadmodel->surfmesh.data_normal3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_svector3f, loadmodel->surfmesh.data_tvector3f, r_smoothnormals_areaweighting.integer != 0);
746                 // encode the svector and tvector in 3 byte format for permanent storage
747                 for (j = 0;j < loadmodel->surfmesh.num_vertices;j++)
748                 {
749                         VectorScaleCast(loadmodel->surfmesh.data_svector3f + j * 3, 127.0f, signed char, loadmodel->surfmesh.data_morphtexvecvertex[i*loadmodel->surfmesh.num_vertices+j].svec);
750                         VectorScaleCast(loadmodel->surfmesh.data_tvector3f + j * 3, 127.0f, signed char, loadmodel->surfmesh.data_morphtexvecvertex[i*loadmodel->surfmesh.num_vertices+j].tvec);
751                 }
752         }
753 }
754
755 static void Mod_MDLMD2MD3_TraceLine(dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, trace_t *trace, const vec3_t start, const vec3_t end, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask)
756 {
757         int i;
758         float segmentmins[3], segmentmaxs[3];
759         msurface_t *surface;
760         float vertex3fbuf[1024 * 3];
761         float *vertex3f = vertex3fbuf;
762         float *freevertex3f = NULL;
763         // for static cases we can just call CollisionBIH which is much faster
764         if ((frameblend == NULL || (frameblend[0].subframe == 0 && frameblend[1].lerp == 0)) && (skeleton == NULL || skeleton->relativetransforms == NULL))
765         {
766                 Mod_CollisionBIH_TraceLine(model, frameblend, skeleton, trace, start, end, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
767                 return;
768         }
769         memset(trace, 0, sizeof(*trace));
770         trace->fraction = 1;
771         trace->hitsupercontentsmask = hitsupercontentsmask;
772         trace->skipsupercontentsmask = skipsupercontentsmask;
773         trace->skipmaterialflagsmask = skipmaterialflagsmask;
774         segmentmins[0] = min(start[0], end[0]) - 1;
775         segmentmins[1] = min(start[1], end[1]) - 1;
776         segmentmins[2] = min(start[2], end[2]) - 1;
777         segmentmaxs[0] = max(start[0], end[0]) + 1;
778         segmentmaxs[1] = max(start[1], end[1]) + 1;
779         segmentmaxs[2] = max(start[2], end[2]) + 1;
780         if (frameblend == NULL || frameblend[0].subframe != 0 || frameblend[0].lerp != 0 || skeleton != NULL)
781         {
782                 if (model->surfmesh.num_vertices > 1024)
783                         vertex3f = freevertex3f = (float *)Mem_Alloc(tempmempool, model->surfmesh.num_vertices * sizeof(float[3]));
784                 model->AnimateVertices(model, frameblend, skeleton, vertex3f, NULL, NULL, NULL);
785         }
786         else
787                 vertex3f = model->surfmesh.data_vertex3f;
788         for (i = 0, surface = model->data_surfaces;i < model->num_surfaces;i++, surface++)
789                 Collision_TraceLineTriangleMeshFloat(trace, start, end, model->surfmesh.num_triangles, model->surfmesh.data_element3i, vertex3f, 0, NULL, SUPERCONTENTS_SOLID | (surface->texture->basematerialflags & MATERIALFLAGMASK_TRANSLUCENT ? 0 : SUPERCONTENTS_OPAQUE), 0, surface->texture, segmentmins, segmentmaxs);
790         if (freevertex3f)
791                 Mem_Free(freevertex3f);
792 }
793
794 static void Mod_MDLMD2MD3_TraceBox(dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, trace_t *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask)
795 {
796         int i;
797         vec3_t shiftstart, shiftend;
798         float segmentmins[3], segmentmaxs[3];
799         msurface_t *surface;
800         float vertex3fbuf[1024*3];
801         float *vertex3f = vertex3fbuf;
802         colboxbrushf_t thisbrush_start, thisbrush_end;
803         vec3_t boxstartmins, boxstartmaxs, boxendmins, boxendmaxs;
804
805         if (VectorCompare(boxmins, boxmaxs))
806         {
807                 VectorAdd(start, boxmins, shiftstart);
808                 VectorAdd(end, boxmins, shiftend);
809                 Mod_MDLMD2MD3_TraceLine(model, frameblend, skeleton, trace, shiftstart, shiftend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
810                 VectorSubtract(trace->endpos, boxmins, trace->endpos);
811                 return;
812         }
813
814         // for static cases we can just call CollisionBIH which is much faster
815         if ((frameblend == NULL || (frameblend[0].subframe == 0 && frameblend[1].lerp == 0)) && (skeleton == NULL || skeleton->relativetransforms == NULL))
816         {
817                 Mod_CollisionBIH_TraceBox(model, frameblend, skeleton, trace, start, boxmins, boxmaxs, end, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
818                 return;
819         }
820
821         // box trace, performed as brush trace
822         memset(trace, 0, sizeof(*trace));
823         trace->fraction = 1;
824         trace->hitsupercontentsmask = hitsupercontentsmask;
825         trace->skipsupercontentsmask = skipsupercontentsmask;
826         trace->skipmaterialflagsmask = skipmaterialflagsmask;
827         if (model->surfmesh.num_vertices > 1024)
828                 vertex3f = (float *)Mem_Alloc(tempmempool, model->surfmesh.num_vertices * sizeof(float[3]));
829         segmentmins[0] = min(start[0], end[0]) + boxmins[0] - 1;
830         segmentmins[1] = min(start[1], end[1]) + boxmins[1] - 1;
831         segmentmins[2] = min(start[2], end[2]) + boxmins[2] - 1;
832         segmentmaxs[0] = max(start[0], end[0]) + boxmaxs[0] + 1;
833         segmentmaxs[1] = max(start[1], end[1]) + boxmaxs[1] + 1;
834         segmentmaxs[2] = max(start[2], end[2]) + boxmaxs[2] + 1;
835         VectorAdd(start, boxmins, boxstartmins);
836         VectorAdd(start, boxmaxs, boxstartmaxs);
837         VectorAdd(end, boxmins, boxendmins);
838         VectorAdd(end, boxmaxs, boxendmaxs);
839         Collision_BrushForBox(&thisbrush_start, boxstartmins, boxstartmaxs, 0, 0, NULL);
840         Collision_BrushForBox(&thisbrush_end, boxendmins, boxendmaxs, 0, 0, NULL);
841         model->AnimateVertices(model, frameblend, skeleton, vertex3f, NULL, NULL, NULL);
842         for (i = 0, surface = model->data_surfaces;i < model->num_surfaces;i++, surface++)
843                 Collision_TraceBrushTriangleMeshFloat(trace, &thisbrush_start.brush, &thisbrush_end.brush, model->surfmesh.num_triangles, model->surfmesh.data_element3i, vertex3f, 0, NULL, SUPERCONTENTS_SOLID | (surface->texture->basematerialflags & MATERIALFLAGMASK_TRANSLUCENT ? 0 : SUPERCONTENTS_OPAQUE), 0, surface->texture, segmentmins, segmentmaxs);
844         if (vertex3f != vertex3fbuf)
845                 Mem_Free(vertex3f);
846 }
847
848 static void Mod_ConvertAliasVerts (int inverts, trivertx_t *v, trivertx_t *out, int *vertremap)
849 {
850         int i, j;
851         for (i = 0;i < inverts;i++)
852         {
853                 if (vertremap[i] < 0 && vertremap[i+inverts] < 0) // only used vertices need apply...
854                         continue;
855                 j = vertremap[i]; // not onseam
856                 if (j >= 0)
857                         out[j] = v[i];
858                 j = vertremap[i+inverts]; // onseam
859                 if (j >= 0)
860                         out[j] = v[i];
861         }
862 }
863
864 static void Mod_MDL_LoadFrames (unsigned char* datapointer, int inverts, int *vertremap)
865 {
866         int i, f, pose, groupframes;
867         float interval;
868         daliasframetype_t *pframetype;
869         daliasframe_t *pinframe;
870         daliasgroup_t *group;
871         daliasinterval_t *intervals;
872         animscene_t *scene;
873         pose = 0;
874         scene = loadmodel->animscenes;
875         for (f = 0;f < loadmodel->numframes;f++)
876         {
877                 pframetype = (daliasframetype_t *)datapointer;
878                 datapointer += sizeof(daliasframetype_t);
879                 if (LittleLong (pframetype->type) == ALIAS_SINGLE)
880                 {
881                         // a single frame is still treated as a group
882                         interval = 0.1f;
883                         groupframes = 1;
884                 }
885                 else
886                 {
887                         // read group header
888                         group = (daliasgroup_t *)datapointer;
889                         datapointer += sizeof(daliasgroup_t);
890                         groupframes = LittleLong (group->numframes);
891
892                         // intervals (time per frame)
893                         intervals = (daliasinterval_t *)datapointer;
894                         datapointer += sizeof(daliasinterval_t) * groupframes;
895
896                         interval = LittleFloat (intervals->interval); // FIXME: support variable framerate groups
897                         if (interval < 0.01f)
898                         {
899                                 Con_Printf("%s has an invalid interval %f, changing to 0.1\n", loadmodel->name, interval);
900                                 interval = 0.1f;
901                         }
902                 }
903
904                 // get scene name from first frame
905                 pinframe = (daliasframe_t *)datapointer;
906
907                 strlcpy(scene->name, pinframe->name, sizeof(scene->name));
908                 scene->firstframe = pose;
909                 scene->framecount = groupframes;
910                 scene->framerate = 1.0f / interval;
911                 scene->loop = true;
912                 scene++;
913
914                 // read frames
915                 for (i = 0;i < groupframes;i++)
916                 {
917                         datapointer += sizeof(daliasframe_t);
918                         Mod_ConvertAliasVerts(inverts, (trivertx_t *)datapointer, loadmodel->surfmesh.data_morphmdlvertex + pose * loadmodel->surfmesh.num_vertices, vertremap);
919                         datapointer += sizeof(trivertx_t) * inverts;
920                         pose++;
921                 }
922         }
923 }
924
925 void Mod_BuildAliasSkinsFromSkinFiles(texture_t *skin, skinfile_t *skinfile, const char *meshname, const char *shadername)
926 {
927         int i;
928         char stripbuf[MAX_QPATH];
929         skinfileitem_t *skinfileitem;
930         if(developer_extra.integer)
931                 Con_DPrintf("Looking up texture for %s (default: %s)\n", meshname, shadername);
932         if (skinfile)
933         {
934                 // the skin += loadmodel->num_surfaces part of this is because data_textures on alias models is arranged as [numskins][numsurfaces]
935                 for (i = 0;skinfile;skinfile = skinfile->next, i++, skin += loadmodel->num_surfaces)
936                 {
937                         memset(skin, 0, sizeof(*skin));
938                         // see if a mesh
939                         for (skinfileitem = skinfile->items;skinfileitem;skinfileitem = skinfileitem->next)
940                         {
941                                 // leave the skin unitialized (nodraw) if the replacement is "common/nodraw" or "textures/common/nodraw"
942                                 if (!strcmp(skinfileitem->name, meshname))
943                                 {
944                                         Image_StripImageExtension(skinfileitem->replacement, stripbuf, sizeof(stripbuf));
945                                         if(developer_extra.integer)
946                                                 Con_DPrintf("--> got %s from skin file\n", stripbuf);
947                                         Mod_LoadTextureFromQ3Shader(loadmodel->mempool, loadmodel->name, skin, stripbuf, true, true, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PICMIP | TEXF_COMPRESS, MATERIALFLAG_WALL);
948                                         break;
949                                 }
950                         }
951                         if (!skinfileitem)
952                         {
953                                 // don't render unmentioned meshes
954                                 Mod_LoadCustomMaterial(loadmodel->mempool, skin, meshname, SUPERCONTENTS_SOLID, MATERIALFLAG_WALL, R_SkinFrame_LoadMissing());
955                                 if(developer_extra.integer)
956                                         Con_DPrintf("--> skipping\n");
957                                 skin->basematerialflags = skin->currentmaterialflags = MATERIALFLAG_NOSHADOW | MATERIALFLAG_NODRAW;
958                         }
959                 }
960         }
961         else
962         {
963                 if(developer_extra.integer)
964                         Con_DPrintf("--> using default\n");
965                 Image_StripImageExtension(shadername, stripbuf, sizeof(stripbuf));
966                 Mod_LoadTextureFromQ3Shader(loadmodel->mempool, loadmodel->name, skin, stripbuf, true, true, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PICMIP | TEXF_COMPRESS, MATERIALFLAG_WALL);
967         }
968 }
969
970 #define BOUNDI(VALUE,MIN,MAX) if (VALUE < MIN || VALUE >= MAX) Host_Error("model %s has an invalid ##VALUE (%d exceeds %d - %d)", loadmodel->name, VALUE, MIN, MAX);
971 #define BOUNDF(VALUE,MIN,MAX) if (VALUE < MIN || VALUE >= MAX) Host_Error("model %s has an invalid ##VALUE (%f exceeds %f - %f)", loadmodel->name, VALUE, MIN, MAX);
972 void Mod_IDP0_Load(dp_model_t *mod, void *buffer, void *bufferend)
973 {
974         int i, j, version, totalskins, skinwidth, skinheight, groupframes, groupskins, numverts;
975         float scales, scalet, interval;
976         msurface_t *surface;
977         unsigned char *data;
978         mdl_t *pinmodel;
979         stvert_t *pinstverts;
980         dtriangle_t *pintriangles;
981         daliasskintype_t *pinskintype;
982         daliasskingroup_t *pinskingroup;
983         daliasskininterval_t *pinskinintervals;
984         daliasframetype_t *pinframetype;
985         daliasgroup_t *pinframegroup;
986         unsigned char *datapointer, *startframes, *startskins;
987         char name[MAX_QPATH];
988         skinframe_t *tempskinframe;
989         animscene_t *tempskinscenes;
990         texture_t *tempaliasskins;
991         float *vertst;
992         int *vertonseam, *vertremap;
993         skinfile_t *skinfiles;
994
995         datapointer = (unsigned char *)buffer;
996         pinmodel = (mdl_t *)datapointer;
997         datapointer += sizeof(mdl_t);
998
999         version = LittleLong (pinmodel->version);
1000         if (version != ALIAS_VERSION)
1001                 Host_Error ("%s has wrong version number (%i should be %i)",
1002                                  loadmodel->name, version, ALIAS_VERSION);
1003
1004         loadmodel->modeldatatypestring = "MDL";
1005
1006         loadmodel->type = mod_alias;
1007         loadmodel->DrawSky = NULL;
1008         loadmodel->DrawAddWaterPlanes = NULL;
1009         loadmodel->Draw = R_Q1BSP_Draw;
1010         loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
1011         loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
1012         loadmodel->DrawPrepass = R_Q1BSP_DrawPrepass;
1013         loadmodel->CompileShadowMap = R_Q1BSP_CompileShadowMap;
1014         loadmodel->DrawShadowMap = R_Q1BSP_DrawShadowMap;
1015         loadmodel->DrawLight = R_Q1BSP_DrawLight;
1016         loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
1017         loadmodel->TraceLine = Mod_MDLMD2MD3_TraceLine;
1018         // FIXME add TraceBrush!
1019         loadmodel->PointSuperContents = NULL;
1020         loadmodel->AnimateVertices = Mod_MDL_AnimateVertices;
1021
1022         loadmodel->num_surfaces = 1;
1023         loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
1024         data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int));
1025         loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
1026         loadmodel->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
1027         loadmodel->sortedmodelsurfaces[0] = 0;
1028
1029         loadmodel->numskins = LittleLong(pinmodel->numskins);
1030         BOUNDI(loadmodel->numskins,0,65536);
1031         skinwidth = LittleLong (pinmodel->skinwidth);
1032         BOUNDI(skinwidth,0,65536);
1033         skinheight = LittleLong (pinmodel->skinheight);
1034         BOUNDI(skinheight,0,65536);
1035         numverts = LittleLong(pinmodel->numverts);
1036         BOUNDI(numverts,0,65536);
1037         loadmodel->surfmesh.num_triangles = LittleLong(pinmodel->numtris);
1038         BOUNDI(loadmodel->surfmesh.num_triangles,0,65536);
1039         loadmodel->numframes = LittleLong(pinmodel->numframes);
1040         BOUNDI(loadmodel->numframes,0,65536);
1041         loadmodel->synctype = (synctype_t)LittleLong (pinmodel->synctype);
1042         BOUNDI((int)loadmodel->synctype,0,2);
1043         // convert model flags to EF flags (MF_ROCKET becomes EF_ROCKET, etc)
1044         i = LittleLong (pinmodel->flags);
1045         loadmodel->effects = ((i & 255) << 24) | (i & 0x00FFFF00);
1046
1047         for (i = 0;i < 3;i++)
1048         {
1049                 loadmodel->surfmesh.num_morphmdlframescale[i] = LittleFloat (pinmodel->scale[i]);
1050                 loadmodel->surfmesh.num_morphmdlframetranslate[i] = LittleFloat (pinmodel->scale_origin[i]);
1051         }
1052
1053         startskins = datapointer;
1054         totalskins = 0;
1055         for (i = 0;i < loadmodel->numskins;i++)
1056         {
1057                 pinskintype = (daliasskintype_t *)datapointer;
1058                 datapointer += sizeof(daliasskintype_t);
1059                 if (LittleLong(pinskintype->type) == ALIAS_SKIN_SINGLE)
1060                         groupskins = 1;
1061                 else
1062                 {
1063                         pinskingroup = (daliasskingroup_t *)datapointer;
1064                         datapointer += sizeof(daliasskingroup_t);
1065                         groupskins = LittleLong(pinskingroup->numskins);
1066                         datapointer += sizeof(daliasskininterval_t) * groupskins;
1067                 }
1068
1069                 for (j = 0;j < groupskins;j++)
1070                 {
1071                         datapointer += skinwidth * skinheight;
1072                         totalskins++;
1073                 }
1074         }
1075
1076         pinstverts = (stvert_t *)datapointer;
1077         datapointer += sizeof(stvert_t) * numverts;
1078
1079         pintriangles = (dtriangle_t *)datapointer;
1080         datapointer += sizeof(dtriangle_t) * loadmodel->surfmesh.num_triangles;
1081
1082         startframes = datapointer;
1083         loadmodel->surfmesh.num_morphframes = 0;
1084         for (i = 0;i < loadmodel->numframes;i++)
1085         {
1086                 pinframetype = (daliasframetype_t *)datapointer;
1087                 datapointer += sizeof(daliasframetype_t);
1088                 if (LittleLong (pinframetype->type) == ALIAS_SINGLE)
1089                         groupframes = 1;
1090                 else
1091                 {
1092                         pinframegroup = (daliasgroup_t *)datapointer;
1093                         datapointer += sizeof(daliasgroup_t);
1094                         groupframes = LittleLong(pinframegroup->numframes);
1095                         datapointer += sizeof(daliasinterval_t) * groupframes;
1096                 }
1097
1098                 for (j = 0;j < groupframes;j++)
1099                 {
1100                         datapointer += sizeof(daliasframe_t);
1101                         datapointer += sizeof(trivertx_t) * numverts;
1102                         loadmodel->surfmesh.num_morphframes++;
1103                 }
1104         }
1105         loadmodel->num_poses = loadmodel->surfmesh.num_morphframes;
1106
1107         // store texture coordinates into temporary array, they will be stored
1108         // after usage is determined (triangle data)
1109         vertst = (float *)Mem_Alloc(tempmempool, numverts * 2 * sizeof(float[2]));
1110         vertremap = (int *)Mem_Alloc(tempmempool, numverts * 3 * sizeof(int));
1111         vertonseam = vertremap + numverts * 2;
1112
1113         scales = 1.0 / skinwidth;
1114         scalet = 1.0 / skinheight;
1115         for (i = 0;i < numverts;i++)
1116         {
1117                 vertonseam[i] = LittleLong(pinstverts[i].onseam);
1118                 vertst[i*2+0] = LittleLong(pinstverts[i].s) * scales;
1119                 vertst[i*2+1] = LittleLong(pinstverts[i].t) * scalet;
1120                 vertst[(i+numverts)*2+0] = vertst[i*2+0] + 0.5;
1121                 vertst[(i+numverts)*2+1] = vertst[i*2+1];
1122         }
1123
1124 // load triangle data
1125         loadmodel->surfmesh.data_element3i = (int *)Mem_Alloc(loadmodel->mempool, sizeof(int[3]) * loadmodel->surfmesh.num_triangles);
1126
1127         // read the triangle elements
1128         for (i = 0;i < loadmodel->surfmesh.num_triangles;i++)
1129                 for (j = 0;j < 3;j++)
1130                         loadmodel->surfmesh.data_element3i[i*3+j] = LittleLong(pintriangles[i].vertindex[j]);
1131         // validate (note numverts is used because this is the original data)
1132         Mod_ValidateElements(loadmodel->surfmesh.data_element3i, NULL, loadmodel->surfmesh.num_triangles, 0, numverts, __FILE__, __LINE__);
1133         // now butcher the elements according to vertonseam and tri->facesfront
1134         // and then compact the vertex set to remove duplicates
1135         for (i = 0;i < loadmodel->surfmesh.num_triangles;i++)
1136                 if (!LittleLong(pintriangles[i].facesfront)) // backface
1137                         for (j = 0;j < 3;j++)
1138                                 if (vertonseam[loadmodel->surfmesh.data_element3i[i*3+j]])
1139                                         loadmodel->surfmesh.data_element3i[i*3+j] += numverts;
1140         // count the usage
1141         // (this uses vertremap to count usage to save some memory)
1142         for (i = 0;i < numverts*2;i++)
1143                 vertremap[i] = 0;
1144         for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
1145                 vertremap[loadmodel->surfmesh.data_element3i[i]]++;
1146         // build remapping table and compact array
1147         loadmodel->surfmesh.num_vertices = 0;
1148         for (i = 0;i < numverts*2;i++)
1149         {
1150                 if (vertremap[i])
1151                 {
1152                         vertremap[i] = loadmodel->surfmesh.num_vertices;
1153                         vertst[loadmodel->surfmesh.num_vertices*2+0] = vertst[i*2+0];
1154                         vertst[loadmodel->surfmesh.num_vertices*2+1] = vertst[i*2+1];
1155                         loadmodel->surfmesh.num_vertices++;
1156                 }
1157                 else
1158                         vertremap[i] = -1; // not used at all
1159         }
1160         // remap the elements to the new vertex set
1161         for (i = 0;i < loadmodel->surfmesh.num_triangles * 3;i++)
1162                 loadmodel->surfmesh.data_element3i[i] = vertremap[loadmodel->surfmesh.data_element3i[i]];
1163         // store the texture coordinates
1164         loadmodel->surfmesh.data_texcoordtexture2f = (float *)Mem_Alloc(loadmodel->mempool, sizeof(float[2]) * loadmodel->surfmesh.num_vertices);
1165         for (i = 0;i < loadmodel->surfmesh.num_vertices;i++)
1166         {
1167                 loadmodel->surfmesh.data_texcoordtexture2f[i*2+0] = vertst[i*2+0];
1168                 loadmodel->surfmesh.data_texcoordtexture2f[i*2+1] = vertst[i*2+1];
1169         }
1170
1171         // generate ushort elements array if possible
1172         if (loadmodel->surfmesh.num_vertices <= 65536)
1173                 loadmodel->surfmesh.data_element3s = (unsigned short *)Mem_Alloc(loadmodel->mempool, sizeof(unsigned short[3]) * loadmodel->surfmesh.num_triangles);
1174         if (loadmodel->surfmesh.data_element3s)
1175                 for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
1176                         loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
1177
1178 // load the frames
1179         loadmodel->animscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, sizeof(animscene_t) * loadmodel->numframes);
1180         loadmodel->surfmesh.data_morphmdlvertex = (trivertx_t *)Mem_Alloc(loadmodel->mempool, sizeof(trivertx_t) * loadmodel->surfmesh.num_morphframes * loadmodel->surfmesh.num_vertices);
1181         Mod_MDL_LoadFrames (startframes, numverts, vertremap);
1182         loadmodel->surfmesh.isanimated = Mod_Alias_CalculateBoundingBox();
1183         Mod_Alias_MorphMesh_CompileFrames();
1184
1185         Mem_Free(vertst);
1186         Mem_Free(vertremap);
1187
1188         // load the skins
1189         skinfiles = Mod_LoadSkinFiles();
1190         if (skinfiles)
1191         {
1192                 loadmodel->skinscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, loadmodel->numskins * sizeof(animscene_t));
1193                 loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
1194                 loadmodel->num_texturesperskin = loadmodel->num_surfaces;
1195                 loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
1196                 Mod_BuildAliasSkinsFromSkinFiles(loadmodel->data_textures, skinfiles, "default", "");
1197                 Mod_FreeSkinFiles(skinfiles);
1198                 for (i = 0;i < loadmodel->numskins;i++)
1199                 {
1200                         loadmodel->skinscenes[i].firstframe = i;
1201                         loadmodel->skinscenes[i].framecount = 1;
1202                         loadmodel->skinscenes[i].loop = true;
1203                         loadmodel->skinscenes[i].framerate = 10;
1204                 }
1205         }
1206         else
1207         {
1208                 loadmodel->skinscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, loadmodel->numskins * sizeof(animscene_t));
1209                 loadmodel->num_textures = loadmodel->num_surfaces * totalskins;
1210                 loadmodel->num_texturesperskin = loadmodel->num_surfaces;
1211                 loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * totalskins * sizeof(texture_t));
1212                 totalskins = 0;
1213                 datapointer = startskins;
1214                 for (i = 0;i < loadmodel->numskins;i++)
1215                 {
1216                         pinskintype = (daliasskintype_t *)datapointer;
1217                         datapointer += sizeof(daliasskintype_t);
1218
1219                         if (pinskintype->type == ALIAS_SKIN_SINGLE)
1220                         {
1221                                 groupskins = 1;
1222                                 interval = 0.1f;
1223                         }
1224                         else
1225                         {
1226                                 pinskingroup = (daliasskingroup_t *)datapointer;
1227                                 datapointer += sizeof(daliasskingroup_t);
1228
1229                                 groupskins = LittleLong (pinskingroup->numskins);
1230
1231                                 pinskinintervals = (daliasskininterval_t *)datapointer;
1232                                 datapointer += sizeof(daliasskininterval_t) * groupskins;
1233
1234                                 interval = LittleFloat(pinskinintervals[0].interval);
1235                                 if (interval < 0.01f)
1236                                 {
1237                                         Con_Printf("%s has an invalid interval %f, changing to 0.1\n", loadmodel->name, interval);
1238                                         interval = 0.1f;
1239                                 }
1240                         }
1241
1242                         dpsnprintf(loadmodel->skinscenes[i].name, sizeof(loadmodel->skinscenes[i].name), "skin %i", i);
1243                         loadmodel->skinscenes[i].firstframe = totalskins;
1244                         loadmodel->skinscenes[i].framecount = groupskins;
1245                         loadmodel->skinscenes[i].framerate = 1.0f / interval;
1246                         loadmodel->skinscenes[i].loop = true;
1247
1248                         for (j = 0;j < groupskins;j++)
1249                         {
1250                                 if (groupskins > 1)
1251                                         dpsnprintf (name, sizeof(name), "%s_%i_%i", loadmodel->name, i, j);
1252                                 else
1253                                         dpsnprintf (name, sizeof(name), "%s_%i", loadmodel->name, i);
1254                                 if (!Mod_LoadTextureFromQ3Shader(loadmodel->mempool, loadmodel->name, loadmodel->data_textures + totalskins * loadmodel->num_surfaces, name, false, false, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PICMIP | TEXF_COMPRESS, MATERIALFLAG_WALL))
1255                                         Mod_LoadCustomMaterial(loadmodel->mempool, loadmodel->data_textures + totalskins * loadmodel->num_surfaces, name, SUPERCONTENTS_SOLID, MATERIALFLAG_WALL, R_SkinFrame_LoadInternalQuake(name, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_PICMIP, true, r_fullbrights.integer, (unsigned char *)datapointer, skinwidth, skinheight));
1256                                 datapointer += skinwidth * skinheight;
1257                                 totalskins++;
1258                         }
1259                 }
1260                 // check for skins that don't exist in the model, but do exist as external images
1261                 // (this was added because yummyluv kept pestering me about support for it)
1262                 // TODO: support shaders here?
1263                 for (;;)
1264                 {
1265                         dpsnprintf(name, sizeof(name), "%s_%i", loadmodel->name, loadmodel->numskins);
1266                         tempskinframe = R_SkinFrame_LoadExternal(name, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PICMIP | TEXF_COMPRESS, false, false);
1267                         if (!tempskinframe)
1268                                 break;
1269                         // expand the arrays to make room
1270                         tempskinscenes = loadmodel->skinscenes;
1271                         loadmodel->skinscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, (loadmodel->numskins + 1) * sizeof(animscene_t));
1272                         memcpy(loadmodel->skinscenes, tempskinscenes, loadmodel->numskins * sizeof(animscene_t));
1273                         Mem_Free(tempskinscenes);
1274
1275                         tempaliasskins = loadmodel->data_textures;
1276                         loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * (totalskins + 1) * sizeof(texture_t));
1277                         memcpy(loadmodel->data_textures, tempaliasskins, loadmodel->num_surfaces * totalskins * sizeof(texture_t));
1278                         Mem_Free(tempaliasskins);
1279
1280                         // store the info about the new skin
1281                         Mod_LoadCustomMaterial(loadmodel->mempool, loadmodel->data_textures + totalskins * loadmodel->num_surfaces, name, SUPERCONTENTS_SOLID, MATERIALFLAG_WALL, tempskinframe);
1282                         strlcpy(loadmodel->skinscenes[loadmodel->numskins].name, name, sizeof(loadmodel->skinscenes[loadmodel->numskins].name));
1283                         loadmodel->skinscenes[loadmodel->numskins].firstframe = totalskins;
1284                         loadmodel->skinscenes[loadmodel->numskins].framecount = 1;
1285                         loadmodel->skinscenes[loadmodel->numskins].framerate = 10.0f;
1286                         loadmodel->skinscenes[loadmodel->numskins].loop = true;
1287
1288                         //increase skin counts
1289                         loadmodel->num_textures++;
1290                         loadmodel->numskins++;
1291                         totalskins++;
1292
1293                         // fix up the pointers since they are pointing at the old textures array
1294                         // FIXME: this is a hack!
1295                         for (j = 0;j < loadmodel->numskins * loadmodel->num_surfaces;j++)
1296                                 loadmodel->data_textures[j].currentframe = &loadmodel->data_textures[j];
1297                 }
1298         }
1299
1300         surface = loadmodel->data_surfaces;
1301         surface->texture = loadmodel->data_textures;
1302         surface->num_firsttriangle = 0;
1303         surface->num_triangles = loadmodel->surfmesh.num_triangles;
1304         surface->num_firstvertex = 0;
1305         surface->num_vertices = loadmodel->surfmesh.num_vertices;
1306
1307         if(mod_alias_force_animated.string[0])
1308                 loadmodel->surfmesh.isanimated = mod_alias_force_animated.integer != 0;
1309
1310         // Always make a BIH for the first frame, we can use it where possible.
1311         Mod_MakeCollisionBIH(loadmodel, true, &loadmodel->collision_bih);
1312         if (!loadmodel->surfmesh.isanimated)
1313         {
1314                 loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
1315                 loadmodel->TraceBrush = Mod_CollisionBIH_TraceBrush;
1316                 loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
1317                 loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
1318                 loadmodel->PointSuperContents = Mod_CollisionBIH_PointSuperContents_Mesh;
1319         }
1320
1321         // because shaders can do somewhat unexpected things, check for unusual features now
1322         for (i = 0;i < loadmodel->num_textures;i++)
1323         {
1324                 if (loadmodel->data_textures[i].basematerialflags & (MATERIALFLAG_SKY))
1325                         mod->DrawSky = R_Q1BSP_DrawSky;
1326                 if (loadmodel->data_textures[i].basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
1327                         mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
1328         }
1329 }
1330
1331 void Mod_IDP2_Load(dp_model_t *mod, void *buffer, void *bufferend)
1332 {
1333         int i, j, hashindex, numxyz, numst, xyz, st, skinwidth, skinheight, *vertremap, version, end;
1334         float iskinwidth, iskinheight;
1335         unsigned char *data;
1336         msurface_t *surface;
1337         md2_t *pinmodel;
1338         unsigned char *base, *datapointer;
1339         md2frame_t *pinframe;
1340         char *inskin;
1341         md2triangle_t *intri;
1342         unsigned short *inst;
1343         struct md2verthash_s
1344         {
1345                 struct md2verthash_s *next;
1346                 unsigned short xyz;
1347                 unsigned short st;
1348         }
1349         *hash, **md2verthash, *md2verthashdata;
1350         skinfile_t *skinfiles;
1351
1352         pinmodel = (md2_t *)buffer;
1353         base = (unsigned char *)buffer;
1354
1355         version = LittleLong (pinmodel->version);
1356         if (version != MD2ALIAS_VERSION)
1357                 Host_Error ("%s has wrong version number (%i should be %i)",
1358                         loadmodel->name, version, MD2ALIAS_VERSION);
1359
1360         loadmodel->modeldatatypestring = "MD2";
1361
1362         loadmodel->type = mod_alias;
1363         loadmodel->DrawSky = NULL;
1364         loadmodel->DrawAddWaterPlanes = NULL;
1365         loadmodel->Draw = R_Q1BSP_Draw;
1366         loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
1367         loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
1368         loadmodel->DrawPrepass = R_Q1BSP_DrawPrepass;
1369         loadmodel->CompileShadowMap = R_Q1BSP_CompileShadowMap;
1370         loadmodel->DrawShadowMap = R_Q1BSP_DrawShadowMap;
1371         loadmodel->DrawLight = R_Q1BSP_DrawLight;
1372         loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
1373         loadmodel->TraceLine = Mod_MDLMD2MD3_TraceLine;
1374         loadmodel->PointSuperContents = NULL;
1375         loadmodel->AnimateVertices = Mod_MDL_AnimateVertices;
1376
1377         if (LittleLong(pinmodel->num_tris) < 1 || LittleLong(pinmodel->num_tris) > 65536)
1378                 Host_Error ("%s has invalid number of triangles: %i", loadmodel->name, LittleLong(pinmodel->num_tris));
1379         if (LittleLong(pinmodel->num_xyz) < 1 || LittleLong(pinmodel->num_xyz) > 65536)
1380                 Host_Error ("%s has invalid number of vertices: %i", loadmodel->name, LittleLong(pinmodel->num_xyz));
1381         if (LittleLong(pinmodel->num_frames) < 1 || LittleLong(pinmodel->num_frames) > 65536)
1382                 Host_Error ("%s has invalid number of frames: %i", loadmodel->name, LittleLong(pinmodel->num_frames));
1383         if (LittleLong(pinmodel->num_skins) < 0 || LittleLong(pinmodel->num_skins) > 256)
1384                 Host_Error ("%s has invalid number of skins: %i", loadmodel->name, LittleLong(pinmodel->num_skins));
1385
1386         end = LittleLong(pinmodel->ofs_end);
1387         if (LittleLong(pinmodel->num_skins) >= 1 && (LittleLong(pinmodel->ofs_skins) <= 0 || LittleLong(pinmodel->ofs_skins) >= end))
1388                 Host_Error ("%s is not a valid model", loadmodel->name);
1389         if (LittleLong(pinmodel->ofs_st) <= 0 || LittleLong(pinmodel->ofs_st) >= end)
1390                 Host_Error ("%s is not a valid model", loadmodel->name);
1391         if (LittleLong(pinmodel->ofs_tris) <= 0 || LittleLong(pinmodel->ofs_tris) >= end)
1392                 Host_Error ("%s is not a valid model", loadmodel->name);
1393         if (LittleLong(pinmodel->ofs_frames) <= 0 || LittleLong(pinmodel->ofs_frames) >= end)
1394                 Host_Error ("%s is not a valid model", loadmodel->name);
1395         if (LittleLong(pinmodel->ofs_glcmds) <= 0 || LittleLong(pinmodel->ofs_glcmds) >= end)
1396                 Host_Error ("%s is not a valid model", loadmodel->name);
1397
1398         loadmodel->numskins = LittleLong(pinmodel->num_skins);
1399         numxyz = LittleLong(pinmodel->num_xyz);
1400         numst = LittleLong(pinmodel->num_st);
1401         loadmodel->surfmesh.num_triangles = LittleLong(pinmodel->num_tris);
1402         loadmodel->numframes = LittleLong(pinmodel->num_frames);
1403         loadmodel->surfmesh.num_morphframes = loadmodel->numframes;
1404         loadmodel->num_poses = loadmodel->surfmesh.num_morphframes;
1405         skinwidth = LittleLong(pinmodel->skinwidth);
1406         skinheight = LittleLong(pinmodel->skinheight);
1407         iskinwidth = 1.0f / skinwidth;
1408         iskinheight = 1.0f / skinheight;
1409
1410         loadmodel->num_surfaces = 1;
1411         loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
1412         data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->numframes * sizeof(animscene_t) + loadmodel->numframes * sizeof(float[6]) + loadmodel->surfmesh.num_triangles * sizeof(int[3]));
1413         loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
1414         loadmodel->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
1415         loadmodel->sortedmodelsurfaces[0] = 0;
1416         loadmodel->animscenes = (animscene_t *)data;data += loadmodel->numframes * sizeof(animscene_t);
1417         loadmodel->surfmesh.data_morphmd2framesize6f = (float *)data;data += loadmodel->numframes * sizeof(float[6]);
1418         loadmodel->surfmesh.data_element3i = (int *)data;data += loadmodel->surfmesh.num_triangles * sizeof(int[3]);
1419
1420         loadmodel->synctype = ST_RAND;
1421
1422         // load the skins
1423         inskin = (char *)(base + LittleLong(pinmodel->ofs_skins));
1424         skinfiles = Mod_LoadSkinFiles();
1425         if (skinfiles)
1426         {
1427                 loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
1428                 loadmodel->num_texturesperskin = loadmodel->num_surfaces;
1429                 loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
1430                 Mod_BuildAliasSkinsFromSkinFiles(loadmodel->data_textures, skinfiles, "default", "");
1431                 Mod_FreeSkinFiles(skinfiles);
1432         }
1433         else if (loadmodel->numskins)
1434         {
1435                 // skins found (most likely not a player model)
1436                 loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
1437                 loadmodel->num_texturesperskin = loadmodel->num_surfaces;
1438                 loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
1439                 for (i = 0;i < loadmodel->numskins;i++, inskin += MD2_SKINNAME)
1440                         Mod_LoadTextureFromQ3Shader(loadmodel->mempool, loadmodel->name, loadmodel->data_textures + i * loadmodel->num_surfaces, inskin, true, true, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PICMIP | TEXF_COMPRESS, MATERIALFLAG_WALL);
1441         }
1442         else
1443         {
1444                 // no skins (most likely a player model)
1445                 loadmodel->numskins = 1;
1446                 loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
1447                 loadmodel->num_texturesperskin = loadmodel->num_surfaces;
1448                 loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
1449                 Mod_LoadCustomMaterial(loadmodel->mempool, loadmodel->data_textures, loadmodel->name, SUPERCONTENTS_SOLID, MATERIALFLAG_WALL, R_SkinFrame_LoadMissing());
1450         }
1451
1452         loadmodel->skinscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, sizeof(animscene_t) * loadmodel->numskins);
1453         for (i = 0;i < loadmodel->numskins;i++)
1454         {
1455                 loadmodel->skinscenes[i].firstframe = i;
1456                 loadmodel->skinscenes[i].framecount = 1;
1457                 loadmodel->skinscenes[i].loop = true;
1458                 loadmodel->skinscenes[i].framerate = 10;
1459         }
1460
1461         // load the triangles and stvert data
1462         inst = (unsigned short *)(base + LittleLong(pinmodel->ofs_st));
1463         intri = (md2triangle_t *)(base + LittleLong(pinmodel->ofs_tris));
1464         md2verthash = (struct md2verthash_s **)Mem_Alloc(tempmempool, 65536 * sizeof(hash));
1465         md2verthashdata = (struct md2verthash_s *)Mem_Alloc(tempmempool, loadmodel->surfmesh.num_triangles * 3 * sizeof(*hash));
1466         // swap the triangle list
1467         loadmodel->surfmesh.num_vertices = 0;
1468         for (i = 0;i < loadmodel->surfmesh.num_triangles;i++)
1469         {
1470                 for (j = 0;j < 3;j++)
1471                 {
1472                         xyz = (unsigned short) LittleShort (intri[i].index_xyz[j]);
1473                         st = (unsigned short) LittleShort (intri[i].index_st[j]);
1474                         if (xyz >= numxyz)
1475                         {
1476                                 Con_Printf("%s has an invalid xyz index (%i) on triangle %i, resetting to 0\n", loadmodel->name, xyz, i);
1477                                 xyz = 0;
1478                         }
1479                         if (st >= numst)
1480                         {
1481                                 Con_Printf("%s has an invalid st index (%i) on triangle %i, resetting to 0\n", loadmodel->name, st, i);
1482                                 st = 0;
1483                         }
1484                         hashindex = (xyz * 256 + st) & 65535;
1485                         for (hash = md2verthash[hashindex];hash;hash = hash->next)
1486                                 if (hash->xyz == xyz && hash->st == st)
1487                                         break;
1488                         if (hash == NULL)
1489                         {
1490                                 hash = md2verthashdata + loadmodel->surfmesh.num_vertices++;
1491                                 hash->xyz = xyz;
1492                                 hash->st = st;
1493                                 hash->next = md2verthash[hashindex];
1494                                 md2verthash[hashindex] = hash;
1495                         }
1496                         loadmodel->surfmesh.data_element3i[i*3+j] = (hash - md2verthashdata);
1497                 }
1498         }
1499
1500         vertremap = (int *)Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_vertices * sizeof(int));
1501         data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_vertices * sizeof(float[2]) + loadmodel->surfmesh.num_vertices * loadmodel->surfmesh.num_morphframes * sizeof(trivertx_t));
1502         loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
1503         loadmodel->surfmesh.data_morphmdlvertex = (trivertx_t *)data;data += loadmodel->surfmesh.num_vertices * loadmodel->surfmesh.num_morphframes * sizeof(trivertx_t);
1504         for (i = 0;i < loadmodel->surfmesh.num_vertices;i++)
1505         {
1506                 int sts, stt;
1507                 hash = md2verthashdata + i;
1508                 vertremap[i] = hash->xyz;
1509                 sts = LittleShort(inst[hash->st*2+0]);
1510                 stt = LittleShort(inst[hash->st*2+1]);
1511                 if (sts < 0 || sts >= skinwidth || stt < 0 || stt >= skinheight)
1512                 {
1513                         Con_Printf("%s has an invalid skin coordinate (%i %i) on vert %i, changing to 0 0\n", loadmodel->name, sts, stt, i);
1514                         sts = 0;
1515                         stt = 0;
1516                 }
1517                 loadmodel->surfmesh.data_texcoordtexture2f[i*2+0] = sts * iskinwidth;
1518                 loadmodel->surfmesh.data_texcoordtexture2f[i*2+1] = stt * iskinheight;
1519         }
1520
1521         Mem_Free(md2verthash);
1522         Mem_Free(md2verthashdata);
1523
1524         // generate ushort elements array if possible
1525         if (loadmodel->surfmesh.num_vertices <= 65536)
1526                 loadmodel->surfmesh.data_element3s = (unsigned short *)Mem_Alloc(loadmodel->mempool, sizeof(unsigned short[3]) * loadmodel->surfmesh.num_triangles);
1527         if (loadmodel->surfmesh.data_element3s)
1528                 for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
1529                         loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
1530
1531         // load the frames
1532         datapointer = (base + LittleLong(pinmodel->ofs_frames));
1533         for (i = 0;i < loadmodel->surfmesh.num_morphframes;i++)
1534         {
1535                 int k;
1536                 trivertx_t *v;
1537                 trivertx_t *out;
1538                 pinframe = (md2frame_t *)datapointer;
1539                 datapointer += sizeof(md2frame_t);
1540                 // store the frame scale/translate into the appropriate array
1541                 for (j = 0;j < 3;j++)
1542                 {
1543                         loadmodel->surfmesh.data_morphmd2framesize6f[i*6+j] = LittleFloat(pinframe->scale[j]);
1544                         loadmodel->surfmesh.data_morphmd2framesize6f[i*6+3+j] = LittleFloat(pinframe->translate[j]);
1545                 }
1546                 // convert the vertices
1547                 v = (trivertx_t *)datapointer;
1548                 out = loadmodel->surfmesh.data_morphmdlvertex + i * loadmodel->surfmesh.num_vertices;
1549                 for (k = 0;k < loadmodel->surfmesh.num_vertices;k++)
1550                         out[k] = v[vertremap[k]];
1551                 datapointer += numxyz * sizeof(trivertx_t);
1552
1553                 strlcpy(loadmodel->animscenes[i].name, pinframe->name, sizeof(loadmodel->animscenes[i].name));
1554                 loadmodel->animscenes[i].firstframe = i;
1555                 loadmodel->animscenes[i].framecount = 1;
1556                 loadmodel->animscenes[i].framerate = 10;
1557                 loadmodel->animscenes[i].loop = true;
1558         }
1559
1560         Mem_Free(vertremap);
1561
1562         loadmodel->surfmesh.isanimated = Mod_Alias_CalculateBoundingBox();
1563         Mod_Alias_MorphMesh_CompileFrames();
1564         if(mod_alias_force_animated.string[0])
1565                 loadmodel->surfmesh.isanimated = mod_alias_force_animated.integer != 0;
1566
1567         surface = loadmodel->data_surfaces;
1568         surface->texture = loadmodel->data_textures;
1569         surface->num_firsttriangle = 0;
1570         surface->num_triangles = loadmodel->surfmesh.num_triangles;
1571         surface->num_firstvertex = 0;
1572         surface->num_vertices = loadmodel->surfmesh.num_vertices;
1573
1574         // Always make a BIH for the first frame, we can use it where possible.
1575         Mod_MakeCollisionBIH(loadmodel, true, &loadmodel->collision_bih);
1576         if (!loadmodel->surfmesh.isanimated)
1577         {
1578                 loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
1579                 loadmodel->TraceBrush = Mod_CollisionBIH_TraceBrush;
1580                 loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
1581                 loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
1582                 loadmodel->PointSuperContents = Mod_CollisionBIH_PointSuperContents_Mesh;
1583         }
1584
1585         // because shaders can do somewhat unexpected things, check for unusual features now
1586         for (i = 0;i < loadmodel->num_textures;i++)
1587         {
1588                 if (loadmodel->data_textures[i].basematerialflags & (MATERIALFLAG_SKY))
1589                         mod->DrawSky = R_Q1BSP_DrawSky;
1590                 if (loadmodel->data_textures[i].basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
1591                         mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
1592         }
1593 }
1594
1595 void Mod_IDP3_Load(dp_model_t *mod, void *buffer, void *bufferend)
1596 {
1597         int i, j, k, version, meshvertices, meshtriangles;
1598         unsigned char *data;
1599         msurface_t *surface;
1600         md3modelheader_t *pinmodel;
1601         md3frameinfo_t *pinframe;
1602         md3mesh_t *pinmesh;
1603         md3tag_t *pintag;
1604         skinfile_t *skinfiles;
1605
1606         pinmodel = (md3modelheader_t *)buffer;
1607
1608         if (memcmp(pinmodel->identifier, "IDP3", 4))
1609                 Host_Error ("%s is not a MD3 (IDP3) file", loadmodel->name);
1610         version = LittleLong (pinmodel->version);
1611         if (version != MD3VERSION)
1612                 Host_Error ("%s has wrong version number (%i should be %i)",
1613                         loadmodel->name, version, MD3VERSION);
1614
1615         skinfiles = Mod_LoadSkinFiles();
1616         if (loadmodel->numskins < 1)
1617                 loadmodel->numskins = 1;
1618
1619         loadmodel->modeldatatypestring = "MD3";
1620
1621         loadmodel->type = mod_alias;
1622         loadmodel->DrawSky = NULL;
1623         loadmodel->DrawAddWaterPlanes = NULL;
1624         loadmodel->Draw = R_Q1BSP_Draw;
1625         loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
1626         loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
1627         loadmodel->DrawPrepass = R_Q1BSP_DrawPrepass;
1628         loadmodel->CompileShadowMap = R_Q1BSP_CompileShadowMap;
1629         loadmodel->DrawShadowMap = R_Q1BSP_DrawShadowMap;
1630         loadmodel->DrawLight = R_Q1BSP_DrawLight;
1631         loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
1632         loadmodel->TraceLine = Mod_MDLMD2MD3_TraceLine;
1633         loadmodel->PointSuperContents = NULL;
1634         loadmodel->AnimateVertices = Mod_MD3_AnimateVertices;
1635         loadmodel->synctype = ST_RAND;
1636         // convert model flags to EF flags (MF_ROCKET becomes EF_ROCKET, etc)
1637         i = LittleLong (pinmodel->flags);
1638         loadmodel->effects = ((i & 255) << 24) | (i & 0x00FFFF00);
1639
1640         // set up some global info about the model
1641         loadmodel->numframes = LittleLong(pinmodel->num_frames);
1642         loadmodel->num_surfaces = LittleLong(pinmodel->num_meshes);
1643
1644         // make skinscenes for the skins (no groups)
1645         loadmodel->skinscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, sizeof(animscene_t) * loadmodel->numskins);
1646         for (i = 0;i < loadmodel->numskins;i++)
1647         {
1648                 loadmodel->skinscenes[i].firstframe = i;
1649                 loadmodel->skinscenes[i].framecount = 1;
1650                 loadmodel->skinscenes[i].loop = true;
1651                 loadmodel->skinscenes[i].framerate = 10;
1652         }
1653
1654         // load frameinfo
1655         loadmodel->animscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, loadmodel->numframes * sizeof(animscene_t));
1656         for (i = 0, pinframe = (md3frameinfo_t *)((unsigned char *)pinmodel + LittleLong(pinmodel->lump_frameinfo));i < loadmodel->numframes;i++, pinframe++)
1657         {
1658                 strlcpy(loadmodel->animscenes[i].name, pinframe->name, sizeof(loadmodel->animscenes[i].name));
1659                 loadmodel->animscenes[i].firstframe = i;
1660                 loadmodel->animscenes[i].framecount = 1;
1661                 loadmodel->animscenes[i].framerate = 10;
1662                 loadmodel->animscenes[i].loop = true;
1663         }
1664
1665         // load tags
1666         loadmodel->num_tagframes = loadmodel->numframes;
1667         loadmodel->num_tags = LittleLong(pinmodel->num_tags);
1668         loadmodel->data_tags = (aliastag_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_tagframes * loadmodel->num_tags * sizeof(aliastag_t));
1669         for (i = 0, pintag = (md3tag_t *)((unsigned char *)pinmodel + LittleLong(pinmodel->lump_tags));i < loadmodel->num_tagframes * loadmodel->num_tags;i++, pintag++)
1670         {
1671                 strlcpy(loadmodel->data_tags[i].name, pintag->name, sizeof(loadmodel->data_tags[i].name));
1672                 for (j = 0;j < 9;j++)
1673                         loadmodel->data_tags[i].matrixgl[j] = LittleFloat(pintag->rotationmatrix[j]);
1674                 for (j = 0;j < 3;j++)
1675                         loadmodel->data_tags[i].matrixgl[9+j] = LittleFloat(pintag->origin[j]);
1676                 //Con_Printf("model \"%s\" frame #%i tag #%i \"%s\"\n", loadmodel->name, i / loadmodel->num_tags, i % loadmodel->num_tags, loadmodel->data_tags[i].name);
1677         }
1678
1679         // load meshes
1680         meshvertices = 0;
1681         meshtriangles = 0;
1682         for (i = 0, pinmesh = (md3mesh_t *)((unsigned char *)pinmodel + LittleLong(pinmodel->lump_meshes));i < loadmodel->num_surfaces;i++, pinmesh = (md3mesh_t *)((unsigned char *)pinmesh + LittleLong(pinmesh->lump_end)))
1683         {
1684                 if (memcmp(pinmesh->identifier, "IDP3", 4))
1685                         Host_Error("Mod_IDP3_Load: invalid mesh identifier (not IDP3)");
1686                 if (LittleLong(pinmesh->num_frames) != loadmodel->numframes)
1687                         Host_Error("Mod_IDP3_Load: mesh numframes differs from header");
1688                 meshvertices += LittleLong(pinmesh->num_vertices);
1689                 meshtriangles += LittleLong(pinmesh->num_triangles);
1690         }
1691
1692         loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
1693         loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
1694         loadmodel->num_texturesperskin = loadmodel->num_surfaces;
1695         data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t) + meshtriangles * sizeof(int[3]) + (meshvertices <= 65536 ? meshtriangles * sizeof(unsigned short[3]) : 0) + meshvertices * sizeof(float[2]) + meshvertices * loadmodel->numframes * sizeof(md3vertex_t));
1696         loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
1697         loadmodel->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
1698         loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
1699         loadmodel->surfmesh.num_vertices = meshvertices;
1700         loadmodel->surfmesh.num_triangles = meshtriangles;
1701         loadmodel->surfmesh.num_morphframes = loadmodel->numframes; // TODO: remove?
1702         loadmodel->num_poses = loadmodel->surfmesh.num_morphframes;
1703         loadmodel->surfmesh.data_element3i = (int *)data;data += meshtriangles * sizeof(int[3]);
1704         loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += meshvertices * sizeof(float[2]);
1705         loadmodel->surfmesh.data_morphmd3vertex = (md3vertex_t *)data;data += meshvertices * loadmodel->numframes * sizeof(md3vertex_t);
1706         if (meshvertices <= 65536)
1707         {
1708                 loadmodel->surfmesh.data_element3s = (unsigned short *)data;data += meshtriangles * sizeof(unsigned short[3]);
1709         }
1710
1711         meshvertices = 0;
1712         meshtriangles = 0;
1713         for (i = 0, pinmesh = (md3mesh_t *)((unsigned char *)pinmodel + LittleLong(pinmodel->lump_meshes));i < loadmodel->num_surfaces;i++, pinmesh = (md3mesh_t *)((unsigned char *)pinmesh + LittleLong(pinmesh->lump_end)))
1714         {
1715                 if (memcmp(pinmesh->identifier, "IDP3", 4))
1716                         Host_Error("Mod_IDP3_Load: invalid mesh identifier (not IDP3)");
1717                 loadmodel->sortedmodelsurfaces[i] = i;
1718                 surface = loadmodel->data_surfaces + i;
1719                 surface->texture = loadmodel->data_textures + i;
1720                 surface->num_firsttriangle = meshtriangles;
1721                 surface->num_triangles = LittleLong(pinmesh->num_triangles);
1722                 surface->num_firstvertex = meshvertices;
1723                 surface->num_vertices = LittleLong(pinmesh->num_vertices);
1724                 meshvertices += surface->num_vertices;
1725                 meshtriangles += surface->num_triangles;
1726
1727                 for (j = 0;j < surface->num_triangles * 3;j++)
1728                 {
1729                         int e = surface->num_firstvertex + LittleLong(((int *)((unsigned char *)pinmesh + LittleLong(pinmesh->lump_elements)))[j]);
1730                         loadmodel->surfmesh.data_element3i[j + surface->num_firsttriangle * 3] = e;
1731                         if (loadmodel->surfmesh.data_element3s)
1732                                 loadmodel->surfmesh.data_element3s[j + surface->num_firsttriangle * 3] = e;
1733                 }
1734                 for (j = 0;j < surface->num_vertices;j++)
1735                 {
1736                         loadmodel->surfmesh.data_texcoordtexture2f[(j + surface->num_firstvertex) * 2 + 0] = LittleFloat(((float *)((unsigned char *)pinmesh + LittleLong(pinmesh->lump_texcoords)))[j * 2 + 0]);
1737                         loadmodel->surfmesh.data_texcoordtexture2f[(j + surface->num_firstvertex) * 2 + 1] = LittleFloat(((float *)((unsigned char *)pinmesh + LittleLong(pinmesh->lump_texcoords)))[j * 2 + 1]);
1738                 }
1739                 for (j = 0;j < loadmodel->numframes;j++)
1740                 {
1741                         const md3vertex_t *in = (md3vertex_t *)((unsigned char *)pinmesh + LittleLong(pinmesh->lump_framevertices)) + j * surface->num_vertices;
1742                         md3vertex_t *out = loadmodel->surfmesh.data_morphmd3vertex + surface->num_firstvertex + j * loadmodel->surfmesh.num_vertices;
1743                         for (k = 0;k < surface->num_vertices;k++, in++, out++)
1744                         {
1745                                 out->origin[0] = LittleShort(in->origin[0]);
1746                                 out->origin[1] = LittleShort(in->origin[1]);
1747                                 out->origin[2] = LittleShort(in->origin[2]);
1748                                 out->pitch = in->pitch;
1749                                 out->yaw = in->yaw;
1750                         }
1751                 }
1752
1753                 Mod_BuildAliasSkinsFromSkinFiles(loadmodel->data_textures + i, skinfiles, pinmesh->name, LittleLong(pinmesh->num_shaders) >= 1 ? ((md3shader_t *)((unsigned char *) pinmesh + LittleLong(pinmesh->lump_shaders)))->name : "");
1754
1755                 Mod_ValidateElements(loadmodel->surfmesh.data_element3i + surface->num_firsttriangle * 3, loadmodel->surfmesh.data_element3s + surface->num_firsttriangle * 3, surface->num_triangles, surface->num_firstvertex, surface->num_vertices, __FILE__, __LINE__);
1756         }
1757         Mod_Alias_MorphMesh_CompileFrames();
1758         loadmodel->surfmesh.isanimated = Mod_Alias_CalculateBoundingBox();
1759         Mod_FreeSkinFiles(skinfiles);
1760         Mod_MakeSortedSurfaces(loadmodel);
1761         if(mod_alias_force_animated.string[0])
1762                 loadmodel->surfmesh.isanimated = mod_alias_force_animated.integer != 0;
1763
1764         // Always make a BIH for the first frame, we can use it where possible.
1765         Mod_MakeCollisionBIH(loadmodel, true, &loadmodel->collision_bih);
1766         if (!loadmodel->surfmesh.isanimated)
1767         {
1768                 loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
1769                 loadmodel->TraceBrush = Mod_CollisionBIH_TraceBrush;
1770                 loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
1771                 loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
1772                 loadmodel->PointSuperContents = Mod_CollisionBIH_PointSuperContents_Mesh;
1773         }
1774
1775         // because shaders can do somewhat unexpected things, check for unusual features now
1776         for (i = 0;i < loadmodel->num_textures;i++)
1777         {
1778                 if (loadmodel->data_textures[i].basematerialflags & (MATERIALFLAG_SKY))
1779                         mod->DrawSky = R_Q1BSP_DrawSky;
1780                 if (loadmodel->data_textures[i].basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
1781                         mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
1782         }
1783 }
1784
1785 void Mod_ZYMOTICMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
1786 {
1787         zymtype1header_t *pinmodel, *pheader;
1788         unsigned char *pbase;
1789         int i, j, k, numposes, meshvertices, meshtriangles, *bonecount, *vertbonecounts, count, *renderlist, *renderlistend, *outelements;
1790         float modelradius, corner[2], *poses, *intexcoord2f, *outtexcoord2f, *bonepose, f, biggestorigin, tempvec[3], modelscale;
1791         zymvertex_t *verts, *vertdata;
1792         zymscene_t *scene;
1793         zymbone_t *bone;
1794         char *shadername;
1795         skinfile_t *skinfiles;
1796         unsigned char *data;
1797         msurface_t *surface;
1798
1799         pinmodel = (zymtype1header_t *)buffer;
1800         pbase = (unsigned char *)buffer;
1801         if (memcmp(pinmodel->id, "ZYMOTICMODEL", 12))
1802                 Host_Error ("Mod_ZYMOTICMODEL_Load: %s is not a zymotic model", loadmodel->name);
1803         if (BigLong(pinmodel->type) != 1)
1804                 Host_Error ("Mod_ZYMOTICMODEL_Load: only type 1 (skeletal pose) models are currently supported (name = %s)", loadmodel->name);
1805
1806         loadmodel->modeldatatypestring = "ZYM";
1807
1808         loadmodel->type = mod_alias;
1809         loadmodel->synctype = ST_RAND;
1810
1811         // byteswap header
1812         pheader = pinmodel;
1813         pheader->type = BigLong(pinmodel->type);
1814         pheader->filesize = BigLong(pinmodel->filesize);
1815         pheader->mins[0] = BigFloat(pinmodel->mins[0]);
1816         pheader->mins[1] = BigFloat(pinmodel->mins[1]);
1817         pheader->mins[2] = BigFloat(pinmodel->mins[2]);
1818         pheader->maxs[0] = BigFloat(pinmodel->maxs[0]);
1819         pheader->maxs[1] = BigFloat(pinmodel->maxs[1]);
1820         pheader->maxs[2] = BigFloat(pinmodel->maxs[2]);
1821         pheader->radius = BigFloat(pinmodel->radius);
1822         pheader->numverts = BigLong(pinmodel->numverts);
1823         pheader->numtris = BigLong(pinmodel->numtris);
1824         pheader->numshaders = BigLong(pinmodel->numshaders);
1825         pheader->numbones = BigLong(pinmodel->numbones);
1826         pheader->numscenes = BigLong(pinmodel->numscenes);
1827         pheader->lump_scenes.start = BigLong(pinmodel->lump_scenes.start);
1828         pheader->lump_scenes.length = BigLong(pinmodel->lump_scenes.length);
1829         pheader->lump_poses.start = BigLong(pinmodel->lump_poses.start);
1830         pheader->lump_poses.length = BigLong(pinmodel->lump_poses.length);
1831         pheader->lump_bones.start = BigLong(pinmodel->lump_bones.start);
1832         pheader->lump_bones.length = BigLong(pinmodel->lump_bones.length);
1833         pheader->lump_vertbonecounts.start = BigLong(pinmodel->lump_vertbonecounts.start);
1834         pheader->lump_vertbonecounts.length = BigLong(pinmodel->lump_vertbonecounts.length);
1835         pheader->lump_verts.start = BigLong(pinmodel->lump_verts.start);
1836         pheader->lump_verts.length = BigLong(pinmodel->lump_verts.length);
1837         pheader->lump_texcoords.start = BigLong(pinmodel->lump_texcoords.start);
1838         pheader->lump_texcoords.length = BigLong(pinmodel->lump_texcoords.length);
1839         pheader->lump_render.start = BigLong(pinmodel->lump_render.start);
1840         pheader->lump_render.length = BigLong(pinmodel->lump_render.length);
1841         pheader->lump_shaders.start = BigLong(pinmodel->lump_shaders.start);
1842         pheader->lump_shaders.length = BigLong(pinmodel->lump_shaders.length);
1843         pheader->lump_trizone.start = BigLong(pinmodel->lump_trizone.start);
1844         pheader->lump_trizone.length = BigLong(pinmodel->lump_trizone.length);
1845
1846         if (pheader->numtris < 1 || pheader->numverts < 3 || pheader->numshaders < 1)
1847         {
1848                 Con_Printf("%s has no geometry\n", loadmodel->name);
1849                 return;
1850         }
1851         if (pheader->numscenes < 1 || pheader->lump_poses.length < (int)sizeof(float[3][4]))
1852         {
1853                 Con_Printf("%s has no animations\n", loadmodel->name);
1854                 return;
1855         }
1856
1857         loadmodel->DrawSky = NULL;
1858         loadmodel->DrawAddWaterPlanes = NULL;
1859         loadmodel->Draw = R_Q1BSP_Draw;
1860         loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
1861         loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
1862         loadmodel->DrawPrepass = R_Q1BSP_DrawPrepass;
1863         loadmodel->CompileShadowMap = R_Q1BSP_CompileShadowMap;
1864         loadmodel->DrawShadowMap = R_Q1BSP_DrawShadowMap;
1865         loadmodel->DrawLight = R_Q1BSP_DrawLight;
1866         loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
1867         loadmodel->TraceLine = Mod_MDLMD2MD3_TraceLine;
1868         loadmodel->PointSuperContents = NULL;
1869         loadmodel->AnimateVertices = Mod_Skeletal_AnimateVertices;
1870
1871         loadmodel->numframes = pheader->numscenes;
1872         loadmodel->num_surfaces = pheader->numshaders;
1873
1874         skinfiles = Mod_LoadSkinFiles();
1875         if (loadmodel->numskins < 1)
1876                 loadmodel->numskins = 1;
1877
1878         // make skinscenes for the skins (no groups)
1879         loadmodel->skinscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, sizeof(animscene_t) * loadmodel->numskins);
1880         for (i = 0;i < loadmodel->numskins;i++)
1881         {
1882                 loadmodel->skinscenes[i].firstframe = i;
1883                 loadmodel->skinscenes[i].framecount = 1;
1884                 loadmodel->skinscenes[i].loop = true;
1885                 loadmodel->skinscenes[i].framerate = 10;
1886         }
1887
1888         // model bbox
1889         // LordHavoc: actually we blow this away later with Mod_Alias_CalculateBoundingBox()
1890         modelradius = pheader->radius;
1891         for (i = 0;i < 3;i++)
1892         {
1893                 loadmodel->normalmins[i] = pheader->mins[i];
1894                 loadmodel->normalmaxs[i] = pheader->maxs[i];
1895                 loadmodel->rotatedmins[i] = -modelradius;
1896                 loadmodel->rotatedmaxs[i] = modelradius;
1897         }
1898         corner[0] = max(fabs(loadmodel->normalmins[0]), fabs(loadmodel->normalmaxs[0]));
1899         corner[1] = max(fabs(loadmodel->normalmins[1]), fabs(loadmodel->normalmaxs[1]));
1900         loadmodel->yawmaxs[0] = loadmodel->yawmaxs[1] = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
1901         if (loadmodel->yawmaxs[0] > modelradius)
1902                 loadmodel->yawmaxs[0] = loadmodel->yawmaxs[1] = modelradius;
1903         loadmodel->yawmins[0] = loadmodel->yawmins[1] = -loadmodel->yawmaxs[0];
1904         loadmodel->yawmins[2] = loadmodel->normalmins[2];
1905         loadmodel->yawmaxs[2] = loadmodel->normalmaxs[2];
1906         loadmodel->radius = modelradius;
1907         loadmodel->radius2 = modelradius * modelradius;
1908
1909         // go through the lumps, swapping things
1910
1911         //zymlump_t lump_scenes; // zymscene_t scene[numscenes]; // name and other information for each scene (see zymscene struct)
1912         loadmodel->animscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, sizeof(animscene_t) * loadmodel->numframes);
1913         scene = (zymscene_t *) (pheader->lump_scenes.start + pbase);
1914         numposes = pheader->lump_poses.length / pheader->numbones / sizeof(float[3][4]);
1915         for (i = 0;i < pheader->numscenes;i++)
1916         {
1917                 memcpy(loadmodel->animscenes[i].name, scene->name, 32);
1918                 loadmodel->animscenes[i].firstframe = BigLong(scene->start);
1919                 loadmodel->animscenes[i].framecount = BigLong(scene->length);
1920                 loadmodel->animscenes[i].framerate = BigFloat(scene->framerate);
1921                 loadmodel->animscenes[i].loop = (BigLong(scene->flags) & ZYMSCENEFLAG_NOLOOP) == 0;
1922                 if ((unsigned int) loadmodel->animscenes[i].firstframe >= (unsigned int) numposes)
1923                         Host_Error("%s scene->firstframe (%i) >= numposes (%i)", loadmodel->name, loadmodel->animscenes[i].firstframe, numposes);
1924                 if ((unsigned int) loadmodel->animscenes[i].firstframe + (unsigned int) loadmodel->animscenes[i].framecount > (unsigned int) numposes)
1925                         Host_Error("%s scene->firstframe (%i) + framecount (%i) >= numposes (%i)", loadmodel->name, loadmodel->animscenes[i].firstframe, loadmodel->animscenes[i].framecount, numposes);
1926                 if (loadmodel->animscenes[i].framerate < 0)
1927                         Host_Error("%s scene->framerate (%f) < 0", loadmodel->name, loadmodel->animscenes[i].framerate);
1928                 scene++;
1929         }
1930
1931         //zymlump_t lump_bones; // zymbone_t bone[numbones];
1932         loadmodel->num_bones = pheader->numbones;
1933         loadmodel->data_bones = (aliasbone_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_bones * sizeof(aliasbone_t));
1934         bone = (zymbone_t *) (pheader->lump_bones.start + pbase);
1935         for (i = 0;i < pheader->numbones;i++)
1936         {
1937                 memcpy(loadmodel->data_bones[i].name, bone[i].name, sizeof(bone[i].name));
1938                 loadmodel->data_bones[i].flags = BigLong(bone[i].flags);
1939                 loadmodel->data_bones[i].parent = BigLong(bone[i].parent);
1940                 if (loadmodel->data_bones[i].parent >= i)
1941                         Host_Error("%s bone[%i].parent >= %i", loadmodel->name, i, i);
1942         }
1943
1944         //zymlump_t lump_vertbonecounts; // int vertbonecounts[numvertices]; // how many bones influence each vertex (separate mainly to make this compress better)
1945         vertbonecounts = (int *)Mem_Alloc(loadmodel->mempool, pheader->numverts * sizeof(int));
1946         bonecount = (int *) (pheader->lump_vertbonecounts.start + pbase);
1947         for (i = 0;i < pheader->numverts;i++)
1948         {
1949                 vertbonecounts[i] = BigLong(bonecount[i]);
1950                 if (vertbonecounts[i] != 1)
1951                         Host_Error("%s bonecount[%i] != 1 (vertex weight support is impossible in this format)", loadmodel->name, i);
1952         }
1953
1954         loadmodel->num_poses = pheader->lump_poses.length / sizeof(float[3][4]) / loadmodel->num_bones;
1955
1956         meshvertices = pheader->numverts;
1957         meshtriangles = pheader->numtris;
1958
1959         loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
1960         loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
1961         loadmodel->num_texturesperskin = loadmodel->num_surfaces;
1962         data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t) + meshtriangles * sizeof(int[3]) + (meshvertices <= 65536 ? meshtriangles * sizeof(unsigned short[3]) : 0) + meshvertices * (sizeof(float[14]) + sizeof(unsigned short) + sizeof(unsigned char[2][4])) + loadmodel->num_poses * loadmodel->num_bones * sizeof(short[7]) + loadmodel->num_bones * sizeof(float[12]));
1963         loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
1964         loadmodel->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
1965         loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
1966         loadmodel->surfmesh.num_vertices = meshvertices;
1967         loadmodel->surfmesh.num_triangles = meshtriangles;
1968         loadmodel->surfmesh.data_element3i = (int *)data;data += meshtriangles * sizeof(int[3]);
1969         loadmodel->surfmesh.data_vertex3f = (float *)data;data += meshvertices * sizeof(float[3]);
1970         loadmodel->surfmesh.data_svector3f = (float *)data;data += meshvertices * sizeof(float[3]);
1971         loadmodel->surfmesh.data_tvector3f = (float *)data;data += meshvertices * sizeof(float[3]);
1972         loadmodel->surfmesh.data_normal3f = (float *)data;data += meshvertices * sizeof(float[3]);
1973         loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += meshvertices * sizeof(float[2]);
1974         loadmodel->surfmesh.data_skeletalindex4ub = (unsigned char *)data;data += meshvertices * sizeof(unsigned char[4]);
1975         loadmodel->surfmesh.data_skeletalweight4ub = (unsigned char *)data;data += meshvertices * sizeof(unsigned char[4]);
1976         loadmodel->data_baseboneposeinverse = (float *)data;data += loadmodel->num_bones * sizeof(float[12]);
1977         loadmodel->surfmesh.num_blends = 0;
1978         loadmodel->surfmesh.blends = (unsigned short *)data;data += meshvertices * sizeof(unsigned short);
1979         if (loadmodel->surfmesh.num_vertices <= 65536)
1980         {
1981                 loadmodel->surfmesh.data_element3s = (unsigned short *)data;data += loadmodel->surfmesh.num_triangles * sizeof(unsigned short[3]);
1982         }
1983         loadmodel->data_poses7s = (short *)data;data += loadmodel->num_poses * loadmodel->num_bones * sizeof(short[7]);
1984         loadmodel->surfmesh.data_blendweights = NULL;
1985
1986         //zymlump_t lump_poses; // float pose[numposes][numbones][3][4]; // animation data
1987         poses = (float *) (pheader->lump_poses.start + pbase);
1988         // figure out scale of model from root bone, for compatibility with old zmodel versions
1989         tempvec[0] = BigFloat(poses[0]);
1990         tempvec[1] = BigFloat(poses[1]);
1991         tempvec[2] = BigFloat(poses[2]);
1992         modelscale = VectorLength(tempvec);
1993         biggestorigin = 0;
1994         for (i = 0;i < loadmodel->num_bones * numposes * 12;i++)
1995         {
1996                 f = fabs(BigFloat(poses[i]));
1997                 biggestorigin = max(biggestorigin, f);
1998         }
1999         loadmodel->num_posescale = biggestorigin / 32767.0f;
2000         loadmodel->num_poseinvscale = 1.0f / loadmodel->num_posescale;
2001         for (i = 0;i < numposes;i++)
2002         {
2003                 const float *frameposes = (float *) (pheader->lump_poses.start + pbase) + 12*i*loadmodel->num_bones;
2004                 for (j = 0;j < loadmodel->num_bones;j++)
2005                 {
2006                         float pose[12];
2007                         matrix4x4_t posematrix;
2008                         for (k = 0;k < 12;k++)
2009                                 pose[k] = BigFloat(frameposes[j*12+k]);
2010                         //if (j < loadmodel->num_bones)
2011                         //      Con_Printf("%s: bone %i = %f %f %f %f : %f %f %f %f : %f %f %f %f : scale = %f\n", loadmodel->name, j, pose[0], pose[1], pose[2], pose[3], pose[4], pose[5], pose[6], pose[7], pose[8], pose[9], pose[10], pose[11], VectorLength(pose));
2012                         // scale child bones to match the root scale
2013                         if (loadmodel->data_bones[j].parent >= 0)
2014                         {
2015                                 pose[3] *= modelscale;
2016                                 pose[7] *= modelscale;
2017                                 pose[11] *= modelscale;
2018                         }
2019                         // normalize rotation matrix
2020                         VectorNormalize(pose + 0);
2021                         VectorNormalize(pose + 4);
2022                         VectorNormalize(pose + 8);
2023                         Matrix4x4_FromArray12FloatD3D(&posematrix, pose);
2024                         Matrix4x4_ToBonePose7s(&posematrix, loadmodel->num_poseinvscale, loadmodel->data_poses7s + 7*(i*loadmodel->num_bones+j));
2025                 }
2026         }
2027
2028         //zymlump_t lump_verts; // zymvertex_t vert[numvertices]; // see vertex struct
2029         verts = (zymvertex_t *)Mem_Alloc(loadmodel->mempool, pheader->lump_verts.length);
2030         vertdata = (zymvertex_t *) (pheader->lump_verts.start + pbase);
2031         // reconstruct frame 0 matrices to allow reconstruction of the base mesh
2032         // (converting from weight-blending skeletal animation to
2033         //  deformation-based skeletal animation)
2034         bonepose = (float *)Z_Malloc(loadmodel->num_bones * sizeof(float[12]));
2035         for (i = 0;i < loadmodel->num_bones;i++)
2036         {
2037                 float m[12];
2038                 for (k = 0;k < 12;k++)
2039                         m[k] = BigFloat(poses[i*12+k]);
2040                 if (loadmodel->data_bones[i].parent >= 0)
2041                         R_ConcatTransforms(bonepose + 12 * loadmodel->data_bones[i].parent, m, bonepose + 12 * i);
2042                 else
2043                         for (k = 0;k < 12;k++)
2044                                 bonepose[12*i+k] = m[k];
2045         }
2046         for (j = 0;j < pheader->numverts;j++)
2047         {
2048                 // this format really should have had a per vertexweight weight value...
2049                 // but since it does not, the weighting is completely ignored and
2050                 // only one weight is allowed per vertex
2051                 int boneindex = BigLong(vertdata[j].bonenum);
2052                 const float *m = bonepose + 12 * boneindex;
2053                 float relativeorigin[3];
2054                 relativeorigin[0] = BigFloat(vertdata[j].origin[0]);
2055                 relativeorigin[1] = BigFloat(vertdata[j].origin[1]);
2056                 relativeorigin[2] = BigFloat(vertdata[j].origin[2]);
2057                 // transform the vertex bone weight into the base mesh
2058                 loadmodel->surfmesh.data_vertex3f[j*3+0] = relativeorigin[0] * m[0] + relativeorigin[1] * m[1] + relativeorigin[2] * m[ 2] + m[ 3];
2059                 loadmodel->surfmesh.data_vertex3f[j*3+1] = relativeorigin[0] * m[4] + relativeorigin[1] * m[5] + relativeorigin[2] * m[ 6] + m[ 7];
2060                 loadmodel->surfmesh.data_vertex3f[j*3+2] = relativeorigin[0] * m[8] + relativeorigin[1] * m[9] + relativeorigin[2] * m[10] + m[11];
2061                 // store the weight as the primary weight on this vertex
2062                 loadmodel->surfmesh.blends[j] = boneindex;
2063                 loadmodel->surfmesh.data_skeletalindex4ub[j*4  ] = boneindex;
2064                 loadmodel->surfmesh.data_skeletalindex4ub[j*4+1] = 0;
2065                 loadmodel->surfmesh.data_skeletalindex4ub[j*4+2] = 0;
2066                 loadmodel->surfmesh.data_skeletalindex4ub[j*4+3] = 0;
2067                 loadmodel->surfmesh.data_skeletalweight4ub[j*4  ] = 255;
2068                 loadmodel->surfmesh.data_skeletalweight4ub[j*4+1] = 0;
2069                 loadmodel->surfmesh.data_skeletalweight4ub[j*4+2] = 0;
2070                 loadmodel->surfmesh.data_skeletalweight4ub[j*4+3] = 0;
2071         }
2072         Z_Free(bonepose);
2073         // normals and tangents are calculated after elements are loaded
2074
2075         //zymlump_t lump_texcoords; // float texcoords[numvertices][2];
2076         outtexcoord2f = loadmodel->surfmesh.data_texcoordtexture2f;
2077         intexcoord2f = (float *) (pheader->lump_texcoords.start + pbase);
2078         for (i = 0;i < pheader->numverts;i++)
2079         {
2080                 outtexcoord2f[i*2+0] = BigFloat(intexcoord2f[i*2+0]);
2081                 // flip T coordinate for OpenGL
2082                 outtexcoord2f[i*2+1] = 1 - BigFloat(intexcoord2f[i*2+1]);
2083         }
2084
2085         //zymlump_t lump_trizone; // byte trizone[numtris]; // see trizone explanation
2086         //loadmodel->alias.zymdata_trizone = Mem_Alloc(loadmodel->mempool, pheader->numtris);
2087         //memcpy(loadmodel->alias.zymdata_trizone, (void *) (pheader->lump_trizone.start + pbase), pheader->numtris);
2088
2089         //zymlump_t lump_shaders; // char shadername[numshaders][32]; // shaders used on this model
2090         //zymlump_t lump_render; // int renderlist[rendersize]; // sorted by shader with run lengths (int count), shaders are sequentially used, each run can be used with glDrawElements (each triangle is 3 int indices)
2091         // byteswap, validate, and swap winding order of tris
2092         count = pheader->numshaders * sizeof(int) + pheader->numtris * sizeof(int[3]);
2093         if (pheader->lump_render.length != count)
2094                 Host_Error("%s renderlist is wrong size (%i bytes, should be %i bytes)", loadmodel->name, pheader->lump_render.length, count);
2095         renderlist = (int *) (pheader->lump_render.start + pbase);
2096         renderlistend = (int *) ((unsigned char *) renderlist + pheader->lump_render.length);
2097         meshtriangles = 0;
2098         for (i = 0;i < loadmodel->num_surfaces;i++)
2099         {
2100                 int firstvertex, lastvertex;
2101                 if (renderlist >= renderlistend)
2102                         Host_Error("%s corrupt renderlist (wrong size)", loadmodel->name);
2103                 count = BigLong(*renderlist);renderlist++;
2104                 if (renderlist + count * 3 > renderlistend || (i == pheader->numshaders - 1 && renderlist + count * 3 != renderlistend))
2105                         Host_Error("%s corrupt renderlist (wrong size)", loadmodel->name);
2106
2107                 loadmodel->sortedmodelsurfaces[i] = i;
2108                 surface = loadmodel->data_surfaces + i;
2109                 surface->texture = loadmodel->data_textures + i;
2110                 surface->num_firsttriangle = meshtriangles;
2111                 surface->num_triangles = count;
2112                 meshtriangles += surface->num_triangles;
2113
2114                 // load the elements
2115                 outelements = loadmodel->surfmesh.data_element3i + surface->num_firsttriangle * 3;
2116                 for (j = 0;j < surface->num_triangles;j++, renderlist += 3)
2117                 {
2118                         outelements[j*3+2] = BigLong(renderlist[0]);
2119                         outelements[j*3+1] = BigLong(renderlist[1]);
2120                         outelements[j*3+0] = BigLong(renderlist[2]);
2121                 }
2122                 // validate the elements and find the used vertex range
2123                 firstvertex = meshvertices;
2124                 lastvertex = 0;
2125                 for (j = 0;j < surface->num_triangles * 3;j++)
2126                 {
2127                         if ((unsigned int)outelements[j] >= (unsigned int)meshvertices)
2128                                 Host_Error("%s corrupt renderlist (out of bounds index)", loadmodel->name);
2129                         firstvertex = min(firstvertex, outelements[j]);
2130                         lastvertex = max(lastvertex, outelements[j]);
2131                 }
2132                 surface->num_firstvertex = firstvertex;
2133                 surface->num_vertices = lastvertex + 1 - firstvertex;
2134
2135                 // since zym models do not have named sections, reuse their shader
2136                 // name as the section name
2137                 shadername = (char *) (pheader->lump_shaders.start + pbase) + i * 32;
2138                 Mod_BuildAliasSkinsFromSkinFiles(loadmodel->data_textures + i, skinfiles, shadername, shadername);
2139         }
2140         Mod_FreeSkinFiles(skinfiles);
2141         Mem_Free(vertbonecounts);
2142         Mem_Free(verts);
2143         Mod_MakeSortedSurfaces(loadmodel);
2144
2145         // compute all the mesh information that was not loaded from the file
2146         if (loadmodel->surfmesh.data_element3s)
2147                 for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
2148                         loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
2149         Mod_ValidateElements(loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_element3s, loadmodel->surfmesh.num_triangles, 0, loadmodel->surfmesh.num_vertices, __FILE__, __LINE__);
2150         Mod_BuildBaseBonePoses();
2151         Mod_BuildNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_normal3f, r_smoothnormals_areaweighting.integer != 0);
2152         Mod_BuildTextureVectorsFromNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_texcoordtexture2f, loadmodel->surfmesh.data_normal3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_svector3f, loadmodel->surfmesh.data_tvector3f, r_smoothnormals_areaweighting.integer != 0);
2153         loadmodel->surfmesh.isanimated = Mod_Alias_CalculateBoundingBox();
2154         if(mod_alias_force_animated.string[0])
2155                 loadmodel->surfmesh.isanimated = mod_alias_force_animated.integer != 0;
2156
2157         // Always make a BIH for the first frame, we can use it where possible.
2158         Mod_MakeCollisionBIH(loadmodel, true, &loadmodel->collision_bih);
2159         if (!loadmodel->surfmesh.isanimated)
2160         {
2161                 loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
2162                 loadmodel->TraceBrush = Mod_CollisionBIH_TraceBrush;
2163                 loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
2164                 loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
2165                 loadmodel->PointSuperContents = Mod_CollisionBIH_PointSuperContents_Mesh;
2166         }
2167
2168         // because shaders can do somewhat unexpected things, check for unusual features now
2169         for (i = 0;i < loadmodel->num_textures;i++)
2170         {
2171                 if (loadmodel->data_textures[i].basematerialflags & (MATERIALFLAG_SKY))
2172                         mod->DrawSky = R_Q1BSP_DrawSky;
2173                 if (loadmodel->data_textures[i].basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
2174                         mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
2175         }
2176 }
2177
2178 void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
2179 {
2180         dpmheader_t *pheader;
2181         dpmframe_t *frames;
2182         dpmbone_t *bone;
2183         dpmmesh_t *dpmmesh;
2184         unsigned char *pbase;
2185         int i, j, k, meshvertices, meshtriangles;
2186         skinfile_t *skinfiles;
2187         unsigned char *data;
2188         float *bonepose;
2189         float biggestorigin, tempvec[3], modelscale;
2190         float f;
2191         float *poses;
2192
2193         pheader = (dpmheader_t *)buffer;
2194         pbase = (unsigned char *)buffer;
2195         if (memcmp(pheader->id, "DARKPLACESMODEL\0", 16))
2196                 Host_Error ("Mod_DARKPLACESMODEL_Load: %s is not a darkplaces model", loadmodel->name);
2197         if (BigLong(pheader->type) != 2)
2198                 Host_Error ("Mod_DARKPLACESMODEL_Load: only type 2 (hierarchical skeletal pose) models are currently supported (name = %s)", loadmodel->name);
2199
2200         loadmodel->modeldatatypestring = "DPM";
2201
2202         loadmodel->type = mod_alias;
2203         loadmodel->synctype = ST_RAND;
2204
2205         // byteswap header
2206         pheader->type = BigLong(pheader->type);
2207         pheader->filesize = BigLong(pheader->filesize);
2208         pheader->mins[0] = BigFloat(pheader->mins[0]);
2209         pheader->mins[1] = BigFloat(pheader->mins[1]);
2210         pheader->mins[2] = BigFloat(pheader->mins[2]);
2211         pheader->maxs[0] = BigFloat(pheader->maxs[0]);
2212         pheader->maxs[1] = BigFloat(pheader->maxs[1]);
2213         pheader->maxs[2] = BigFloat(pheader->maxs[2]);
2214         pheader->yawradius = BigFloat(pheader->yawradius);
2215         pheader->allradius = BigFloat(pheader->allradius);
2216         pheader->num_bones = BigLong(pheader->num_bones);
2217         pheader->num_meshs = BigLong(pheader->num_meshs);
2218         pheader->num_frames = BigLong(pheader->num_frames);
2219         pheader->ofs_bones = BigLong(pheader->ofs_bones);
2220         pheader->ofs_meshs = BigLong(pheader->ofs_meshs);
2221         pheader->ofs_frames = BigLong(pheader->ofs_frames);
2222
2223         if (pheader->num_bones < 1 || pheader->num_meshs < 1)
2224         {
2225                 Con_Printf("%s has no geometry\n", loadmodel->name);
2226                 return;
2227         }
2228         if (pheader->num_frames < 1)
2229         {
2230                 Con_Printf("%s has no frames\n", loadmodel->name);
2231                 return;
2232         }
2233
2234         loadmodel->DrawSky = NULL;
2235         loadmodel->DrawAddWaterPlanes = NULL;
2236         loadmodel->Draw = R_Q1BSP_Draw;
2237         loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
2238         loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
2239         loadmodel->DrawPrepass = R_Q1BSP_DrawPrepass;
2240         loadmodel->CompileShadowMap = R_Q1BSP_CompileShadowMap;
2241         loadmodel->DrawShadowMap = R_Q1BSP_DrawShadowMap;
2242         loadmodel->DrawLight = R_Q1BSP_DrawLight;
2243         loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
2244         loadmodel->TraceLine = Mod_MDLMD2MD3_TraceLine;
2245         loadmodel->PointSuperContents = NULL;
2246         loadmodel->AnimateVertices = Mod_Skeletal_AnimateVertices;
2247
2248         // model bbox
2249         // LordHavoc: actually we blow this away later with Mod_Alias_CalculateBoundingBox()
2250         for (i = 0;i < 3;i++)
2251         {
2252                 loadmodel->normalmins[i] = pheader->mins[i];
2253                 loadmodel->normalmaxs[i] = pheader->maxs[i];
2254                 loadmodel->yawmins[i] = i != 2 ? -pheader->yawradius : pheader->mins[i];
2255                 loadmodel->yawmaxs[i] = i != 2 ? pheader->yawradius : pheader->maxs[i];
2256                 loadmodel->rotatedmins[i] = -pheader->allradius;
2257                 loadmodel->rotatedmaxs[i] = pheader->allradius;
2258         }
2259         loadmodel->radius = pheader->allradius;
2260         loadmodel->radius2 = pheader->allradius * pheader->allradius;
2261
2262         // load external .skin files if present
2263         skinfiles = Mod_LoadSkinFiles();
2264         if (loadmodel->numskins < 1)
2265                 loadmodel->numskins = 1;
2266
2267         meshvertices = 0;
2268         meshtriangles = 0;
2269
2270         // gather combined statistics from the meshes
2271         dpmmesh = (dpmmesh_t *) (pbase + pheader->ofs_meshs);
2272         for (i = 0;i < (int)pheader->num_meshs;i++)
2273         {
2274                 int numverts = BigLong(dpmmesh->num_verts);
2275                 meshvertices += numverts;
2276                 meshtriangles += BigLong(dpmmesh->num_tris);
2277                 dpmmesh++;
2278         }
2279
2280         loadmodel->numframes = pheader->num_frames;
2281         loadmodel->num_bones = pheader->num_bones;
2282         loadmodel->num_poses = loadmodel->numframes;
2283         loadmodel->nummodelsurfaces = loadmodel->num_surfaces = pheader->num_meshs;
2284         loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
2285         loadmodel->num_texturesperskin = loadmodel->num_surfaces;
2286         // do most allocations as one merged chunk
2287         data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t) + meshtriangles * sizeof(int[3]) + (meshvertices <= 65536 ? meshtriangles * sizeof(unsigned short[3]) : 0) + meshvertices * (sizeof(float[14]) + sizeof(unsigned short) + sizeof(unsigned char[2][4])) + loadmodel->num_poses * loadmodel->num_bones * sizeof(short[7]) + loadmodel->num_bones * sizeof(float[12]) + loadmodel->numskins * sizeof(animscene_t) + loadmodel->num_bones * sizeof(aliasbone_t) + loadmodel->numframes * sizeof(animscene_t));
2288         loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
2289         loadmodel->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
2290         loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
2291         loadmodel->surfmesh.num_vertices = meshvertices;
2292         loadmodel->surfmesh.num_triangles = meshtriangles;
2293         loadmodel->surfmesh.data_element3i = (int *)data;data += meshtriangles * sizeof(int[3]);
2294         loadmodel->surfmesh.data_vertex3f = (float *)data;data += meshvertices * sizeof(float[3]);
2295         loadmodel->surfmesh.data_svector3f = (float *)data;data += meshvertices * sizeof(float[3]);
2296         loadmodel->surfmesh.data_tvector3f = (float *)data;data += meshvertices * sizeof(float[3]);
2297         loadmodel->surfmesh.data_normal3f = (float *)data;data += meshvertices * sizeof(float[3]);
2298         loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += meshvertices * sizeof(float[2]);
2299         loadmodel->surfmesh.data_skeletalindex4ub = (unsigned char *)data;data += meshvertices * sizeof(unsigned char[4]);
2300         loadmodel->surfmesh.data_skeletalweight4ub = (unsigned char *)data;data += meshvertices * sizeof(unsigned char[4]);
2301         loadmodel->data_baseboneposeinverse = (float *)data;data += loadmodel->num_bones * sizeof(float[12]);
2302         loadmodel->skinscenes = (animscene_t *)data;data += loadmodel->numskins * sizeof(animscene_t);
2303         loadmodel->data_bones = (aliasbone_t *)data;data += loadmodel->num_bones * sizeof(aliasbone_t);
2304         loadmodel->animscenes = (animscene_t *)data;data += loadmodel->numframes * sizeof(animscene_t);
2305         loadmodel->surfmesh.num_blends = 0;
2306         loadmodel->surfmesh.blends = (unsigned short *)data;data += meshvertices * sizeof(unsigned short);
2307         if (meshvertices <= 65536)
2308         {
2309                 loadmodel->surfmesh.data_element3s = (unsigned short *)data;data += meshtriangles * sizeof(unsigned short[3]);
2310         }
2311         loadmodel->data_poses7s = (short *)data;data += loadmodel->num_poses * loadmodel->num_bones * sizeof(short[7]);
2312         loadmodel->surfmesh.data_blendweights = (blendweights_t *)Mem_Alloc(loadmodel->mempool, meshvertices * sizeof(blendweights_t));
2313
2314         for (i = 0;i < loadmodel->numskins;i++)
2315         {
2316                 loadmodel->skinscenes[i].firstframe = i;
2317                 loadmodel->skinscenes[i].framecount = 1;
2318                 loadmodel->skinscenes[i].loop = true;
2319                 loadmodel->skinscenes[i].framerate = 10;
2320         }
2321
2322         // load the bone info
2323         bone = (dpmbone_t *) (pbase + pheader->ofs_bones);
2324         for (i = 0;i < loadmodel->num_bones;i++)
2325         {
2326                 memcpy(loadmodel->data_bones[i].name, bone[i].name, sizeof(bone[i].name));
2327                 loadmodel->data_bones[i].flags = BigLong(bone[i].flags);
2328                 loadmodel->data_bones[i].parent = BigLong(bone[i].parent);
2329                 if (loadmodel->data_bones[i].parent >= i)
2330                         Host_Error("%s bone[%i].parent >= %i", loadmodel->name, i, i);
2331         }
2332
2333         // load the frames
2334         frames = (dpmframe_t *) (pbase + pheader->ofs_frames);
2335         // figure out scale of model from root bone, for compatibility with old dpmodel versions
2336         poses = (float *) (pbase + BigLong(frames[0].ofs_bonepositions));
2337         tempvec[0] = BigFloat(poses[0]);
2338         tempvec[1] = BigFloat(poses[1]);
2339         tempvec[2] = BigFloat(poses[2]);
2340         modelscale = VectorLength(tempvec);
2341         biggestorigin = 0;
2342         for (i = 0;i < loadmodel->numframes;i++)
2343         {
2344                 memcpy(loadmodel->animscenes[i].name, frames[i].name, sizeof(frames[i].name));
2345                 loadmodel->animscenes[i].firstframe = i;
2346                 loadmodel->animscenes[i].framecount = 1;
2347                 loadmodel->animscenes[i].loop = true;
2348                 loadmodel->animscenes[i].framerate = 10;
2349                 // load the bone poses for this frame
2350                 poses = (float *) (pbase + BigLong(frames[i].ofs_bonepositions));
2351                 for (j = 0;j < loadmodel->num_bones*12;j++)
2352                 {
2353                         f = fabs(BigFloat(poses[j]));
2354                         biggestorigin = max(biggestorigin, f);
2355                 }
2356                 // stuff not processed here: mins, maxs, yawradius, allradius
2357         }
2358         loadmodel->num_posescale = biggestorigin / 32767.0f;
2359         loadmodel->num_poseinvscale = 1.0f / loadmodel->num_posescale;
2360         for (i = 0;i < loadmodel->numframes;i++)
2361         {
2362                 const float *frameposes = (float *) (pbase + BigLong(frames[i].ofs_bonepositions));
2363                 for (j = 0;j < loadmodel->num_bones;j++)
2364                 {
2365                         float pose[12];
2366                         matrix4x4_t posematrix;
2367                         for (k = 0;k < 12;k++)
2368                                 pose[k] = BigFloat(frameposes[j*12+k]);
2369                         // scale child bones to match the root scale
2370                         if (loadmodel->data_bones[j].parent >= 0)
2371                         {
2372                                 pose[3] *= modelscale;
2373                                 pose[7] *= modelscale;
2374                                 pose[11] *= modelscale;
2375                         }
2376                         // normalize rotation matrix
2377                         VectorNormalize(pose + 0);
2378                         VectorNormalize(pose + 4);
2379                         VectorNormalize(pose + 8);
2380                         Matrix4x4_FromArray12FloatD3D(&posematrix, pose);
2381                         Matrix4x4_ToBonePose7s(&posematrix, loadmodel->num_poseinvscale, loadmodel->data_poses7s + 7*(i*loadmodel->num_bones+j));
2382                 }
2383         }
2384
2385         // load the meshes now
2386         dpmmesh = (dpmmesh_t *) (pbase + pheader->ofs_meshs);
2387         meshvertices = 0;
2388         meshtriangles = 0;
2389         // reconstruct frame 0 matrices to allow reconstruction of the base mesh
2390         // (converting from weight-blending skeletal animation to
2391         //  deformation-based skeletal animation)
2392         poses = (float *) (pbase + BigLong(frames[0].ofs_bonepositions));
2393         bonepose = (float *)Z_Malloc(loadmodel->num_bones * sizeof(float[12]));
2394         for (i = 0;i < loadmodel->num_bones;i++)
2395         {
2396                 float m[12];
2397                 for (k = 0;k < 12;k++)
2398                         m[k] = BigFloat(poses[i*12+k]);
2399                 if (loadmodel->data_bones[i].parent >= 0)
2400                         R_ConcatTransforms(bonepose + 12 * loadmodel->data_bones[i].parent, m, bonepose + 12 * i);
2401                 else
2402                         for (k = 0;k < 12;k++)
2403                                 bonepose[12*i+k] = m[k];
2404         }
2405         for (i = 0;i < loadmodel->num_surfaces;i++, dpmmesh++)
2406         {
2407                 const int *inelements;
2408                 int *outelement3i;
2409                 unsigned short *outelement3s;
2410                 const float *intexcoord;
2411                 msurface_t *surface;
2412
2413                 loadmodel->sortedmodelsurfaces[i] = i;
2414                 surface = loadmodel->data_surfaces + i;
2415                 surface->texture = loadmodel->data_textures + i;
2416                 surface->num_firsttriangle = meshtriangles;
2417                 surface->num_triangles = BigLong(dpmmesh->num_tris);
2418                 surface->num_firstvertex = meshvertices;
2419                 surface->num_vertices = BigLong(dpmmesh->num_verts);
2420                 meshvertices += surface->num_vertices;
2421                 meshtriangles += surface->num_triangles;
2422
2423                 inelements = (int *) (pbase + BigLong(dpmmesh->ofs_indices));
2424                 outelement3i = loadmodel->surfmesh.data_element3i + surface->num_firsttriangle * 3;
2425                 outelement3s = loadmodel->surfmesh.data_element3s ? loadmodel->surfmesh.data_element3s + surface->num_firsttriangle * 3 : NULL;
2426                 for (j = 0;j < surface->num_triangles;j++)
2427                 {
2428                         // swap element order to flip triangles, because Quake uses clockwise (rare) and dpm uses counterclockwise (standard)
2429                         outelement3i[j * 3 + 0] = surface->num_firstvertex + BigLong(inelements[j * 3 + 2]);
2430                         outelement3i[j * 3 + 1] = surface->num_firstvertex + BigLong(inelements[j * 3 + 1]);
2431                         outelement3i[j * 3 + 2] = surface->num_firstvertex + BigLong(inelements[j * 3 + 0]);
2432                         if (outelement3s)
2433                         {
2434                                 outelement3s[j * 3 + 0] = outelement3i[j * 3 + 0];
2435                                 outelement3s[j * 3 + 1] = outelement3i[j * 3 + 1];
2436                                 outelement3s[j * 3 + 2] = outelement3i[j * 3 + 2];
2437                         }
2438                 }
2439
2440                 intexcoord = (float *) (pbase + BigLong(dpmmesh->ofs_texcoords));
2441                 for (j = 0;j < surface->num_vertices*2;j++)
2442                         loadmodel->surfmesh.data_texcoordtexture2f[j + surface->num_firstvertex * 2] = BigFloat(intexcoord[j]);
2443
2444                 data = (unsigned char *) (pbase + BigLong(dpmmesh->ofs_verts));
2445                 for (j = surface->num_firstvertex;j < surface->num_firstvertex + surface->num_vertices;j++)
2446                 {
2447                         int weightindex[4] = { 0, 0, 0, 0 };
2448                         float weightinfluence[4] = { 0, 0, 0, 0 };
2449                         int l;
2450                         int numweights = BigLong(((dpmvertex_t *)data)->numbones);
2451                         data += sizeof(dpmvertex_t);
2452                         for (k = 0;k < numweights;k++)
2453                         {
2454                                 const dpmbonevert_t *vert = (dpmbonevert_t *) data;
2455                                 int boneindex = BigLong(vert->bonenum);
2456                                 const float *m = bonepose + 12 * boneindex;
2457                                 float influence = BigFloat(vert->influence);
2458                                 float relativeorigin[3], relativenormal[3];
2459                                 relativeorigin[0] = BigFloat(vert->origin[0]);
2460                                 relativeorigin[1] = BigFloat(vert->origin[1]);
2461                                 relativeorigin[2] = BigFloat(vert->origin[2]);
2462                                 relativenormal[0] = BigFloat(vert->normal[0]);
2463                                 relativenormal[1] = BigFloat(vert->normal[1]);
2464                                 relativenormal[2] = BigFloat(vert->normal[2]);
2465                                 // blend the vertex bone weights into the base mesh
2466                                 loadmodel->surfmesh.data_vertex3f[j*3+0] += relativeorigin[0] * m[0] + relativeorigin[1] * m[1] + relativeorigin[2] * m[ 2] + influence * m[ 3];
2467                                 loadmodel->surfmesh.data_vertex3f[j*3+1] += relativeorigin[0] * m[4] + relativeorigin[1] * m[5] + relativeorigin[2] * m[ 6] + influence * m[ 7];
2468                                 loadmodel->surfmesh.data_vertex3f[j*3+2] += relativeorigin[0] * m[8] + relativeorigin[1] * m[9] + relativeorigin[2] * m[10] + influence * m[11];
2469                                 loadmodel->surfmesh.data_normal3f[j*3+0] += relativenormal[0] * m[0] + relativenormal[1] * m[1] + relativenormal[2] * m[ 2];
2470                                 loadmodel->surfmesh.data_normal3f[j*3+1] += relativenormal[0] * m[4] + relativenormal[1] * m[5] + relativenormal[2] * m[ 6];
2471                                 loadmodel->surfmesh.data_normal3f[j*3+2] += relativenormal[0] * m[8] + relativenormal[1] * m[9] + relativenormal[2] * m[10];
2472                                 if (!k)
2473                                 {
2474                                         // store the first (and often only) weight
2475                                         weightinfluence[0] = influence;
2476                                         weightindex[0] = boneindex;
2477                                 }
2478                                 else
2479                                 {
2480                                         // sort the new weight into this vertex's weight table
2481                                         // (which only accepts up to 4 bones per vertex)
2482                                         for (l = 0;l < 4;l++)
2483                                         {
2484                                                 if (weightinfluence[l] < influence)
2485                                                 {
2486                                                         // move weaker influence weights out of the way first
2487                                                         int l2;
2488                                                         for (l2 = 3;l2 > l;l2--)
2489                                                         {
2490                                                                 weightinfluence[l2] = weightinfluence[l2-1];
2491                                                                 weightindex[l2] = weightindex[l2-1];
2492                                                         }
2493                                                         // store the new weight
2494                                                         weightinfluence[l] = influence;
2495                                                         weightindex[l] = boneindex;
2496                                                         break;
2497                                                 }
2498                                         }
2499                                 }
2500                                 data += sizeof(dpmbonevert_t);
2501                         }
2502                         loadmodel->surfmesh.blends[j] = Mod_Skeletal_CompressBlend(loadmodel, weightindex, weightinfluence);
2503                         loadmodel->surfmesh.data_skeletalindex4ub[j*4  ] = weightindex[0];
2504                         loadmodel->surfmesh.data_skeletalindex4ub[j*4+1] = weightindex[1];
2505                         loadmodel->surfmesh.data_skeletalindex4ub[j*4+2] = weightindex[2];
2506                         loadmodel->surfmesh.data_skeletalindex4ub[j*4+3] = weightindex[3];
2507                         loadmodel->surfmesh.data_skeletalweight4ub[j*4  ] = (unsigned char)(weightinfluence[0]*255.0f);
2508                         loadmodel->surfmesh.data_skeletalweight4ub[j*4+1] = (unsigned char)(weightinfluence[1]*255.0f);
2509                         loadmodel->surfmesh.data_skeletalweight4ub[j*4+2] = (unsigned char)(weightinfluence[2]*255.0f);
2510                         loadmodel->surfmesh.data_skeletalweight4ub[j*4+3] = (unsigned char)(weightinfluence[3]*255.0f);
2511                 }
2512
2513                 // since dpm models do not have named sections, reuse their shader name as the section name
2514                 Mod_BuildAliasSkinsFromSkinFiles(loadmodel->data_textures + i, skinfiles, dpmmesh->shadername, dpmmesh->shadername);
2515
2516                 Mod_ValidateElements(loadmodel->surfmesh.data_element3i + surface->num_firsttriangle * 3, loadmodel->surfmesh.data_element3s + surface->num_firsttriangle * 3, surface->num_triangles, surface->num_firstvertex, surface->num_vertices, __FILE__, __LINE__);
2517         }
2518         if (loadmodel->surfmesh.num_blends < meshvertices)
2519                 loadmodel->surfmesh.data_blendweights = (blendweights_t *)Mem_Realloc(loadmodel->mempool, loadmodel->surfmesh.data_blendweights, loadmodel->surfmesh.num_blends * sizeof(blendweights_t));
2520         Z_Free(bonepose);
2521         Mod_FreeSkinFiles(skinfiles);
2522         Mod_MakeSortedSurfaces(loadmodel);
2523
2524         // compute all the mesh information that was not loaded from the file
2525         Mod_BuildBaseBonePoses();
2526         Mod_BuildTextureVectorsFromNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_texcoordtexture2f, loadmodel->surfmesh.data_normal3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_svector3f, loadmodel->surfmesh.data_tvector3f, r_smoothnormals_areaweighting.integer != 0);
2527         loadmodel->surfmesh.isanimated = Mod_Alias_CalculateBoundingBox();
2528         if(mod_alias_force_animated.string[0])
2529                 loadmodel->surfmesh.isanimated = mod_alias_force_animated.integer != 0;
2530
2531         // Always make a BIH for the first frame, we can use it where possible.
2532         Mod_MakeCollisionBIH(loadmodel, true, &loadmodel->collision_bih);
2533         if (!loadmodel->surfmesh.isanimated)
2534         {
2535                 loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
2536                 loadmodel->TraceBrush = Mod_CollisionBIH_TraceBrush;
2537                 loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
2538                 loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
2539                 loadmodel->PointSuperContents = Mod_CollisionBIH_PointSuperContents_Mesh;
2540         }
2541
2542         // because shaders can do somewhat unexpected things, check for unusual features now
2543         for (i = 0;i < loadmodel->num_textures;i++)
2544         {
2545                 if (loadmodel->data_textures[i].basematerialflags & (MATERIALFLAG_SKY))
2546                         mod->DrawSky = R_Q1BSP_DrawSky;
2547                 if (loadmodel->data_textures[i].basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
2548                         mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
2549         }
2550 }
2551
2552 // no idea why PSK/PSA files contain weird quaternions but they do...
2553 #define PSKQUATNEGATIONS
2554 void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
2555 {
2556         int i, j, index, version, recordsize, numrecords, meshvertices, meshtriangles;
2557         int numpnts, numvtxw, numfaces, nummatts, numbones, numrawweights, numanimbones, numanims, numanimkeys;
2558         fs_offset_t filesize;
2559         pskpnts_t *pnts;
2560         pskvtxw_t *vtxw;
2561         pskface_t *faces;
2562         pskmatt_t *matts;
2563         pskboneinfo_t *bones;
2564         pskrawweights_t *rawweights;
2565         //pskboneinfo_t *animbones;
2566         pskaniminfo_t *anims;
2567         pskanimkeys_t *animkeys;
2568         void *animfilebuffer, *animbuffer, *animbufferend;
2569         unsigned char *data;
2570         pskchunk_t *pchunk;
2571         skinfile_t *skinfiles;
2572         char animname[MAX_QPATH];
2573         size_t size;
2574         float biggestorigin;
2575
2576         pchunk = (pskchunk_t *)buffer;
2577         if (strcmp(pchunk->id, "ACTRHEAD"))
2578                 Host_Error ("Mod_PSKMODEL_Load: %s is not an Unreal Engine ActorX (.psk + .psa) model", loadmodel->name);
2579
2580         loadmodel->modeldatatypestring = "PSK";
2581
2582         loadmodel->type = mod_alias;
2583         loadmodel->DrawSky = NULL;
2584         loadmodel->DrawAddWaterPlanes = NULL;
2585         loadmodel->Draw = R_Q1BSP_Draw;
2586         loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
2587         loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
2588         loadmodel->DrawPrepass = R_Q1BSP_DrawPrepass;
2589         loadmodel->CompileShadowMap = R_Q1BSP_CompileShadowMap;
2590         loadmodel->DrawShadowMap = R_Q1BSP_DrawShadowMap;
2591         loadmodel->DrawLight = R_Q1BSP_DrawLight;
2592         loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
2593         loadmodel->TraceLine = Mod_MDLMD2MD3_TraceLine;
2594         loadmodel->PointSuperContents = NULL;
2595         loadmodel->AnimateVertices = Mod_Skeletal_AnimateVertices;
2596         loadmodel->synctype = ST_RAND;
2597
2598         FS_StripExtension(loadmodel->name, animname, sizeof(animname));
2599         strlcat(animname, ".psa", sizeof(animname));
2600         animbuffer = animfilebuffer = FS_LoadFile(animname, loadmodel->mempool, false, &filesize);
2601         animbufferend = (void *)((unsigned char*)animbuffer + (int)filesize);
2602         if (!animbuffer)
2603                 animbufferend = animbuffer;
2604
2605         numpnts = 0;
2606         pnts = NULL;
2607         numvtxw = 0;
2608         vtxw = NULL;
2609         numfaces = 0;
2610         faces = NULL;
2611         nummatts = 0;
2612         matts = NULL;
2613         numbones = 0;
2614         bones = NULL;
2615         numrawweights = 0;
2616         rawweights = NULL;
2617         numanims = 0;
2618         anims = NULL;
2619         numanimkeys = 0;
2620         animkeys = NULL;
2621
2622         while (buffer < bufferend)
2623         {
2624                 pchunk = (pskchunk_t *)buffer;
2625                 buffer = (void *)((unsigned char *)buffer + sizeof(pskchunk_t));
2626                 version = LittleLong(pchunk->version);
2627                 recordsize = LittleLong(pchunk->recordsize);
2628                 numrecords = LittleLong(pchunk->numrecords);
2629                 if (developer_extra.integer)
2630                         Con_DPrintf("%s: %s %x: %i * %i = %i\n", loadmodel->name, pchunk->id, version, recordsize, numrecords, recordsize * numrecords);
2631                 if (version != 0x1e83b9 && version != 0x1e9179 && version != 0x2e && version != 0x12f2bc && version != 0x12f2f0)
2632                         Con_Printf ("%s: chunk %s has unknown version %x (0x1e83b9, 0x1e9179, 0x2e, 0x12f2bc, 0x12f2f0 are currently supported), trying to load anyway!\n", loadmodel->name, pchunk->id, version);
2633                 if (!strcmp(pchunk->id, "ACTRHEAD"))
2634                 {
2635                         // nothing to do
2636                 }
2637                 else if (!strcmp(pchunk->id, "PNTS0000"))
2638                 {
2639                         pskpnts_t *p;
2640                         if (recordsize != sizeof(*p))
2641                                 Host_Error("%s: %s has unsupported recordsize", loadmodel->name, pchunk->id);
2642                         // byteswap in place and keep the pointer
2643                         numpnts = numrecords;
2644                         pnts = (pskpnts_t *)buffer;
2645                         for (index = 0, p = (pskpnts_t *)buffer;index < numrecords;index++, p++)
2646                         {
2647                                 p->origin[0] = LittleFloat(p->origin[0]);
2648                                 p->origin[1] = LittleFloat(p->origin[1]);
2649                                 p->origin[2] = LittleFloat(p->origin[2]);
2650                         }
2651                         buffer = p;
2652                 }
2653                 else if (!strcmp(pchunk->id, "VTXW0000"))
2654                 {
2655                         pskvtxw_t *p;
2656                         if (recordsize != sizeof(*p))
2657                                 Host_Error("%s: %s has unsupported recordsize", loadmodel->name, pchunk->id);
2658                         // byteswap in place and keep the pointer
2659                         numvtxw = numrecords;
2660                         vtxw = (pskvtxw_t *)buffer;
2661                         for (index = 0, p = (pskvtxw_t *)buffer;index < numrecords;index++, p++)
2662                         {
2663                                 p->pntsindex = LittleShort(p->pntsindex);
2664                                 p->texcoord[0] = LittleFloat(p->texcoord[0]);
2665                                 p->texcoord[1] = LittleFloat(p->texcoord[1]);
2666                                 if (p->pntsindex >= numpnts)
2667                                 {
2668                                         Con_Printf("%s: vtxw->pntsindex %i >= numpnts %i\n", loadmodel->name, p->pntsindex, numpnts);
2669                                         p->pntsindex = 0;
2670                                 }
2671                         }
2672                         buffer = p;
2673                 }
2674                 else if (!strcmp(pchunk->id, "FACE0000"))
2675                 {
2676                         pskface_t *p;
2677                         if (recordsize != sizeof(*p))
2678                                 Host_Error("%s: %s has unsupported recordsize", loadmodel->name, pchunk->id);
2679                         // byteswap in place and keep the pointer
2680                         numfaces = numrecords;
2681                         faces = (pskface_t *)buffer;
2682                         for (index = 0, p = (pskface_t *)buffer;index < numrecords;index++, p++)
2683                         {
2684                                 p->vtxwindex[0] = LittleShort(p->vtxwindex[0]);
2685                                 p->vtxwindex[1] = LittleShort(p->vtxwindex[1]);
2686                                 p->vtxwindex[2] = LittleShort(p->vtxwindex[2]);
2687                                 p->group = LittleLong(p->group);
2688                                 if (p->vtxwindex[0] >= numvtxw)
2689                                 {
2690                                         Con_Printf("%s: face->vtxwindex[0] %i >= numvtxw %i\n", loadmodel->name, p->vtxwindex[0], numvtxw);
2691                                         p->vtxwindex[0] = 0;
2692                                 }
2693                                 if (p->vtxwindex[1] >= numvtxw)
2694                                 {
2695                                         Con_Printf("%s: face->vtxwindex[1] %i >= numvtxw %i\n", loadmodel->name, p->vtxwindex[1], numvtxw);
2696                                         p->vtxwindex[1] = 0;
2697                                 }
2698                                 if (p->vtxwindex[2] >= numvtxw)
2699                                 {
2700                                         Con_Printf("%s: face->vtxwindex[2] %i >= numvtxw %i\n", loadmodel->name, p->vtxwindex[2], numvtxw);
2701                                         p->vtxwindex[2] = 0;
2702                                 }
2703                         }
2704                         buffer = p;
2705                 }
2706                 else if (!strcmp(pchunk->id, "MATT0000"))
2707                 {
2708                         pskmatt_t *p;
2709                         if (recordsize != sizeof(*p))
2710                                 Host_Error("%s: %s has unsupported recordsize", loadmodel->name, pchunk->id);
2711                         // byteswap in place and keep the pointer
2712                         nummatts = numrecords;
2713                         matts = (pskmatt_t *)buffer;
2714                         for (index = 0, p = (pskmatt_t *)buffer;index < numrecords;index++, p++)
2715                         {
2716                                 // nothing to do
2717                         }
2718                         buffer = p;
2719                 }
2720                 else if (!strcmp(pchunk->id, "REFSKELT"))
2721                 {
2722                         pskboneinfo_t *p;
2723                         if (recordsize != sizeof(*p))
2724                                 Host_Error("%s: %s has unsupported recordsize", loadmodel->name, pchunk->id);
2725                         // byteswap in place and keep the pointer
2726                         numbones = numrecords;
2727                         bones = (pskboneinfo_t *)buffer;
2728                         for (index = 0, p = (pskboneinfo_t *)buffer;index < numrecords;index++, p++)
2729                         {
2730                                 p->numchildren = LittleLong(p->numchildren);
2731                                 p->parent = LittleLong(p->parent);
2732                                 p->basepose.quat[0] = LittleFloat(p->basepose.quat[0]);
2733                                 p->basepose.quat[1] = LittleFloat(p->basepose.quat[1]);
2734                                 p->basepose.quat[2] = LittleFloat(p->basepose.quat[2]);
2735                                 p->basepose.quat[3] = LittleFloat(p->basepose.quat[3]);
2736                                 p->basepose.origin[0] = LittleFloat(p->basepose.origin[0]);
2737                                 p->basepose.origin[1] = LittleFloat(p->basepose.origin[1]);
2738                                 p->basepose.origin[2] = LittleFloat(p->basepose.origin[2]);
2739                                 p->basepose.unknown = LittleFloat(p->basepose.unknown);
2740                                 p->basepose.size[0] = LittleFloat(p->basepose.size[0]);
2741                                 p->basepose.size[1] = LittleFloat(p->basepose.size[1]);
2742                                 p->basepose.size[2] = LittleFloat(p->basepose.size[2]);
2743 #ifdef PSKQUATNEGATIONS
2744                                 if (index)
2745                                 {
2746                                         p->basepose.quat[0] *= -1;
2747                                         p->basepose.quat[1] *= -1;
2748                                         p->basepose.quat[2] *= -1;
2749                                 }
2750                                 else
2751                                 {
2752                                         p->basepose.quat[0] *=  1;
2753                                         p->basepose.quat[1] *= -1;
2754                                         p->basepose.quat[2] *=  1;
2755                                 }
2756 #endif
2757                                 if (p->parent < 0 || p->parent >= numbones)
2758                                 {
2759                                         Con_Printf("%s: bone->parent %i >= numbones %i\n", loadmodel->name, p->parent, numbones);
2760                                         p->parent = 0;
2761                                 }
2762                         }
2763                         buffer = p;
2764                 }
2765                 else if (!strcmp(pchunk->id, "RAWWEIGHTS"))
2766                 {
2767                         pskrawweights_t *p;
2768                         if (recordsize != sizeof(*p))
2769                                 Host_Error("%s: %s has unsupported recordsize", loadmodel->name, pchunk->id);
2770                         // byteswap in place and keep the pointer
2771                         numrawweights = numrecords;
2772                         rawweights = (pskrawweights_t *)buffer;
2773                         for (index = 0, p = (pskrawweights_t *)buffer;index < numrecords;index++, p++)
2774                         {
2775                                 p->weight = LittleFloat(p->weight);
2776                                 p->pntsindex = LittleLong(p->pntsindex);
2777                                 p->boneindex = LittleLong(p->boneindex);
2778                                 if (p->pntsindex < 0 || p->pntsindex >= numpnts)
2779                                 {
2780                                         Con_Printf("%s: weight->pntsindex %i >= numpnts %i\n", loadmodel->name, p->pntsindex, numpnts);
2781                                         p->pntsindex = 0;
2782                                 }
2783                                 if (p->boneindex < 0 || p->boneindex >= numbones)
2784                                 {
2785                                         Con_Printf("%s: weight->boneindex %i >= numbones %i\n", loadmodel->name, p->boneindex, numbones);
2786                                         p->boneindex = 0;
2787                                 }
2788                         }
2789                         buffer = p;
2790                 }
2791         }
2792
2793         while (animbuffer < animbufferend)
2794         {
2795                 pchunk = (pskchunk_t *)animbuffer;
2796                 animbuffer = (void *)((unsigned char *)animbuffer + sizeof(pskchunk_t));
2797                 version = LittleLong(pchunk->version);
2798                 recordsize = LittleLong(pchunk->recordsize);
2799                 numrecords = LittleLong(pchunk->numrecords);
2800                 if (developer_extra.integer)
2801                         Con_DPrintf("%s: %s %x: %i * %i = %i\n", animname, pchunk->id, version, recordsize, numrecords, recordsize * numrecords);
2802                 if (version != 0x1e83b9 && version != 0x1e9179 && version != 0x2e && version != 0x12f2bc && version != 0x12f2f0)
2803                         Con_Printf ("%s: chunk %s has unknown version %x (0x1e83b9, 0x1e9179, 0x2e, 0x12f2bc, 0x12f2f0 are currently supported), trying to load anyway!\n", animname, pchunk->id, version);
2804                 if (!strcmp(pchunk->id, "ANIMHEAD"))
2805                 {
2806                         // nothing to do
2807                 }
2808                 else if (!strcmp(pchunk->id, "BONENAMES"))
2809                 {
2810                         pskboneinfo_t *p;
2811                         if (recordsize != sizeof(*p))
2812                                 Host_Error("%s: %s has unsupported recordsize", animname, pchunk->id);
2813                         // byteswap in place and keep the pointer
2814                         numanimbones = numrecords;
2815                         //animbones = (pskboneinfo_t *)animbuffer;
2816                         // NOTE: supposedly psa does not need to match the psk model, the
2817                         // bones missing from the psa would simply use their base
2818                         // positions from the psk, but this is hard for me to implement
2819                         // and people can easily make animations that match.
2820                         if (numanimbones != numbones)
2821                                 Host_Error("%s: this loader only supports animations with the same bones as the mesh", loadmodel->name);
2822                         for (index = 0, p = (pskboneinfo_t *)animbuffer;index < numrecords;index++, p++)
2823                         {
2824                                 p->numchildren = LittleLong(p->numchildren);
2825                                 p->parent = LittleLong(p->parent);
2826                                 p->basepose.quat[0] = LittleFloat(p->basepose.quat[0]);
2827                                 p->basepose.quat[1] = LittleFloat(p->basepose.quat[1]);
2828                                 p->basepose.quat[2] = LittleFloat(p->basepose.quat[2]);
2829                                 p->basepose.quat[3] = LittleFloat(p->basepose.quat[3]);
2830                                 p->basepose.origin[0] = LittleFloat(p->basepose.origin[0]);
2831                                 p->basepose.origin[1] = LittleFloat(p->basepose.origin[1]);
2832                                 p->basepose.origin[2] = LittleFloat(p->basepose.origin[2]);
2833                                 p->basepose.unknown = LittleFloat(p->basepose.unknown);
2834                                 p->basepose.size[0] = LittleFloat(p->basepose.size[0]);
2835                                 p->basepose.size[1] = LittleFloat(p->basepose.size[1]);
2836                                 p->basepose.size[2] = LittleFloat(p->basepose.size[2]);
2837 #ifdef PSKQUATNEGATIONS
2838                                 if (index)
2839                                 {
2840                                         p->basepose.quat[0] *= -1;
2841                                         p->basepose.quat[1] *= -1;
2842                                         p->basepose.quat[2] *= -1;
2843                                 }
2844                                 else
2845                                 {
2846                                         p->basepose.quat[0] *=  1;
2847                                         p->basepose.quat[1] *= -1;
2848                                         p->basepose.quat[2] *=  1;
2849                                 }
2850 #endif
2851                                 if (p->parent < 0 || p->parent >= numanimbones)
2852                                 {
2853                                         Con_Printf("%s: bone->parent %i >= numanimbones %i\n", animname, p->parent, numanimbones);
2854                                         p->parent = 0;
2855                                 }
2856                                 // check that bones are the same as in the base
2857                                 if (strcmp(p->name, bones[index].name) || p->parent != bones[index].parent)
2858                                         Host_Error("%s: this loader only supports animations with the same bones as the mesh", animname);
2859                         }
2860                         animbuffer = p;
2861                 }
2862                 else if (!strcmp(pchunk->id, "ANIMINFO"))
2863                 {
2864                         pskaniminfo_t *p;
2865                         if (recordsize != sizeof(*p))
2866                                 Host_Error("%s: %s has unsupported recordsize", animname, pchunk->id);
2867                         // byteswap in place and keep the pointer
2868                         numanims = numrecords;
2869                         anims = (pskaniminfo_t *)animbuffer;
2870                         for (index = 0, p = (pskaniminfo_t *)animbuffer;index < numrecords;index++, p++)
2871                         {
2872                                 p->numbones = LittleLong(p->numbones);
2873                                 p->playtime = LittleFloat(p->playtime);
2874                                 p->fps = LittleFloat(p->fps);
2875                                 p->firstframe = LittleLong(p->firstframe);
2876                                 p->numframes = LittleLong(p->numframes);
2877                                 if (p->numbones != numbones)
2878                                         Con_Printf("%s: animinfo->numbones != numbones, trying to load anyway!\n", animname);
2879                         }
2880                         animbuffer = p;
2881                 }
2882                 else if (!strcmp(pchunk->id, "ANIMKEYS"))
2883                 {
2884                         pskanimkeys_t *p;
2885                         if (recordsize != sizeof(*p))
2886                                 Host_Error("%s: %s has unsupported recordsize", animname, pchunk->id);
2887                         numanimkeys = numrecords;
2888                         animkeys = (pskanimkeys_t *)animbuffer;
2889                         for (index = 0, p = (pskanimkeys_t *)animbuffer;index < numrecords;index++, p++)
2890                         {
2891                                 p->origin[0] = LittleFloat(p->origin[0]);