]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - model_alias.c
let traces hit sky by default, don't know why this wasn't in
[xonotic/darkplaces.git] / model_alias.c
index f6b6f68716e5f80d427094fb7d379031a2b3102f..8d26e5b23fe91f0e61a97d2cab930a51da29a037 100644 (file)
@@ -21,16 +21,72 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "quakedef.h"
 #include "image.h"
 #include "r_shadow.h"
 #include "quakedef.h"
 #include "image.h"
 #include "r_shadow.h"
+#include "mod_skeletal_animatevertices_generic.h"
+#ifdef SSE_POSSIBLE
+#include "mod_skeletal_animatevertices_sse.h"
+#endif
 
 
+#ifdef SSE_POSSIBLE
+static qboolean r_skeletal_use_sse_defined = false;
+cvar_t r_skeletal_use_sse = {0, "r_skeletal_use_sse", "1", "use SSE for skeletal model animation"};
+#endif
 cvar_t r_skeletal_debugbone = {0, "r_skeletal_debugbone", "-1", "development cvar for testing skeletal model code"};
 cvar_t r_skeletal_debugbonecomponent = {0, "r_skeletal_debugbonecomponent", "3", "development cvar for testing skeletal model code"};
 cvar_t r_skeletal_debugbonevalue = {0, "r_skeletal_debugbonevalue", "100", "development cvar for testing skeletal model code"};
 cvar_t r_skeletal_debugtranslatex = {0, "r_skeletal_debugtranslatex", "1", "development cvar for testing skeletal model code"};
 cvar_t r_skeletal_debugtranslatey = {0, "r_skeletal_debugtranslatey", "1", "development cvar for testing skeletal model code"};
 cvar_t r_skeletal_debugtranslatez = {0, "r_skeletal_debugtranslatez", "1", "development cvar for testing skeletal model code"};
 cvar_t r_skeletal_debugbone = {0, "r_skeletal_debugbone", "-1", "development cvar for testing skeletal model code"};
 cvar_t r_skeletal_debugbonecomponent = {0, "r_skeletal_debugbonecomponent", "3", "development cvar for testing skeletal model code"};
 cvar_t r_skeletal_debugbonevalue = {0, "r_skeletal_debugbonevalue", "100", "development cvar for testing skeletal model code"};
 cvar_t r_skeletal_debugtranslatex = {0, "r_skeletal_debugtranslatex", "1", "development cvar for testing skeletal model code"};
 cvar_t r_skeletal_debugtranslatey = {0, "r_skeletal_debugtranslatey", "1", "development cvar for testing skeletal model code"};
 cvar_t r_skeletal_debugtranslatez = {0, "r_skeletal_debugtranslatez", "1", "development cvar for testing skeletal model code"};
+cvar_t mod_alias_supporttagscale = {0, "mod_alias_supporttagscale", "1", "support scaling factors in bone/tag attachment matrices as supported by MD3"};
 
 float mod_md3_sin[320];
 
 
 float mod_md3_sin[320];
 
+static size_t Mod_Skeletal_AnimateVertices_maxbonepose = 0;
+static void *Mod_Skeletal_AnimateVertices_bonepose = NULL;
+void Mod_Skeletal_FreeBuffers(void)
+{
+       if(Mod_Skeletal_AnimateVertices_bonepose)
+               Mem_Free(Mod_Skeletal_AnimateVertices_bonepose);
+       Mod_Skeletal_AnimateVertices_maxbonepose = 0;
+       Mod_Skeletal_AnimateVertices_bonepose = NULL;
+}
+void *Mod_Skeletal_AnimateVertices_AllocBuffers(size_t nbytes)
+{
+       if(Mod_Skeletal_AnimateVertices_maxbonepose < nbytes)
+       {
+               if(Mod_Skeletal_AnimateVertices_bonepose)
+                       Mem_Free(Mod_Skeletal_AnimateVertices_bonepose);
+               Mod_Skeletal_AnimateVertices_bonepose = Z_Malloc(nbytes);
+               Mod_Skeletal_AnimateVertices_maxbonepose = nbytes;
+       }
+       return Mod_Skeletal_AnimateVertices_bonepose;
+}
+
+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)
+{
+
+       if (!model->surfmesh.num_vertices)
+               return;
+
+       if (!model->num_bones)
+       {
+               if (vertex3f) memcpy(vertex3f, model->surfmesh.data_vertex3f, model->surfmesh.num_vertices*sizeof(float[3]));
+               if (normal3f) memcpy(normal3f, model->surfmesh.data_normal3f, model->surfmesh.num_vertices*sizeof(float[3]));
+               if (svector3f) memcpy(svector3f, model->surfmesh.data_svector3f, model->surfmesh.num_vertices*sizeof(float[3]));
+               if (tvector3f) memcpy(tvector3f, model->surfmesh.data_tvector3f, model->surfmesh.num_vertices*sizeof(float[3]));
+               return;
+       }
+
+#ifdef SSE_POSSIBLE
+       if(r_skeletal_use_sse_defined)
+               if(r_skeletal_use_sse.integer)
+               {
+                       Mod_Skeletal_AnimateVertices_SSE(model, frameblend, skeleton, vertex3f, normal3f, svector3f, tvector3f);
+                       return;
+               }
+#endif
+       Mod_Skeletal_AnimateVertices_Generic(model, frameblend, skeleton, vertex3f, normal3f, svector3f, tvector3f);
+}
+
 void Mod_AliasInit (void)
 {
        int i;
 void Mod_AliasInit (void)
 {
        int i;
@@ -40,187 +96,90 @@ void Mod_AliasInit (void)
        Cvar_RegisterVariable(&r_skeletal_debugtranslatex);
        Cvar_RegisterVariable(&r_skeletal_debugtranslatey);
        Cvar_RegisterVariable(&r_skeletal_debugtranslatez);
        Cvar_RegisterVariable(&r_skeletal_debugtranslatex);
        Cvar_RegisterVariable(&r_skeletal_debugtranslatey);
        Cvar_RegisterVariable(&r_skeletal_debugtranslatez);
+       Cvar_RegisterVariable(&mod_alias_supporttagscale);
        for (i = 0;i < 320;i++)
                mod_md3_sin[i] = sin(i * M_PI * 2.0f / 256.0);
        for (i = 0;i < 320;i++)
                mod_md3_sin[i] = sin(i * M_PI * 2.0f / 256.0);
+#ifdef SSE_POSSIBLE
+       if(Sys_HaveSSE())
+       {
+               Con_Printf("Skeletal animation uses SSE code path\n");
+               r_skeletal_use_sse_defined = true;
+               Cvar_RegisterVariable(&r_skeletal_use_sse);
+       }
+       else
+               Con_Printf("Skeletal animation uses generic code path (SSE disabled or not detected)\n");
+#else
+       Con_Printf("Skeletal animation uses generic code path (SSE not compiled in)\n");
+#endif
 }
 
 }
 
-void Mod_Skeletal_AnimateVertices(const dp_model_t *model, const frameblend_t *frameblend, float *vertex3f, float *normal3f, float *svector3f, float *tvector3f)
+static int Mod_Skeletal_AddBlend(dp_model_t *model, const blendweights_t *newweights)
 {
 {
-#define MAX_BONES 256
-       // vertex weighted skeletal
-       int i, k;
-       int blends;
-       float boneposerelative[MAX_BONES][12];
-       float *matrix, m[12], bonepose[MAX_BONES][12];
+       int i;
+       blendweights_t *weights;
+       if(!newweights->influence[1])
+               return newweights->index[0];
+       weights = model->surfmesh.data_blendweights;
+       for (i = 0;i < model->surfmesh.num_blends;i++, weights++)
+       {
+               if (!memcmp(weights, newweights, sizeof(blendweights_t)))
+                       return model->num_bones + i;
+       }
+       model->surfmesh.num_blends++;
+       memcpy(weights, newweights, sizeof(blendweights_t));
+       return model->num_bones + i;
+}
 
 
-       // interpolate matrices and concatenate them to their parents
-       for (i = 0;i < model->num_bones;i++)
-       {
-               for (k = 0;k < 12;k++)
-                       m[k] = 0;
-               for (blends = 0;blends < 4 && frameblend[blends].lerp > 0;blends++)
-               {
-                       matrix = model->data_poses + (frameblend[blends].frame * model->num_bones + i) * 12;
-                       for (k = 0;k < 12;k++)
-                               m[k] += matrix[k] * frameblend[blends].lerp;
-               }
-               if (i == r_skeletal_debugbone.integer)
-                       m[r_skeletal_debugbonecomponent.integer % 12] += r_skeletal_debugbonevalue.value;
-               m[3] *= r_skeletal_debugtranslatex.value;
-               m[7] *= r_skeletal_debugtranslatey.value;
-               m[11] *= r_skeletal_debugtranslatez.value;
-               if (model->data_bones[i].parent >= 0)
-                       R_ConcatTransforms(bonepose[model->data_bones[i].parent], m, bonepose[i]);
-               else
-                       for (k = 0;k < 12;k++)
-                               bonepose[i][k] = m[k];
-               // create a relative deformation matrix to describe displacement
-               // from the base mesh, which is used by the actual weighting
-               R_ConcatTransforms(bonepose[i], model->data_baseboneposeinverse + i * 12, boneposerelative[i]);
-       }
-       // blend the vertex bone weights
-       // special case for the extremely common wf[0] == 1 because it saves 3 multiplies per array when compared to the other case (w[0] is always 1 if only one bone controls this vertex, artists only use multiple bones for certain special cases)
-       // special case for the first bone because it avoids the need to memset the arrays before filling
-       {
-               const float *v = model->surfmesh.data_vertex3f;
-               const int *wi = model->surfmesh.data_vertexweightindex4i;
-               const float *wf = model->surfmesh.data_vertexweightinfluence4f;
-               memset(vertex3f, 0, sizeof(float[3]) * model->surfmesh.num_vertices);
-               for (i = 0;i < model->surfmesh.num_vertices;i++, v += 3, wi += 4, wf += 4, vertex3f += 3)
-               {
-                       if (wf[0] == 1)
-                       {
-                               const float *m = boneposerelative[wi[0]];
-                               vertex3f[0] = (v[0] * m[0] + v[1] * m[1] + v[2] * m[ 2] + m[ 3]);
-                               vertex3f[1] = (v[0] * m[4] + v[1] * m[5] + v[2] * m[ 6] + m[ 7]);
-                               vertex3f[2] = (v[0] * m[8] + v[1] * m[9] + v[2] * m[10] + m[11]);
-                       }
-                       else
-                       {
-                               const float *m = boneposerelative[wi[0]];
-                               float f = wf[0];
-                               vertex3f[0] = f * (v[0] * m[0] + v[1] * m[1] + v[2] * m[ 2] + m[ 3]);
-                               vertex3f[1] = f * (v[0] * m[4] + v[1] * m[5] + v[2] * m[ 6] + m[ 7]);
-                               vertex3f[2] = f * (v[0] * m[8] + v[1] * m[9] + v[2] * m[10] + m[11]);
-                               for (k = 1;k < 4 && wf[k];k++)
-                               {
-                                       const float *m = boneposerelative[wi[k]];
-                                       float f = wf[k];
-                                       vertex3f[0] += f * (v[0] * m[0] + v[1] * m[1] + v[2] * m[ 2] + m[ 3]);
-                                       vertex3f[1] += f * (v[0] * m[4] + v[1] * m[5] + v[2] * m[ 6] + m[ 7]);
-                                       vertex3f[2] += f * (v[0] * m[8] + v[1] * m[9] + v[2] * m[10] + m[11]);
-                               }
-                       }
-               }
-       }
-       if (normal3f)
-       {
-               const float *n = model->surfmesh.data_normal3f;
-               const int *wi = model->surfmesh.data_vertexweightindex4i;
-               const float *wf = model->surfmesh.data_vertexweightinfluence4f;
-               memset(normal3f, 0, sizeof(float[3]) * model->surfmesh.num_vertices);
-               for (i = 0;i < model->surfmesh.num_vertices;i++, n += 3, wi += 4, wf += 4, normal3f += 3)
+static int Mod_Skeletal_CompressBlend(dp_model_t *model, const int *newindex, const float *newinfluence)
+{
+       int i, total;
+       float scale;
+       blendweights_t newweights;
+       if(!newinfluence[1])
+               return newindex[0];
+       scale = 0;
+       for (i = 0;i < 4;i++)
+               scale += newinfluence[i];
+       scale = 255.0f / scale;
+       total = 0;
+       for (i = 0;i < 4;i++)
+       {
+               newweights.index[i] = newindex[i];
+               newweights.influence[i] = (unsigned char)(newinfluence[i] * scale);
+               total += newweights.influence[i];
+       }       
+       while (total > 255)
+       {
+               for (i = 0;i < 4;i++)
                {
                {
-                       if (wf[0] == 1)
-                       {
-                               const float *m = boneposerelative[wi[0]];
-                               normal3f[0] = (n[0] * m[0] + n[1] * m[1] + n[2] * m[ 2]);
-                               normal3f[1] = (n[0] * m[4] + n[1] * m[5] + n[2] * m[ 6]);
-                               normal3f[2] = (n[0] * m[8] + n[1] * m[9] + n[2] * m[10]);
-                       }
-                       else
-                       {
-                               const float *m = boneposerelative[wi[0]];
-                               float f = wf[0];
-                               normal3f[0] = f * (n[0] * m[0] + n[1] * m[1] + n[2] * m[ 2]);
-                               normal3f[1] = f * (n[0] * m[4] + n[1] * m[5] + n[2] * m[ 6]);
-                               normal3f[2] = f * (n[0] * m[8] + n[1] * m[9] + n[2] * m[10]);
-                               for (k = 1;k < 4 && wf[k];k++)
-                               {
-                                       const float *m = boneposerelative[wi[k]];
-                                       float f = wf[k];
-                                       normal3f[0] += f * (n[0] * m[0] + n[1] * m[1] + n[2] * m[ 2]);
-                                       normal3f[1] += f * (n[0] * m[4] + n[1] * m[5] + n[2] * m[ 6]);
-                                       normal3f[2] += f * (n[0] * m[8] + n[1] * m[9] + n[2] * m[10]);
-                               }
+                       if(newweights.influence[i] > 0 && total > 255) 
+                       { 
+                               newweights.influence[i]--;
+                               total--; 
                        }
                }
        }
                        }
                }
        }
-       if (svector3f)
+       while (total < 255)
        {
        {
-               const float *sv = model->surfmesh.data_svector3f;
-               const int *wi = model->surfmesh.data_vertexweightindex4i;
-               const float *wf = model->surfmesh.data_vertexweightinfluence4f;
-               memset(svector3f, 0, sizeof(float[3]) * model->surfmesh.num_vertices);
-               for (i = 0;i < model->surfmesh.num_vertices;i++, sv += 3, wi += 4, wf += 4, svector3f += 3)
+               for (i = 0; i < 4;i++)
                {
                {
-                       if (wf[0] == 1)
-                       {
-                               const float *m = boneposerelative[wi[0]];
-                               svector3f[0] = (sv[0] * m[0] + sv[1] * m[1] + sv[2] * m[ 2]);
-                               svector3f[1] = (sv[0] * m[4] + sv[1] * m[5] + sv[2] * m[ 6]);
-                               svector3f[2] = (sv[0] * m[8] + sv[1] * m[9] + sv[2] * m[10]);
-                       }
-                       else
-                       {
-                               const float *m = boneposerelative[wi[0]];
-                               float f = wf[0];
-                               svector3f[0] = f * (sv[0] * m[0] + sv[1] * m[1] + sv[2] * m[ 2]);
-                               svector3f[1] = f * (sv[0] * m[4] + sv[1] * m[5] + sv[2] * m[ 6]);
-                               svector3f[2] = f * (sv[0] * m[8] + sv[1] * m[9] + sv[2] * m[10]);
-                               for (k = 1;k < 4 && wf[k];k++)
-                               {
-                                       const float *m = boneposerelative[wi[k]];
-                                       float f = wf[k];
-                                       svector3f[0] += f * (sv[0] * m[0] + sv[1] * m[1] + sv[2] * m[ 2]);
-                                       svector3f[1] += f * (sv[0] * m[4] + sv[1] * m[5] + sv[2] * m[ 6]);
-                                       svector3f[2] += f * (sv[0] * m[8] + sv[1] * m[9] + sv[2] * m[10]);
-                               }
-                       }
-               }
-       }
-       if (tvector3f)
-       {
-               const float *tv = model->surfmesh.data_tvector3f;
-               const int *wi = model->surfmesh.data_vertexweightindex4i;
-               const float *wf = model->surfmesh.data_vertexweightinfluence4f;
-               memset(tvector3f, 0, sizeof(float[3]) * model->surfmesh.num_vertices);
-               for (i = 0;i < model->surfmesh.num_vertices;i++, tv += 3, wi += 4, wf += 4, tvector3f += 3)
-               {
-                       if (wf[0] == 1)
-                       {
-                               const float *m = boneposerelative[wi[0]];
-                               tvector3f[0] = (tv[0] * m[0] + tv[1] * m[1] + tv[2] * m[ 2]);
-                               tvector3f[1] = (tv[0] * m[4] + tv[1] * m[5] + tv[2] * m[ 6]);
-                               tvector3f[2] = (tv[0] * m[8] + tv[1] * m[9] + tv[2] * m[10]);
-                       }
-                       else
-                       {
-                               const float *m = boneposerelative[wi[0]];
-                               float f = wf[0];
-                               tvector3f[0] = f * (tv[0] * m[0] + tv[1] * m[1] + tv[2] * m[ 2]);
-                               tvector3f[1] = f * (tv[0] * m[4] + tv[1] * m[5] + tv[2] * m[ 6]);
-                               tvector3f[2] = f * (tv[0] * m[8] + tv[1] * m[9] + tv[2] * m[10]);
-                               for (k = 1;k < 4 && wf[k];k++)
-                               {
-                                       const float *m = boneposerelative[wi[k]];
-                                       float f = wf[k];
-                                       tvector3f[0] += f * (tv[0] * m[0] + tv[1] * m[1] + tv[2] * m[ 2]);
-                                       tvector3f[1] += f * (tv[0] * m[4] + tv[1] * m[5] + tv[2] * m[ 6]);
-                                       tvector3f[2] += f * (tv[0] * m[8] + tv[1] * m[9] + tv[2] * m[10]);
-                               }
+                       if(newweights.influence[i] < 255 && total < 255) 
+                       { 
+                               newweights.influence[i]++; 
+                               total++; 
                        }
                }
        }
                        }
                }
        }
+       return Mod_Skeletal_AddBlend(model, &newweights);
 }
 
 }
 
-void Mod_MD3_AnimateVertices(const dp_model_t *model, const frameblend_t *frameblend, float *vertex3f, float *normal3f, float *svector3f, float *tvector3f)
+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)
 {
        // vertex morph
        int i, numblends, blendnum;
        int numverts = model->surfmesh.num_vertices;
        numblends = 0;
 {
        // vertex morph
        int i, numblends, blendnum;
        int numverts = model->surfmesh.num_vertices;
        numblends = 0;
-       for (blendnum = 0;blendnum < 4;blendnum++)
+       for (blendnum = 0;blendnum < MAX_FRAMEBLENDS;blendnum++)
        {
                //VectorMA(translate, model->surfmesh.num_morphmdlframetranslate, frameblend[blendnum].lerp, translate);
                if (frameblend[blendnum].lerp > 0)
        {
                //VectorMA(translate, model->surfmesh.num_morphmdlframetranslate, frameblend[blendnum].lerp, translate);
                if (frameblend[blendnum].lerp > 0)
@@ -229,29 +188,32 @@ void Mod_MD3_AnimateVertices(const dp_model_t *model, const frameblend_t *frameb
        // special case for the first blend because it avoids some adds and the need to memset the arrays first
        for (blendnum = 0;blendnum < numblends;blendnum++)
        {
        // special case for the first blend because it avoids some adds and the need to memset the arrays first
        for (blendnum = 0;blendnum < numblends;blendnum++)
        {
-               const md3vertex_t *verts = model->surfmesh.data_morphmd3vertex + numverts * frameblend[blendnum].frame;
-               float scale = frameblend[blendnum].lerp * (1.0f / 64.0f);
-               if (blendnum == 0)
+               const md3vertex_t *verts = model->surfmesh.data_morphmd3vertex + numverts * frameblend[blendnum].subframe;
+               if (vertex3f)
                {
                {
-                       for (i = 0;i < numverts;i++)
+                       float scale = frameblend[blendnum].lerp * (1.0f / 64.0f);
+                       if (blendnum == 0)
                        {
                        {
-                               vertex3f[i * 3 + 0] = verts[i].origin[0] * scale;
-                               vertex3f[i * 3 + 1] = verts[i].origin[1] * scale;
-                               vertex3f[i * 3 + 2] = verts[i].origin[2] * scale;
+                               for (i = 0;i < numverts;i++)
+                               {
+                                       vertex3f[i * 3 + 0] = verts[i].origin[0] * scale;
+                                       vertex3f[i * 3 + 1] = verts[i].origin[1] * scale;
+                                       vertex3f[i * 3 + 2] = verts[i].origin[2] * scale;
+                               }
                        }
                        }
-               }
-               else
-               {
-                       for (i = 0;i < numverts;i++)
+                       else
                        {
                        {
-                               vertex3f[i * 3 + 0] += verts[i].origin[0] * scale;
-                               vertex3f[i * 3 + 1] += verts[i].origin[1] * scale;
-                               vertex3f[i * 3 + 2] += verts[i].origin[2] * scale;
+                               for (i = 0;i < numverts;i++)
+                               {
+                                       vertex3f[i * 3 + 0] += verts[i].origin[0] * scale;
+                                       vertex3f[i * 3 + 1] += verts[i].origin[1] * scale;
+                                       vertex3f[i * 3 + 2] += verts[i].origin[2] * scale;
+                               }
                        }
                }
                // the yaw and pitch stored in md3 models are 8bit quantized angles
                // (0-255), and as such a lookup table is very well suited to
                        }
                }
                // the yaw and pitch stored in md3 models are 8bit quantized angles
                // (0-255), and as such a lookup table is very well suited to
-               // decoding them, and since cosine is equivilant to sine with an
+               // decoding them, and since cosine is equivalent to sine with an
                // extra 45 degree rotation, this uses one lookup table for both
                // sine and cosine with a +64 bias to get cosine.
                if (normal3f)
                // extra 45 degree rotation, this uses one lookup table for both
                // sine and cosine with a +64 bias to get cosine.
                if (normal3f)
@@ -278,7 +240,7 @@ void Mod_MD3_AnimateVertices(const dp_model_t *model, const frameblend_t *frameb
                }
                if (svector3f)
                {
                }
                if (svector3f)
                {
-                       const texvecvertex_t *texvecvert = model->surfmesh.data_morphtexvecvertex + numverts * frameblend[blendnum].frame;
+                       const texvecvertex_t *texvecvert = model->surfmesh.data_morphtexvecvertex + numverts * frameblend[blendnum].subframe;
                        float f = frameblend[blendnum].lerp * (1.0f / 127.0f);
                        if (blendnum == 0)
                        {
                        float f = frameblend[blendnum].lerp * (1.0f / 127.0f);
                        if (blendnum == 0)
                        {
@@ -299,8 +261,7 @@ void Mod_MD3_AnimateVertices(const dp_model_t *model, const frameblend_t *frameb
                }
        }
 }
                }
        }
 }
-
-void Mod_MDL_AnimateVertices(const dp_model_t *model, const frameblend_t *frameblend, float *vertex3f, float *normal3f, float *svector3f, float *tvector3f)
+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)
 {
        // vertex morph
        int i, numblends, blendnum;
 {
        // vertex morph
        int i, numblends, blendnum;
@@ -310,10 +271,10 @@ void Mod_MDL_AnimateVertices(const dp_model_t *model, const frameblend_t *frameb
        numblends = 0;
        // blend the frame translates to avoid redundantly doing so on each vertex
        // (a bit of a brain twister but it works)
        numblends = 0;
        // blend the frame translates to avoid redundantly doing so on each vertex
        // (a bit of a brain twister but it works)
-       for (blendnum = 0;blendnum < 4;blendnum++)
+       for (blendnum = 0;blendnum < MAX_FRAMEBLENDS;blendnum++)
        {
                if (model->surfmesh.data_morphmd2framesize6f)
        {
                if (model->surfmesh.data_morphmd2framesize6f)
-                       VectorMA(translate, frameblend[blendnum].lerp, model->surfmesh.data_morphmd2framesize6f + frameblend[blendnum].frame * 6 + 3, translate);
+                       VectorMA(translate, frameblend[blendnum].lerp, model->surfmesh.data_morphmd2framesize6f + frameblend[blendnum].subframe * 6 + 3, translate);
                else
                        VectorMA(translate, frameblend[blendnum].lerp, model->surfmesh.num_morphmdlframetranslate, translate);
                if (frameblend[blendnum].lerp > 0)
                else
                        VectorMA(translate, frameblend[blendnum].lerp, model->surfmesh.num_morphmdlframetranslate, translate);
                if (frameblend[blendnum].lerp > 0)
@@ -322,28 +283,31 @@ void Mod_MDL_AnimateVertices(const dp_model_t *model, const frameblend_t *frameb
        // special case for the first blend because it avoids some adds and the need to memset the arrays first
        for (blendnum = 0;blendnum < numblends;blendnum++)
        {
        // special case for the first blend because it avoids some adds and the need to memset the arrays first
        for (blendnum = 0;blendnum < numblends;blendnum++)
        {
-               const trivertx_t *verts = model->surfmesh.data_morphmdlvertex + numverts * frameblend[blendnum].frame;
-               float scale[3];
-               if (model->surfmesh.data_morphmd2framesize6f)
-                       VectorScale(model->surfmesh.data_morphmd2framesize6f + frameblend[blendnum].frame * 6, frameblend[blendnum].lerp, scale);
-               else
-                       VectorScale(model->surfmesh.num_morphmdlframescale, frameblend[blendnum].lerp, scale);
-               if (blendnum == 0)
+               const trivertx_t *verts = model->surfmesh.data_morphmdlvertex + numverts * frameblend[blendnum].subframe;
+               if (vertex3f)
                {
                {
-                       for (i = 0;i < numverts;i++)
+                       float scale[3];
+                       if (model->surfmesh.data_morphmd2framesize6f)
+                               VectorScale(model->surfmesh.data_morphmd2framesize6f + frameblend[blendnum].subframe * 6, frameblend[blendnum].lerp, scale);
+                       else
+                               VectorScale(model->surfmesh.num_morphmdlframescale, frameblend[blendnum].lerp, scale);
+                       if (blendnum == 0)
                        {
                        {
-                               vertex3f[i * 3 + 0] = translate[0] + verts[i].v[0] * scale[0];
-                               vertex3f[i * 3 + 1] = translate[1] + verts[i].v[1] * scale[1];
-                               vertex3f[i * 3 + 2] = translate[2] + verts[i].v[2] * scale[2];
+                               for (i = 0;i < numverts;i++)
+                               {
+                                       vertex3f[i * 3 + 0] = translate[0] + verts[i].v[0] * scale[0];
+                                       vertex3f[i * 3 + 1] = translate[1] + verts[i].v[1] * scale[1];
+                                       vertex3f[i * 3 + 2] = translate[2] + verts[i].v[2] * scale[2];
+                               }
                        }
                        }
-               }
-               else
-               {
-                       for (i = 0;i < numverts;i++)
+                       else
                        {
                        {
-                               vertex3f[i * 3 + 0] += verts[i].v[0] * scale[0];
-                               vertex3f[i * 3 + 1] += verts[i].v[1] * scale[1];
-                               vertex3f[i * 3 + 2] += verts[i].v[2] * scale[2];
+                               for (i = 0;i < numverts;i++)
+                               {
+                                       vertex3f[i * 3 + 0] += verts[i].v[0] * scale[0];
+                                       vertex3f[i * 3 + 1] += verts[i].v[1] * scale[1];
+                                       vertex3f[i * 3 + 2] += verts[i].v[2] * scale[2];
+                               }
                        }
                }
                // the vertex normals in mdl models are an index into a table of
                        }
                }
                // the vertex normals in mdl models are an index into a table of
@@ -372,7 +336,7 @@ void Mod_MDL_AnimateVertices(const dp_model_t *model, const frameblend_t *frameb
                }
                if (svector3f)
                {
                }
                if (svector3f)
                {
-                       const texvecvertex_t *texvecvert = model->surfmesh.data_morphtexvecvertex + numverts * frameblend[blendnum].frame;
+                       const texvecvertex_t *texvecvert = model->surfmesh.data_morphtexvecvertex + numverts * frameblend[blendnum].subframe;
                        float f = frameblend[blendnum].lerp * (1.0f / 127.0f);
                        if (blendnum == 0)
                        {
                        float f = frameblend[blendnum].lerp * (1.0f / 127.0f);
                        if (blendnum == 0)
                        {
@@ -394,38 +358,130 @@ void Mod_MDL_AnimateVertices(const dp_model_t *model, const frameblend_t *frameb
        }
 }
 
        }
 }
 
-int Mod_Alias_GetTagMatrix(const dp_model_t *model, int poseframe, int tagindex, matrix4x4_t *outmatrix)
+int Mod_Alias_GetTagMatrix(const dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, int tagindex, matrix4x4_t *outmatrix)
 {
 {
-       const float *boneframe;
-       float tempbonematrix[12], bonematrix[12];
+       matrix4x4_t temp;
+       matrix4x4_t parentbonematrix;
+       matrix4x4_t tempbonematrix;
+       matrix4x4_t bonematrix;
+       matrix4x4_t blendmatrix;
+       int blendindex;
+       int parenttagindex;
+       int k;
+       float lerp;
+       const float *input;
+       float blendtag[12];
        *outmatrix = identitymatrix;
        *outmatrix = identitymatrix;
-       if (model->num_bones)
+       if (skeleton && skeleton->relativetransforms)
+       {
+               if (tagindex < 0 || tagindex >= skeleton->model->num_bones)
+                       return 4;
+               *outmatrix = skeleton->relativetransforms[tagindex];
+               while ((tagindex = model->data_bones[tagindex].parent) >= 0)
+               {
+                       temp = *outmatrix;
+                       Matrix4x4_Concat(outmatrix, &skeleton->relativetransforms[tagindex], &temp);
+               }
+       }
+       else if (model->num_bones)
        {
                if (tagindex < 0 || tagindex >= model->num_bones)
                        return 4;
        {
                if (tagindex < 0 || tagindex >= model->num_bones)
                        return 4;
-               if (poseframe >= model->num_poses)
-                       return 6;
-               boneframe = model->data_poses + poseframe * model->num_bones * 12;
-               memcpy(bonematrix, boneframe + tagindex * 12, sizeof(float[12]));
-               while (model->data_bones[tagindex].parent >= 0)
+               Matrix4x4_Clear(&blendmatrix);
+               for (blendindex = 0;blendindex < MAX_FRAMEBLENDS && frameblend[blendindex].lerp > 0;blendindex++)
                {
                {
-                       memcpy(tempbonematrix, bonematrix, sizeof(float[12]));
-                       R_ConcatTransforms(boneframe + model->data_bones[tagindex].parent * 12, tempbonematrix, bonematrix);
-                       tagindex = model->data_bones[tagindex].parent;
+                       lerp = frameblend[blendindex].lerp;
+                       Matrix4x4_FromBonePose6s(&bonematrix, model->num_posescale, model->data_poses6s + 6 * (frameblend[blendindex].subframe * model->num_bones + tagindex));
+                       parenttagindex = tagindex;
+                       while ((parenttagindex = model->data_bones[parenttagindex].parent) >= 0)
+                       {
+                               Matrix4x4_FromBonePose6s(&parentbonematrix, model->num_posescale, model->data_poses6s + 6 * (frameblend[blendindex].subframe * model->num_bones + parenttagindex));
+                               tempbonematrix = bonematrix;
+                               Matrix4x4_Concat(&bonematrix, &parentbonematrix, &tempbonematrix);
+                       }
+                       Matrix4x4_Accumulate(&blendmatrix, &bonematrix, lerp);
                }
                }
-               Matrix4x4_FromArray12FloatD3D(outmatrix, bonematrix);
+               *outmatrix = blendmatrix;
        }
        else if (model->num_tags)
        {
                if (tagindex < 0 || tagindex >= model->num_tags)
                        return 4;
        }
        else if (model->num_tags)
        {
                if (tagindex < 0 || tagindex >= model->num_tags)
                        return 4;
-               if (poseframe >= model->num_tagframes)
-                       return 6;
-               Matrix4x4_FromArray12FloatGL(outmatrix, model->data_tags[poseframe * model->num_tags + tagindex].matrixgl);
+               for (k = 0;k < 12;k++)
+                       blendtag[k] = 0;
+               for (blendindex = 0;blendindex < MAX_FRAMEBLENDS && frameblend[blendindex].lerp > 0;blendindex++)
+               {
+                       lerp = frameblend[blendindex].lerp;
+                       input = model->data_tags[frameblend[blendindex].subframe * model->num_tags + tagindex].matrixgl;
+                       for (k = 0;k < 12;k++)
+                               blendtag[k] += input[k] * lerp;
+               }
+               Matrix4x4_FromArray12FloatGL(outmatrix, blendtag);
        }
        }
+
+       if(!mod_alias_supporttagscale.integer)
+               Matrix4x4_Normalize3(outmatrix, outmatrix);
+
        return 0;
 }
 
        return 0;
 }
 
+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)
+{
+       int blendindex;
+       int k;
+       float lerp;
+       matrix4x4_t bonematrix;
+       matrix4x4_t blendmatrix;
+       const float *input;
+       float blendtag[12];
+
+       if (skeleton && skeleton->relativetransforms)
+       {
+               if (tagindex < 0 || tagindex >= skeleton->model->num_bones)
+                       return 1;
+               *parentindex = skeleton->model->data_bones[tagindex].parent;
+               *tagname = skeleton->model->data_bones[tagindex].name;
+               *tag_localmatrix = skeleton->relativetransforms[tagindex];
+               return 0;
+       }
+       else if (model->num_bones)
+       {
+               if (tagindex < 0 || tagindex >= model->num_bones)
+                       return 1;
+               *parentindex = model->data_bones[tagindex].parent;
+               *tagname = model->data_bones[tagindex].name;
+               Matrix4x4_Clear(&blendmatrix);
+               for (blendindex = 0;blendindex < MAX_FRAMEBLENDS && frameblend[blendindex].lerp > 0;blendindex++)
+               {
+                       lerp = frameblend[blendindex].lerp;
+                       Matrix4x4_FromBonePose6s(&bonematrix, model->num_posescale, model->data_poses6s + 6 * (frameblend[blendindex].subframe * model->num_bones + tagindex));
+                       Matrix4x4_Accumulate(&blendmatrix, &bonematrix, lerp);
+               }
+               *tag_localmatrix = blendmatrix;
+               return 0;
+       }
+       else if (model->num_tags)
+       {
+               if (tagindex < 0 || tagindex >= model->num_tags)
+                       return 1;
+               *parentindex = -1;
+               *tagname = model->data_tags[tagindex].name;
+               for (k = 0;k < 12;k++)
+                       blendtag[k] = 0;
+               for (blendindex = 0;blendindex < MAX_FRAMEBLENDS && frameblend[blendindex].lerp > 0;blendindex++)
+               {
+                       lerp = frameblend[blendindex].lerp;
+                       input = model->data_tags[frameblend[blendindex].subframe * model->num_tags + tagindex].matrixgl;
+                       for (k = 0;k < 12;k++)
+                               blendtag[k] += input[k] * lerp;
+               }
+               Matrix4x4_FromArray12FloatGL(tag_localmatrix, blendtag);
+               return 0;
+       }
+
+       return 2;
+}
+
 int Mod_Alias_GetTagIndexForName(const dp_model_t *model, unsigned int skin, const char *tagname)
 {
        int i;
 int Mod_Alias_GetTagIndexForName(const dp_model_t *model, unsigned int skin, const char *tagname)
 {
        int i;
@@ -444,44 +500,25 @@ int Mod_Alias_GetTagIndexForName(const dp_model_t *model, unsigned int skin, con
 
 static void Mod_BuildBaseBonePoses(void)
 {
 
 static void Mod_BuildBaseBonePoses(void)
 {
-       int i, k;
-       double scale;
-       float *basebonepose = Mem_Alloc(tempmempool, loadmodel->num_bones * sizeof(float[12]));
-       float *in12f = loadmodel->data_poses;
-       float *out12f = basebonepose;
-       float *outinv12f = loadmodel->data_baseboneposeinverse;
-       for (i = 0;i < loadmodel->num_bones;i++, in12f += 12, out12f += 12, outinv12f += 12)
+       int boneindex;
+       matrix4x4_t *basebonepose;
+       float *outinvmatrix = loadmodel->data_baseboneposeinverse;
+       matrix4x4_t bonematrix;
+       matrix4x4_t tempbonematrix;
+       if (!loadmodel->num_bones)
+               return;
+       basebonepose = (matrix4x4_t *)Mem_Alloc(tempmempool, loadmodel->num_bones * sizeof(matrix4x4_t));
+       for (boneindex = 0;boneindex < loadmodel->num_bones;boneindex++)
        {
        {
-               if (loadmodel->data_bones[i].parent >= 0)
-                       R_ConcatTransforms(basebonepose + 12 * loadmodel->data_bones[i].parent, in12f, out12f);
-               else
-                       for (k = 0;k < 12;k++)
-                               out12f[k] = in12f[k];
-
-               // invert The Matrix
-
-               // we only support uniform scaling, so assume the first row is enough
-               // (note the lack of sqrt here, because we're trying to undo the scaling,
-               // this means multiplying by the inverse scale twice - squaring it, which
-               // makes the sqrt a waste of time)
-               scale = 1.0 / (out12f[ 0] * out12f[ 0] + out12f[ 1] * out12f[ 1] + out12f[ 2] * out12f[ 2]);
-
-               // invert the rotation by transposing and multiplying by the squared
-               // recipricol of the input matrix scale as described above
-               outinv12f[ 0] = (float)(out12f[ 0] * scale);
-               outinv12f[ 1] = (float)(out12f[ 4] * scale);
-               outinv12f[ 2] = (float)(out12f[ 8] * scale);
-               outinv12f[ 4] = (float)(out12f[ 1] * scale);
-               outinv12f[ 5] = (float)(out12f[ 5] * scale);
-               outinv12f[ 6] = (float)(out12f[ 9] * scale);
-               outinv12f[ 8] = (float)(out12f[ 2] * scale);
-               outinv12f[ 9] = (float)(out12f[ 6] * scale);
-               outinv12f[10] = (float)(out12f[10] * scale);
-
-               // invert the translate
-               outinv12f[ 3] = -(out12f[ 3] * outinv12f[ 0] + out12f[ 7] * outinv12f[ 1] + out12f[11] * outinv12f[ 2]);
-               outinv12f[ 7] = -(out12f[ 3] * outinv12f[ 4] + out12f[ 7] * outinv12f[ 5] + out12f[11] * outinv12f[ 6]);
-               outinv12f[11] = -(out12f[ 3] * outinv12f[ 8] + out12f[ 7] * outinv12f[ 9] + out12f[11] * outinv12f[10]);
+               Matrix4x4_FromBonePose6s(&bonematrix, loadmodel->num_posescale, loadmodel->data_poses6s + 6 * boneindex);
+               if (loadmodel->data_bones[boneindex].parent >= 0)
+               {
+                       tempbonematrix = bonematrix;
+                       Matrix4x4_Concat(&bonematrix, basebonepose + loadmodel->data_bones[boneindex].parent, &tempbonematrix);
+               }
+               basebonepose[boneindex] = bonematrix;
+               Matrix4x4_Invert_Simple(&tempbonematrix, basebonepose + boneindex);
+               Matrix4x4_ToArray12FloatD3D(&tempbonematrix, outinvmatrix + 12*boneindex);
        }
        Mem_Free(basebonepose);
 }
        }
        Mem_Free(basebonepose);
 }
@@ -492,19 +529,51 @@ static void Mod_Alias_CalculateBoundingBox(void)
        qboolean firstvertex = true;
        float dist, yawradius, radius;
        float *v;
        qboolean firstvertex = true;
        float dist, yawradius, radius;
        float *v;
-       float *vertex3f;
-       frameblend_t frameblend[4];
-       memset(frameblend, 0, sizeof(frameblend));
-       frameblend[0].lerp = 1;
-       vertex3f = Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_vertices * sizeof(float[3]));
        VectorClear(loadmodel->normalmins);
        VectorClear(loadmodel->normalmaxs);
        yawradius = 0;
        radius = 0;
        VectorClear(loadmodel->normalmins);
        VectorClear(loadmodel->normalmaxs);
        yawradius = 0;
        radius = 0;
-       for (frameblend[0].frame = 0;frameblend[0].frame < loadmodel->num_poses;frameblend[0].frame++)
+       if (loadmodel->AnimateVertices)
+       {
+               float *vertex3f;
+               frameblend_t frameblend[MAX_FRAMEBLENDS];
+               memset(frameblend, 0, sizeof(frameblend));
+               frameblend[0].lerp = 1;
+               vertex3f = (float *) Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_vertices * sizeof(float[3]));
+               for (frameblend[0].subframe = 0;frameblend[0].subframe < loadmodel->num_poses;frameblend[0].subframe++)
+               {
+                       loadmodel->AnimateVertices(loadmodel, frameblend, NULL, vertex3f, NULL, NULL, NULL);
+                       for (vnum = 0, v = vertex3f;vnum < loadmodel->surfmesh.num_vertices;vnum++, v += 3)
+                       {
+                               if (firstvertex)
+                               {
+                                       firstvertex = false;
+                                       VectorCopy(v, loadmodel->normalmins);
+                                       VectorCopy(v, loadmodel->normalmaxs);
+                               }
+                               else
+                               {
+                                       if (loadmodel->normalmins[0] > v[0]) loadmodel->normalmins[0] = v[0];
+                                       if (loadmodel->normalmins[1] > v[1]) loadmodel->normalmins[1] = v[1];
+                                       if (loadmodel->normalmins[2] > v[2]) loadmodel->normalmins[2] = v[2];
+                                       if (loadmodel->normalmaxs[0] < v[0]) loadmodel->normalmaxs[0] = v[0];
+                                       if (loadmodel->normalmaxs[1] < v[1]) loadmodel->normalmaxs[1] = v[1];
+                                       if (loadmodel->normalmaxs[2] < v[2]) loadmodel->normalmaxs[2] = v[2];
+                               }
+                               dist = v[0] * v[0] + v[1] * v[1];
+                               if (yawradius < dist)
+                                       yawradius = dist;
+                               dist += v[2] * v[2];
+                               if (radius < dist)
+                                       radius = dist;
+                       }
+               }
+               if (vertex3f)
+                       Mem_Free(vertex3f);
+       }
+       else
        {
        {
-               loadmodel->AnimateVertices(loadmodel, frameblend, vertex3f, NULL, NULL, NULL);
-               for (vnum = 0, v = vertex3f;vnum < loadmodel->surfmesh.num_vertices;vnum++, v += 3)
+               for (vnum = 0, v = loadmodel->surfmesh.data_vertex3f;vnum < loadmodel->surfmesh.num_vertices;vnum++, v += 3)
                {
                        if (firstvertex)
                        {
                {
                        if (firstvertex)
                        {
@@ -529,7 +598,6 @@ static void Mod_Alias_CalculateBoundingBox(void)
                                radius = dist;
                }
        }
                                radius = dist;
                }
        }
-       Mem_Free(vertex3f);
        radius = sqrt(radius);
        yawradius = sqrt(yawradius);
        loadmodel->yawmins[0] = loadmodel->yawmins[1] = -yawradius;
        radius = sqrt(radius);
        yawradius = sqrt(yawradius);
        loadmodel->yawmins[0] = loadmodel->yawmins[1] = -yawradius;
@@ -545,8 +613,10 @@ static void Mod_Alias_CalculateBoundingBox(void)
 static void Mod_Alias_MorphMesh_CompileFrames(void)
 {
        int i, j;
 static void Mod_Alias_MorphMesh_CompileFrames(void)
 {
        int i, j;
-       frameblend_t frameblend[4] = {{0, 1}, {0, 0}, {0, 0}, {0, 0}};
+       frameblend_t frameblend[MAX_FRAMEBLENDS];
        unsigned char *datapointer;
        unsigned char *datapointer;
+       memset(frameblend, 0, sizeof(frameblend));
+       frameblend[0].lerp = 1;
        datapointer = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_vertices * (sizeof(float[3]) * 4 + loadmodel->surfmesh.num_morphframes * sizeof(texvecvertex_t)));
        loadmodel->surfmesh.data_vertex3f = (float *)datapointer;datapointer += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
        loadmodel->surfmesh.data_svector3f = (float *)datapointer;datapointer += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
        datapointer = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_vertices * (sizeof(float[3]) * 4 + loadmodel->surfmesh.num_morphframes * sizeof(texvecvertex_t)));
        loadmodel->surfmesh.data_vertex3f = (float *)datapointer;datapointer += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
        loadmodel->surfmesh.data_svector3f = (float *)datapointer;datapointer += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
@@ -556,85 +626,88 @@ static void Mod_Alias_MorphMesh_CompileFrames(void)
        // 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)
        for (i = loadmodel->surfmesh.num_morphframes-1;i >= 0;i--)
        {
        // 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)
        for (i = loadmodel->surfmesh.num_morphframes-1;i >= 0;i--)
        {
-               frameblend[0].frame = i;
-               loadmodel->AnimateVertices(loadmodel, frameblend, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_normal3f, NULL, NULL);
-               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);
+               frameblend[0].subframe = i;
+               loadmodel->AnimateVertices(loadmodel, frameblend, NULL, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_normal3f, NULL, NULL);
+               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);
                // encode the svector and tvector in 3 byte format for permanent storage
                for (j = 0;j < loadmodel->surfmesh.num_vertices;j++)
                {
                // encode the svector and tvector in 3 byte format for permanent storage
                for (j = 0;j < loadmodel->surfmesh.num_vertices;j++)
                {
-                       VectorScale(loadmodel->surfmesh.data_svector3f + j * 3, 127.0f, loadmodel->surfmesh.data_morphtexvecvertex[i*loadmodel->surfmesh.num_vertices+j].svec);
-                       VectorScale(loadmodel->surfmesh.data_tvector3f + j * 3, 127.0f, loadmodel->surfmesh.data_morphtexvecvertex[i*loadmodel->surfmesh.num_vertices+j].tvec);
+                       VectorScaleCast(loadmodel->surfmesh.data_svector3f + j * 3, 127.0f, signed char, loadmodel->surfmesh.data_morphtexvecvertex[i*loadmodel->surfmesh.num_vertices+j].svec);
+                       VectorScaleCast(loadmodel->surfmesh.data_tvector3f + j * 3, 127.0f, signed char, loadmodel->surfmesh.data_morphtexvecvertex[i*loadmodel->surfmesh.num_vertices+j].tvec);
                }
        }
 }
 
                }
        }
 }
 
-static void Mod_MDLMD2MD3_TraceBox(dp_model_t *model, int frame, trace_t *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask)
+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 i;
        float segmentmins[3], segmentmaxs[3];
 {
        int i;
        float segmentmins[3], segmentmaxs[3];
-       frameblend_t frameblend[4];
        msurface_t *surface;
        msurface_t *surface;
-       static int maxvertices = 0;
-       static float *vertex3f = NULL;
+       float vertex3fbuf[1024*3];
+       float *vertex3f = vertex3fbuf;
        memset(trace, 0, sizeof(*trace));
        trace->fraction = 1;
        trace->realfraction = 1;
        trace->hitsupercontentsmask = hitsupercontentsmask;
        memset(trace, 0, sizeof(*trace));
        trace->fraction = 1;
        trace->realfraction = 1;
        trace->hitsupercontentsmask = hitsupercontentsmask;
-       memset(frameblend, 0, sizeof(frameblend));
-       frameblend[0].frame = frame;
-       frameblend[0].lerp = 1;
-       if (maxvertices < model->surfmesh.num_vertices)
-       {
-               if (vertex3f)
-                       Z_Free(vertex3f);
-               maxvertices = (model->surfmesh.num_vertices + 255) & ~255;
-               vertex3f = (float *)Z_Malloc(maxvertices * sizeof(float[3]));
-       }
-       if (VectorLength2(boxmins) + VectorLength2(boxmaxs) == 0)
-       {
-               // line trace
-               segmentmins[0] = min(start[0], end[0]) - 1;
-               segmentmins[1] = min(start[1], end[1]) - 1;
-               segmentmins[2] = min(start[2], end[2]) - 1;
-               segmentmaxs[0] = max(start[0], end[0]) + 1;
-               segmentmaxs[1] = max(start[1], end[1]) + 1;
-               segmentmaxs[2] = max(start[2], end[2]) + 1;
-               for (i = 0, surface = model->data_surfaces;i < model->num_surfaces;i++, surface++)
-               {
-                       model->AnimateVertices(model, frameblend, vertex3f, NULL, NULL, NULL);
-                       Collision_TraceLineTriangleMeshFloat(trace, start, end, model->surfmesh.num_triangles, model->surfmesh.data_element3i, vertex3f, SUPERCONTENTS_SOLID, 0, surface->texture, segmentmins, segmentmaxs);
-               }
-       }
-       else
+       if (model->surfmesh.num_vertices > 1024)
+               vertex3f = Mem_Alloc(tempmempool, model->surfmesh.num_vertices * sizeof(float[3]));
+       segmentmins[0] = min(start[0], end[0]) - 1;
+       segmentmins[1] = min(start[1], end[1]) - 1;
+       segmentmins[2] = min(start[2], end[2]) - 1;
+       segmentmaxs[0] = max(start[0], end[0]) + 1;
+       segmentmaxs[1] = max(start[1], end[1]) + 1;
+       segmentmaxs[2] = max(start[2], end[2]) + 1;
+       model->AnimateVertices(model, frameblend, skeleton, vertex3f, NULL, NULL, NULL);
+       for (i = 0, surface = model->data_surfaces;i < model->num_surfaces;i++, surface++)
+               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);
+       if (vertex3f != vertex3fbuf)
+               Mem_Free(vertex3f);
+}
+
+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 i;
+       vec3_t shiftstart, shiftend;
+       float segmentmins[3], segmentmaxs[3];
+       msurface_t *surface;
+       float vertex3fbuf[1024*3];
+       float *vertex3f = vertex3fbuf;
+       colboxbrushf_t thisbrush_start, thisbrush_end;
+       vec3_t boxstartmins, boxstartmaxs, boxendmins, boxendmaxs;
+
+       if (VectorCompare(boxmins, boxmaxs))
        {
        {
-               // box trace, performed as brush trace
-               colbrushf_t *thisbrush_start, *thisbrush_end;
-               vec3_t boxstartmins, boxstartmaxs, boxendmins, boxendmaxs;
-               segmentmins[0] = min(start[0], end[0]) + boxmins[0] - 1;
-               segmentmins[1] = min(start[1], end[1]) + boxmins[1] - 1;
-               segmentmins[2] = min(start[2], end[2]) + boxmins[2] - 1;
-               segmentmaxs[0] = max(start[0], end[0]) + boxmaxs[0] + 1;
-               segmentmaxs[1] = max(start[1], end[1]) + boxmaxs[1] + 1;
-               segmentmaxs[2] = max(start[2], end[2]) + boxmaxs[2] + 1;
-               VectorAdd(start, boxmins, boxstartmins);
-               VectorAdd(start, boxmaxs, boxstartmaxs);
-               VectorAdd(end, boxmins, boxendmins);
-               VectorAdd(end, boxmaxs, boxendmaxs);
-               thisbrush_start = Collision_BrushForBox(&identitymatrix, boxstartmins, boxstartmaxs, 0, 0, NULL);
-               thisbrush_end = Collision_BrushForBox(&identitymatrix, boxendmins, boxendmaxs, 0, 0, NULL);
-               for (i = 0, surface = model->data_surfaces;i < model->num_surfaces;i++, surface++)
-               {
-                       if (maxvertices < model->surfmesh.num_vertices)
-                       {
-                               if (vertex3f)
-                                       Z_Free(vertex3f);
-                               maxvertices = (model->surfmesh.num_vertices + 255) & ~255;
-                               vertex3f = (float *)Z_Malloc(maxvertices * sizeof(float[3]));
-                       }
-                       model->AnimateVertices(model, frameblend, vertex3f, NULL, NULL, NULL);
-                       Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, model->surfmesh.num_triangles, model->surfmesh.data_element3i, vertex3f, SUPERCONTENTS_SOLID, 0, surface->texture, segmentmins, segmentmaxs);
-               }
+               VectorAdd(start, boxmins, shiftstart);
+               VectorAdd(end, boxmins, shiftend);
+               Mod_MDLMD2MD3_TraceLine(model, frameblend, skeleton, trace, shiftstart, shiftend, hitsupercontentsmask);
+               VectorSubtract(trace->endpos, boxmins, trace->endpos);
+               return;
        }
        }
+
+       // box trace, performed as brush trace
+       memset(trace, 0, sizeof(*trace));
+       trace->fraction = 1;
+       trace->realfraction = 1;
+       trace->hitsupercontentsmask = hitsupercontentsmask;
+       if (model->surfmesh.num_vertices > 1024)
+               vertex3f = Mem_Alloc(tempmempool, model->surfmesh.num_vertices * sizeof(float[3]));
+       segmentmins[0] = min(start[0], end[0]) + boxmins[0] - 1;
+       segmentmins[1] = min(start[1], end[1]) + boxmins[1] - 1;
+       segmentmins[2] = min(start[2], end[2]) + boxmins[2] - 1;
+       segmentmaxs[0] = max(start[0], end[0]) + boxmaxs[0] + 1;
+       segmentmaxs[1] = max(start[1], end[1]) + boxmaxs[1] + 1;
+       segmentmaxs[2] = max(start[2], end[2]) + boxmaxs[2] + 1;
+       VectorAdd(start, boxmins, boxstartmins);
+       VectorAdd(start, boxmaxs, boxstartmaxs);
+       VectorAdd(end, boxmins, boxendmins);
+       VectorAdd(end, boxmaxs, boxendmaxs);
+       Collision_BrushForBox(&thisbrush_start, boxstartmins, boxstartmaxs, 0, 0, NULL);
+       Collision_BrushForBox(&thisbrush_end, boxendmins, boxendmaxs, 0, 0, NULL);
+       model->AnimateVertices(model, frameblend, skeleton, vertex3f, NULL, NULL, NULL);
+       for (i = 0, surface = model->data_surfaces;i < model->num_surfaces;i++, surface++)
+               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);
+       if (vertex3f != vertex3fbuf)
+               Mem_Free(vertex3f);
 }
 
 static void Mod_ConvertAliasVerts (int inverts, trivertx_t *v, trivertx_t *out, int *vertremap)
 }
 
 static void Mod_ConvertAliasVerts (int inverts, trivertx_t *v, trivertx_t *out, int *vertremap)
@@ -738,15 +811,27 @@ static void Mod_BuildAliasSkinFromSkinFrame(texture_t *texture, skinframe_t *ski
        //texture->textureflags = 0;
 
        texture->basematerialflags = MATERIALFLAG_WALL;
        //texture->textureflags = 0;
 
        texture->basematerialflags = MATERIALFLAG_WALL;
-       if (texture->currentskinframe->fog)
+       if (texture->currentskinframe->hasalpha)
                texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
        texture->currentmaterialflags = texture->basematerialflags;
                texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
        texture->currentmaterialflags = texture->basematerialflags;
+       texture->offsetmapping = OFFSETMAPPING_DEFAULT;
+       texture->offsetscale = 1;
+       texture->offsetbias = 0;
+       texture->specularscalemod = 1;
+       texture->specularpowermod = 1;
+       texture->surfaceflags = 0;
+       texture->supercontents = SUPERCONTENTS_SOLID;
+       if (!(texture->basematerialflags & MATERIALFLAG_BLENDED))
+               texture->supercontents |= SUPERCONTENTS_OPAQUE;
 }
 
 }
 
-static void Mod_BuildAliasSkinsFromSkinFiles(texture_t *skin, skinfile_t *skinfile, char *meshname, char *shadername)
+void Mod_BuildAliasSkinsFromSkinFiles(texture_t *skin, skinfile_t *skinfile, const char *meshname, const char *shadername)
 {
        int i;
 {
        int i;
+       char stripbuf[MAX_QPATH];
        skinfileitem_t *skinfileitem;
        skinfileitem_t *skinfileitem;
+       if(developer_extra.integer)
+               Con_DPrintf("Looking up texture for %s (default: %s)\n", meshname, shadername);
        if (skinfile)
        {
                // the skin += loadmodel->num_surfaces part of this is because data_textures on alias models is arranged as [numskins][numsurfaces]
        if (skinfile)
        {
                // the skin += loadmodel->num_surfaces part of this is because data_textures on alias models is arranged as [numskins][numsurfaces]
@@ -759,7 +844,10 @@ static void Mod_BuildAliasSkinsFromSkinFiles(texture_t *skin, skinfile_t *skinfi
                                // leave the skin unitialized (nodraw) if the replacement is "common/nodraw" or "textures/common/nodraw"
                                if (!strcmp(skinfileitem->name, meshname))
                                {
                                // leave the skin unitialized (nodraw) if the replacement is "common/nodraw" or "textures/common/nodraw"
                                if (!strcmp(skinfileitem->name, meshname))
                                {
-                                       Mod_LoadTextureFromQ3Shader(skin, skinfileitem->replacement, true, true, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP | TEXF_COMPRESS);
+                                       Image_StripImageExtension(skinfileitem->replacement, stripbuf, sizeof(stripbuf));
+                                       if(developer_extra.integer)
+                                               Con_DPrintf("--> got %s from skin file\n", stripbuf);
+                                       Mod_LoadTextureFromQ3Shader(skin, stripbuf, true, true, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PICMIP | TEXF_COMPRESS);
                                        break;
                                }
                        }
                                        break;
                                }
                        }
@@ -767,12 +855,19 @@ static void Mod_BuildAliasSkinsFromSkinFiles(texture_t *skin, skinfile_t *skinfi
                        {
                                // don't render unmentioned meshes
                                Mod_BuildAliasSkinFromSkinFrame(skin, NULL);
                        {
                                // don't render unmentioned meshes
                                Mod_BuildAliasSkinFromSkinFrame(skin, NULL);
-                               skin->basematerialflags = skin->currentmaterialflags = 0;
+                               if(developer_extra.integer)
+                                       Con_DPrintf("--> skipping\n");
+                               skin->basematerialflags = skin->currentmaterialflags = MATERIALFLAG_NOSHADOW | MATERIALFLAG_NODRAW;
                        }
                }
        }
        else
                        }
                }
        }
        else
-               Mod_LoadTextureFromQ3Shader(skin, shadername, true, true, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP | TEXF_COMPRESS);
+       {
+               if(developer_extra.integer)
+                       Con_DPrintf("--> using default\n");
+               Image_StripImageExtension(shadername, stripbuf, sizeof(stripbuf));
+               Mod_LoadTextureFromQ3Shader(skin, stripbuf, true, true, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PICMIP | TEXF_COMPRESS);
+       }
 }
 
 #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);
 }
 
 #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);
@@ -799,6 +894,7 @@ void Mod_IDP0_Load(dp_model_t *mod, void *buffer, void *bufferend)
        float *vertst;
        int *vertonseam, *vertremap;
        skinfile_t *skinfiles;
        float *vertst;
        int *vertonseam, *vertremap;
        skinfile_t *skinfiles;
+       char vabuf[1024];
 
        datapointer = (unsigned char *)buffer;
        pinmodel = (mdl_t *)datapointer;
 
        datapointer = (unsigned char *)buffer;
        pinmodel = (mdl_t *)datapointer;
@@ -812,24 +908,28 @@ void Mod_IDP0_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->modeldatatypestring = "MDL";
 
        loadmodel->type = mod_alias;
        loadmodel->modeldatatypestring = "MDL";
 
        loadmodel->type = mod_alias;
-       loadmodel->AnimateVertices = Mod_MDL_AnimateVertices;
        loadmodel->DrawSky = NULL;
        loadmodel->DrawAddWaterPlanes = NULL;
        loadmodel->Draw = R_Q1BSP_Draw;
        loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
        loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
        loadmodel->DrawSky = NULL;
        loadmodel->DrawAddWaterPlanes = NULL;
        loadmodel->Draw = R_Q1BSP_Draw;
        loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
        loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
+       loadmodel->DrawPrepass = R_Q1BSP_DrawPrepass;
+       loadmodel->CompileShadowMap = R_Q1BSP_CompileShadowMap;
+       loadmodel->DrawShadowMap = R_Q1BSP_DrawShadowMap;
        loadmodel->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
        loadmodel->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
        loadmodel->DrawLight = R_Q1BSP_DrawLight;
        loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
        loadmodel->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
        loadmodel->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
        loadmodel->DrawLight = R_Q1BSP_DrawLight;
        loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
+       loadmodel->TraceLine = Mod_MDLMD2MD3_TraceLine;
+       // FIXME add TraceBrush!
        loadmodel->PointSuperContents = NULL;
 
        loadmodel->num_surfaces = 1;
        loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
        data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int));
        loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
        loadmodel->PointSuperContents = NULL;
 
        loadmodel->num_surfaces = 1;
        loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
        data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int));
        loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
-       loadmodel->surfacelist = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
-       loadmodel->surfacelist[0] = 0;
+       loadmodel->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
+       loadmodel->sortedmodelsurfaces[0] = 0;
 
        loadmodel->numskins = LittleLong(pinmodel->numskins);
        BOUNDI(loadmodel->numskins,0,65536);
 
        loadmodel->numskins = LittleLong(pinmodel->numskins);
        BOUNDI(loadmodel->numskins,0,65536);
@@ -844,7 +944,7 @@ void Mod_IDP0_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->numframes = LittleLong(pinmodel->numframes);
        BOUNDI(loadmodel->numframes,0,65536);
        loadmodel->synctype = (synctype_t)LittleLong (pinmodel->synctype);
        loadmodel->numframes = LittleLong(pinmodel->numframes);
        BOUNDI(loadmodel->numframes,0,65536);
        loadmodel->synctype = (synctype_t)LittleLong (pinmodel->synctype);
-       BOUNDI(loadmodel->synctype,0,2);
+       BOUNDI((int)loadmodel->synctype,0,2);
        // convert model flags to EF flags (MF_ROCKET becomes EF_ROCKET, etc)
        i = LittleLong (pinmodel->flags);
        loadmodel->effects = ((i & 255) << 24) | (i & 0x00FFFF00);
        // convert model flags to EF flags (MF_ROCKET becomes EF_ROCKET, etc)
        i = LittleLong (pinmodel->flags);
        loadmodel->effects = ((i & 255) << 24) | (i & 0x00FFFF00);
@@ -975,20 +1075,25 @@ void Mod_IDP0_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
        // generate ushort elements array if possible
        if (loadmodel->surfmesh.num_vertices <= 65536)
 
        // generate ushort elements array if possible
        if (loadmodel->surfmesh.num_vertices <= 65536)
-       {
                loadmodel->surfmesh.data_element3s = (unsigned short *)Mem_Alloc(loadmodel->mempool, sizeof(unsigned short[3]) * loadmodel->surfmesh.num_triangles);
                loadmodel->surfmesh.data_element3s = (unsigned short *)Mem_Alloc(loadmodel->mempool, sizeof(unsigned short[3]) * loadmodel->surfmesh.num_triangles);
+       if (loadmodel->surfmesh.data_element3s)
                for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
                        loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
                for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
                        loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
-       }
 
 // load the frames
        loadmodel->animscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, sizeof(animscene_t) * loadmodel->numframes);
        loadmodel->surfmesh.data_morphmdlvertex = (trivertx_t *)Mem_Alloc(loadmodel->mempool, sizeof(trivertx_t) * loadmodel->surfmesh.num_morphframes * loadmodel->surfmesh.num_vertices);
 
 // load the frames
        loadmodel->animscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, sizeof(animscene_t) * loadmodel->numframes);
        loadmodel->surfmesh.data_morphmdlvertex = (trivertx_t *)Mem_Alloc(loadmodel->mempool, sizeof(trivertx_t) * loadmodel->surfmesh.num_morphframes * loadmodel->surfmesh.num_vertices);
-       loadmodel->surfmesh.data_neighbor3i = (int *)Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_triangles * sizeof(int[3]));
+       if (r_enableshadowvolumes.integer)
+       {
+               loadmodel->surfmesh.data_neighbor3i = (int *)Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_triangles * sizeof(int[3]));
+       }
        Mod_MDL_LoadFrames (startframes, numverts, vertremap);
        Mod_MDL_LoadFrames (startframes, numverts, vertremap);
-       Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
+       loadmodel->AnimateVertices = Mod_MDL_AnimateVertices; // needed during loading, may be cleared by code later in this function
+       if (loadmodel->surfmesh.data_neighbor3i)
+               Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
        Mod_Alias_CalculateBoundingBox();
        Mod_Alias_MorphMesh_CompileFrames();
        Mod_Alias_CalculateBoundingBox();
        Mod_Alias_MorphMesh_CompileFrames();
+       loadmodel->AnimateVertices = loadmodel->surfmesh.isanimated ? Mod_MDL_AnimateVertices : NULL;
 
        Mem_Free(vertst);
        Mem_Free(vertremap);
 
        Mem_Free(vertst);
        Mem_Free(vertremap);
@@ -1068,7 +1173,7 @@ void Mod_IDP0_Load(dp_model_t *mod, void *buffer, void *bufferend)
                // check for skins that don't exist in the model, but do exist as external images
                // (this was added because yummyluv kept pestering me about support for it)
                // TODO: support shaders here?
                // check for skins that don't exist in the model, but do exist as external images
                // (this was added because yummyluv kept pestering me about support for it)
                // TODO: support shaders here?
-               while ((tempskinframe = R_SkinFrame_LoadExternal(va("%s_%i", loadmodel->name, loadmodel->numskins), (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PICMIP | TEXF_COMPRESS, false)))
+               while ((tempskinframe = R_SkinFrame_LoadExternal(va(vabuf, sizeof(vabuf), "%s_%i", loadmodel->name, loadmodel->numskins), (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PICMIP | TEXF_COMPRESS, false)))
                {
                        // expand the arrays to make room
                        tempskinscenes = loadmodel->skinscenes;
                {
                        // expand the arrays to make room
                        tempskinscenes = loadmodel->skinscenes;
@@ -1107,7 +1212,27 @@ void Mod_IDP0_Load(dp_model_t *mod, void *buffer, void *bufferend)
        surface->num_firstvertex = 0;
        surface->num_vertices = loadmodel->surfmesh.num_vertices;
 
        surface->num_firstvertex = 0;
        surface->num_vertices = loadmodel->surfmesh.num_vertices;
 
-       loadmodel->surfmesh.isanimated = loadmodel->numframes > 1 || loadmodel->animscenes[0].framecount > 1;
+       loadmodel->surfmesh.isanimated = loadmodel->numframes > 1 || (loadmodel->animscenes && loadmodel->animscenes[0].framecount > 1);
+       loadmodel->AnimateVertices = loadmodel->surfmesh.isanimated ? Mod_MDL_AnimateVertices : NULL;
+
+       if (!loadmodel->surfmesh.isanimated)
+       {
+               Mod_MakeCollisionBIH(loadmodel, true, &loadmodel->collision_bih);
+               loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
+               loadmodel->TraceBrush = Mod_CollisionBIH_TraceBrush;
+               loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
+               loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
+               loadmodel->PointSuperContents = Mod_CollisionBIH_PointSuperContents_Mesh;
+       }
+
+       // because shaders can do somewhat unexpected things, check for unusual features now
+       for (i = 0;i < loadmodel->num_textures;i++)
+       {
+               if (loadmodel->data_textures[i].basematerialflags & (MATERIALFLAG_SKY))
+                       mod->DrawSky = R_Q1BSP_DrawSky;
+               if (loadmodel->data_textures[i].basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
+                       mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
+       }
 }
 
 void Mod_IDP2_Load(dp_model_t *mod, void *buffer, void *bufferend)
 }
 
 void Mod_IDP2_Load(dp_model_t *mod, void *buffer, void *bufferend)
@@ -1142,16 +1267,19 @@ void Mod_IDP2_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->modeldatatypestring = "MD2";
 
        loadmodel->type = mod_alias;
        loadmodel->modeldatatypestring = "MD2";
 
        loadmodel->type = mod_alias;
-       loadmodel->AnimateVertices = Mod_MDL_AnimateVertices;
        loadmodel->DrawSky = NULL;
        loadmodel->DrawAddWaterPlanes = NULL;
        loadmodel->Draw = R_Q1BSP_Draw;
        loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
        loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
        loadmodel->DrawSky = NULL;
        loadmodel->DrawAddWaterPlanes = NULL;
        loadmodel->Draw = R_Q1BSP_Draw;
        loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
        loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
+       loadmodel->DrawPrepass = R_Q1BSP_DrawPrepass;
+       loadmodel->CompileShadowMap = R_Q1BSP_CompileShadowMap;
+       loadmodel->DrawShadowMap = R_Q1BSP_DrawShadowMap;
        loadmodel->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
        loadmodel->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
        loadmodel->DrawLight = R_Q1BSP_DrawLight;
        loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
        loadmodel->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
        loadmodel->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
        loadmodel->DrawLight = R_Q1BSP_DrawLight;
        loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
+       loadmodel->TraceLine = Mod_MDLMD2MD3_TraceLine;
        loadmodel->PointSuperContents = NULL;
 
        if (LittleLong(pinmodel->num_tris) < 1 || LittleLong(pinmodel->num_tris) > 65536)
        loadmodel->PointSuperContents = NULL;
 
        if (LittleLong(pinmodel->num_tris) < 1 || LittleLong(pinmodel->num_tris) > 65536)
@@ -1189,14 +1317,17 @@ void Mod_IDP2_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
        loadmodel->num_surfaces = 1;
        loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
 
        loadmodel->num_surfaces = 1;
        loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
-       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]) + loadmodel->surfmesh.num_triangles * sizeof(int[3]));
+       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]) + (r_enableshadowvolumes.integer ? loadmodel->surfmesh.num_triangles * sizeof(int[3]) : 0));
        loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
        loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
-       loadmodel->surfacelist = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
-       loadmodel->surfacelist[0] = 0;
+       loadmodel->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
+       loadmodel->sortedmodelsurfaces[0] = 0;
        loadmodel->animscenes = (animscene_t *)data;data += loadmodel->numframes * sizeof(animscene_t);
        loadmodel->surfmesh.data_morphmd2framesize6f = (float *)data;data += loadmodel->numframes * sizeof(float[6]);
        loadmodel->surfmesh.data_element3i = (int *)data;data += loadmodel->surfmesh.num_triangles * sizeof(int[3]);
        loadmodel->animscenes = (animscene_t *)data;data += loadmodel->numframes * sizeof(animscene_t);
        loadmodel->surfmesh.data_morphmd2framesize6f = (float *)data;data += loadmodel->numframes * sizeof(float[6]);
        loadmodel->surfmesh.data_element3i = (int *)data;data += loadmodel->surfmesh.num_triangles * sizeof(int[3]);
-       loadmodel->surfmesh.data_neighbor3i = (int *)data;data += loadmodel->surfmesh.num_triangles * sizeof(int[3]);
+       if (r_enableshadowvolumes.integer)
+       {
+               loadmodel->surfmesh.data_neighbor3i = (int *)data;data += loadmodel->surfmesh.num_triangles * sizeof(int[3]);
+       }
 
        loadmodel->synctype = ST_RAND;
 
 
        loadmodel->synctype = ST_RAND;
 
@@ -1218,7 +1349,7 @@ void Mod_IDP2_Load(dp_model_t *mod, void *buffer, void *bufferend)
                loadmodel->num_texturesperskin = loadmodel->num_surfaces;
                loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
                for (i = 0;i < loadmodel->numskins;i++, inskin += MD2_SKINNAME)
                loadmodel->num_texturesperskin = loadmodel->num_surfaces;
                loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
                for (i = 0;i < loadmodel->numskins;i++, inskin += MD2_SKINNAME)
-                       Mod_LoadTextureFromQ3Shader(loadmodel->data_textures + i * loadmodel->num_surfaces, inskin, true, true, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP | TEXF_COMPRESS);
+                       Mod_LoadTextureFromQ3Shader(loadmodel->data_textures + i * loadmodel->num_surfaces, inskin, true, true, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PICMIP | TEXF_COMPRESS);
        }
        else
        {
        }
        else
        {
@@ -1304,11 +1435,10 @@ void Mod_IDP2_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
        // generate ushort elements array if possible
        if (loadmodel->surfmesh.num_vertices <= 65536)
 
        // generate ushort elements array if possible
        if (loadmodel->surfmesh.num_vertices <= 65536)
-       {
                loadmodel->surfmesh.data_element3s = (unsigned short *)Mem_Alloc(loadmodel->mempool, sizeof(unsigned short[3]) * loadmodel->surfmesh.num_triangles);
                loadmodel->surfmesh.data_element3s = (unsigned short *)Mem_Alloc(loadmodel->mempool, sizeof(unsigned short[3]) * loadmodel->surfmesh.num_triangles);
+       if (loadmodel->surfmesh.data_element3s)
                for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
                        loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
                for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
                        loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
-       }
 
        // load the frames
        datapointer = (base + LittleLong(pinmodel->ofs_frames));
 
        // load the frames
        datapointer = (base + LittleLong(pinmodel->ofs_frames));
@@ -1341,9 +1471,13 @@ void Mod_IDP2_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
        Mem_Free(vertremap);
 
 
        Mem_Free(vertremap);
 
-       Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
+       loadmodel->surfmesh.isanimated = loadmodel->numframes > 1 || (loadmodel->animscenes && loadmodel->animscenes[0].framecount > 1);
+       loadmodel->AnimateVertices = Mod_MDL_AnimateVertices; // needed during loading, may be cleared by code later in this function
+       if (loadmodel->surfmesh.data_neighbor3i)
+               Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
        Mod_Alias_CalculateBoundingBox();
        Mod_Alias_MorphMesh_CompileFrames();
        Mod_Alias_CalculateBoundingBox();
        Mod_Alias_MorphMesh_CompileFrames();
+       loadmodel->AnimateVertices = loadmodel->surfmesh.isanimated ? Mod_MDL_AnimateVertices : NULL;
 
        surface = loadmodel->data_surfaces;
        surface->texture = loadmodel->data_textures;
 
        surface = loadmodel->data_surfaces;
        surface->texture = loadmodel->data_textures;
@@ -1352,7 +1486,24 @@ void Mod_IDP2_Load(dp_model_t *mod, void *buffer, void *bufferend)
        surface->num_firstvertex = 0;
        surface->num_vertices = loadmodel->surfmesh.num_vertices;
 
        surface->num_firstvertex = 0;
        surface->num_vertices = loadmodel->surfmesh.num_vertices;
 
-       loadmodel->surfmesh.isanimated = loadmodel->numframes > 1 || loadmodel->animscenes[0].framecount > 1;
+       if (!loadmodel->surfmesh.isanimated)
+       {
+               Mod_MakeCollisionBIH(loadmodel, true, &loadmodel->collision_bih);
+               loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
+               loadmodel->TraceBrush = Mod_CollisionBIH_TraceBrush;
+               loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
+               loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
+               loadmodel->PointSuperContents = Mod_CollisionBIH_PointSuperContents_Mesh;
+       }
+
+       // because shaders can do somewhat unexpected things, check for unusual features now
+       for (i = 0;i < loadmodel->num_textures;i++)
+       {
+               if (loadmodel->data_textures[i].basematerialflags & (MATERIALFLAG_SKY))
+                       mod->DrawSky = R_Q1BSP_DrawSky;
+               if (loadmodel->data_textures[i].basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
+                       mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
+       }
 }
 
 void Mod_IDP3_Load(dp_model_t *mod, void *buffer, void *bufferend)
 }
 
 void Mod_IDP3_Load(dp_model_t *mod, void *buffer, void *bufferend)
@@ -1382,16 +1533,19 @@ void Mod_IDP3_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->modeldatatypestring = "MD3";
 
        loadmodel->type = mod_alias;
        loadmodel->modeldatatypestring = "MD3";
 
        loadmodel->type = mod_alias;
-       loadmodel->AnimateVertices = Mod_MD3_AnimateVertices;
        loadmodel->DrawSky = NULL;
        loadmodel->DrawAddWaterPlanes = NULL;
        loadmodel->Draw = R_Q1BSP_Draw;
        loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
        loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
        loadmodel->DrawSky = NULL;
        loadmodel->DrawAddWaterPlanes = NULL;
        loadmodel->Draw = R_Q1BSP_Draw;
        loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
        loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
+       loadmodel->DrawPrepass = R_Q1BSP_DrawPrepass;
+       loadmodel->CompileShadowMap = R_Q1BSP_CompileShadowMap;
+       loadmodel->DrawShadowMap = R_Q1BSP_DrawShadowMap;
        loadmodel->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
        loadmodel->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
        loadmodel->DrawLight = R_Q1BSP_DrawLight;
        loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
        loadmodel->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
        loadmodel->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
        loadmodel->DrawLight = R_Q1BSP_DrawLight;
        loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
+       loadmodel->TraceLine = Mod_MDLMD2MD3_TraceLine;
        loadmodel->PointSuperContents = NULL;
        loadmodel->synctype = ST_RAND;
        // convert model flags to EF flags (MF_ROCKET becomes EF_ROCKET, etc)
        loadmodel->PointSuperContents = NULL;
        loadmodel->synctype = ST_RAND;
        // convert model flags to EF flags (MF_ROCKET becomes EF_ROCKET, etc)
@@ -1453,23 +1607,24 @@ void Mod_IDP3_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
        loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
        loadmodel->num_texturesperskin = loadmodel->num_surfaces;
        loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
        loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
        loadmodel->num_texturesperskin = loadmodel->num_surfaces;
-       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]) + meshtriangles * sizeof(int[3]) + (meshvertices <= 65536 ? meshtriangles * sizeof(unsigned short[3]) : 0) + meshvertices * sizeof(float[2]) + meshvertices * loadmodel->numframes * sizeof(md3vertex_t));
+       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]) + (r_enableshadowvolumes.integer ? meshtriangles * sizeof(int[3]) : 0) + (meshvertices <= 65536 ? meshtriangles * sizeof(unsigned short[3]) : 0) + meshvertices * sizeof(float[2]) + meshvertices * loadmodel->numframes * sizeof(md3vertex_t));
        loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
        loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
-       loadmodel->surfacelist = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
+       loadmodel->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
        loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
        loadmodel->surfmesh.num_vertices = meshvertices;
        loadmodel->surfmesh.num_triangles = meshtriangles;
        loadmodel->surfmesh.num_morphframes = loadmodel->numframes; // TODO: remove?
        loadmodel->num_poses = loadmodel->surfmesh.num_morphframes;
        loadmodel->surfmesh.data_element3i = (int *)data;data += meshtriangles * sizeof(int[3]);
        loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
        loadmodel->surfmesh.num_vertices = meshvertices;
        loadmodel->surfmesh.num_triangles = meshtriangles;
        loadmodel->surfmesh.num_morphframes = loadmodel->numframes; // TODO: remove?
        loadmodel->num_poses = loadmodel->surfmesh.num_morphframes;
        loadmodel->surfmesh.data_element3i = (int *)data;data += meshtriangles * sizeof(int[3]);
-       loadmodel->surfmesh.data_neighbor3i = (int *)data;data += meshtriangles * sizeof(int[3]);
+       if (r_enableshadowvolumes.integer)
+       {
+               loadmodel->surfmesh.data_neighbor3i = (int *)data;data += meshtriangles * sizeof(int[3]);
+       }
        loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += meshvertices * sizeof(float[2]);
        loadmodel->surfmesh.data_morphmd3vertex = (md3vertex_t *)data;data += meshvertices * loadmodel->numframes * sizeof(md3vertex_t);
        if (meshvertices <= 65536)
        {
                loadmodel->surfmesh.data_element3s = (unsigned short *)data;data += meshtriangles * sizeof(unsigned short[3]);
        loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += meshvertices * sizeof(float[2]);
        loadmodel->surfmesh.data_morphmd3vertex = (md3vertex_t *)data;data += meshvertices * loadmodel->numframes * sizeof(md3vertex_t);
        if (meshvertices <= 65536)
        {
                loadmodel->surfmesh.data_element3s = (unsigned short *)data;data += meshtriangles * sizeof(unsigned short[3]);
-               for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
-                       loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
        }
 
        meshvertices = 0;
        }
 
        meshvertices = 0;
@@ -1478,7 +1633,7 @@ void Mod_IDP3_Load(dp_model_t *mod, void *buffer, void *bufferend)
        {
                if (memcmp(pinmesh->identifier, "IDP3", 4))
                        Host_Error("Mod_IDP3_Load: invalid mesh identifier (not IDP3)");
        {
                if (memcmp(pinmesh->identifier, "IDP3", 4))
                        Host_Error("Mod_IDP3_Load: invalid mesh identifier (not IDP3)");
-               loadmodel->surfacelist[i] = i;
+               loadmodel->sortedmodelsurfaces[i] = i;
                surface = loadmodel->data_surfaces + i;
                surface->texture = loadmodel->data_textures + i;
                surface->num_firsttriangle = meshtriangles;
                surface = loadmodel->data_surfaces + i;
                surface->texture = loadmodel->data_textures + i;
                surface->num_firsttriangle = meshtriangles;
@@ -1513,13 +1668,37 @@ void Mod_IDP3_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
                Mod_ValidateElements(loadmodel->surfmesh.data_element3i + surface->num_firsttriangle * 3, surface->num_triangles, surface->num_firstvertex, surface->num_vertices, __FILE__, __LINE__);
        }
 
                Mod_ValidateElements(loadmodel->surfmesh.data_element3i + surface->num_firsttriangle * 3, surface->num_triangles, surface->num_firstvertex, surface->num_vertices, __FILE__, __LINE__);
        }
-       Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
+       if (loadmodel->surfmesh.data_element3s)
+               for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
+                       loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
+       loadmodel->surfmesh.isanimated = loadmodel->numframes > 1 || (loadmodel->animscenes && loadmodel->animscenes[0].framecount > 1);
+       loadmodel->AnimateVertices = Mod_MD3_AnimateVertices; // needed during loading, may be cleared by code later in this function
+       if (loadmodel->surfmesh.data_neighbor3i)
+               Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
        Mod_Alias_MorphMesh_CompileFrames();
        Mod_Alias_CalculateBoundingBox();
        Mod_FreeSkinFiles(skinfiles);
        Mod_Alias_MorphMesh_CompileFrames();
        Mod_Alias_CalculateBoundingBox();
        Mod_FreeSkinFiles(skinfiles);
+       Mod_MakeSortedSurfaces(loadmodel);
+       loadmodel->AnimateVertices = loadmodel->surfmesh.isanimated ? Mod_MD3_AnimateVertices : NULL;
+
+       if (!loadmodel->surfmesh.isanimated)
+       {
+               Mod_MakeCollisionBIH(loadmodel, true, &loadmodel->collision_bih);
+               loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
+               loadmodel->TraceBrush = Mod_CollisionBIH_TraceBrush;
+               loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
+               loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
+               loadmodel->PointSuperContents = Mod_CollisionBIH_PointSuperContents_Mesh;
+       }
 
 
-       loadmodel->surfmesh.isanimated = loadmodel->numframes > 1
-            || (loadmodel->animscenes && loadmodel->animscenes[0].framecount > 1);
+       // because shaders can do somewhat unexpected things, check for unusual features now
+       for (i = 0;i < loadmodel->num_textures;i++)
+       {
+               if (loadmodel->data_textures[i].basematerialflags & (MATERIALFLAG_SKY))
+                       mod->DrawSky = R_Q1BSP_DrawSky;
+               if (loadmodel->data_textures[i].basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
+                       mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
+       }
 }
 
 void Mod_ZYMOTICMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
 }
 
 void Mod_ZYMOTICMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
@@ -1527,7 +1706,7 @@ void Mod_ZYMOTICMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        zymtype1header_t *pinmodel, *pheader;
        unsigned char *pbase;
        int i, j, k, numposes, meshvertices, meshtriangles, *bonecount, *vertbonecounts, count, *renderlist, *renderlistend, *outelements;
        zymtype1header_t *pinmodel, *pheader;
        unsigned char *pbase;
        int i, j, k, numposes, meshvertices, meshtriangles, *bonecount, *vertbonecounts, count, *renderlist, *renderlistend, *outelements;
-       float modelradius, corner[2], *poses, *intexcoord2f, *outtexcoord2f, *bonepose;
+       float modelradius, corner[2], *poses, *intexcoord2f, *outtexcoord2f, *bonepose, f, biggestorigin, tempvec[3], modelscale;
        zymvertex_t *verts, *vertdata;
        zymscene_t *scene;
        zymbone_t *bone;
        zymvertex_t *verts, *vertdata;
        zymscene_t *scene;
        zymbone_t *bone;
@@ -1594,16 +1773,19 @@ void Mod_ZYMOTICMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
                return;
        }
 
                return;
        }
 
-       loadmodel->AnimateVertices = Mod_Skeletal_AnimateVertices;
        loadmodel->DrawSky = NULL;
        loadmodel->DrawAddWaterPlanes = NULL;
        loadmodel->Draw = R_Q1BSP_Draw;
        loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
        loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
        loadmodel->DrawSky = NULL;
        loadmodel->DrawAddWaterPlanes = NULL;
        loadmodel->Draw = R_Q1BSP_Draw;
        loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
        loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
+       loadmodel->DrawPrepass = R_Q1BSP_DrawPrepass;
+       loadmodel->CompileShadowMap = R_Q1BSP_CompileShadowMap;
+       loadmodel->DrawShadowMap = R_Q1BSP_DrawShadowMap;
        loadmodel->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
        loadmodel->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
        loadmodel->DrawLight = R_Q1BSP_DrawLight;
        loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
        loadmodel->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
        loadmodel->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
        loadmodel->DrawLight = R_Q1BSP_DrawLight;
        loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
+       loadmodel->TraceLine = Mod_MDLMD2MD3_TraceLine;
        loadmodel->PointSuperContents = NULL;
 
        loadmodel->numframes = pheader->numscenes;
        loadmodel->PointSuperContents = NULL;
 
        loadmodel->numframes = pheader->numscenes;
@@ -1667,7 +1849,7 @@ void Mod_ZYMOTICMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
        //zymlump_t lump_bones; // zymbone_t bone[numbones];
        loadmodel->num_bones = pheader->numbones;
 
        //zymlump_t lump_bones; // zymbone_t bone[numbones];
        loadmodel->num_bones = pheader->numbones;
-       loadmodel->data_bones = (aliasbone_t *)Mem_Alloc(loadmodel->mempool, pheader->numbones * sizeof(aliasbone_t));
+       loadmodel->data_bones = (aliasbone_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_bones * sizeof(aliasbone_t));
        bone = (zymbone_t *) (pheader->lump_bones.start + pbase);
        for (i = 0;i < pheader->numbones;i++)
        {
        bone = (zymbone_t *) (pheader->lump_bones.start + pbase);
        for (i = 0;i < pheader->numbones;i++)
        {
@@ -1688,42 +1870,83 @@ void Mod_ZYMOTICMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
                        Host_Error("%s bonecount[%i] != 1 (vertex weight support is impossible in this format)", loadmodel->name, i);
        }
 
                        Host_Error("%s bonecount[%i] != 1 (vertex weight support is impossible in this format)", loadmodel->name, i);
        }
 
-       loadmodel->num_poses = pheader->lump_poses.length / sizeof(float[3][4]);
+       loadmodel->num_poses = pheader->lump_poses.length / sizeof(float[3][4]) / loadmodel->num_bones;
 
        meshvertices = pheader->numverts;
        meshtriangles = pheader->numtris;
 
 
        meshvertices = pheader->numverts;
        meshtriangles = pheader->numtris;
 
+       loadmodel->surfmesh.isanimated = loadmodel->num_bones > 1 || loadmodel->numframes > 1 || (loadmodel->animscenes && loadmodel->animscenes[0].framecount > 1);
+       loadmodel->AnimateVertices = loadmodel->surfmesh.isanimated ? Mod_Skeletal_AnimateVertices : NULL;
        loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
        loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
        loadmodel->num_texturesperskin = loadmodel->num_surfaces;
        loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
        loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
        loadmodel->num_texturesperskin = loadmodel->num_surfaces;
-       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]) + meshtriangles * sizeof(int[3]) + (meshvertices <= 65536 ? meshtriangles * sizeof(unsigned short[3]) : 0) + meshvertices * sizeof(float[14]) + meshvertices * sizeof(int[4]) + meshvertices * sizeof(float[4]) + loadmodel->num_poses * sizeof(float[12]) + loadmodel->num_bones * sizeof(float[12]));
+       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]) + (r_enableshadowvolumes.integer ? meshtriangles * sizeof(int[3]) : 0) + (meshvertices <= 65536 ? meshtriangles * sizeof(unsigned short[3]) : 0) + meshvertices * sizeof(float[14]) + meshvertices * sizeof(unsigned short) + loadmodel->num_poses * loadmodel->num_bones * sizeof(short[6]) + loadmodel->num_bones * sizeof(float[12]));
        loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
        loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
-       loadmodel->surfacelist = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
+       loadmodel->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
        loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
        loadmodel->surfmesh.num_vertices = meshvertices;
        loadmodel->surfmesh.num_triangles = meshtriangles;
        loadmodel->surfmesh.data_element3i = (int *)data;data += meshtriangles * sizeof(int[3]);
        loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
        loadmodel->surfmesh.num_vertices = meshvertices;
        loadmodel->surfmesh.num_triangles = meshtriangles;
        loadmodel->surfmesh.data_element3i = (int *)data;data += meshtriangles * sizeof(int[3]);
-       loadmodel->surfmesh.data_neighbor3i = (int *)data;data += meshtriangles * sizeof(int[3]);
+       if (r_enableshadowvolumes.integer)
+       {
+               loadmodel->surfmesh.data_neighbor3i = (int *)data;data += meshtriangles * sizeof(int[3]);
+       }
        loadmodel->surfmesh.data_vertex3f = (float *)data;data += meshvertices * sizeof(float[3]);
        loadmodel->surfmesh.data_svector3f = (float *)data;data += meshvertices * sizeof(float[3]);
        loadmodel->surfmesh.data_tvector3f = (float *)data;data += meshvertices * sizeof(float[3]);
        loadmodel->surfmesh.data_normal3f = (float *)data;data += meshvertices * sizeof(float[3]);
        loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += meshvertices * sizeof(float[2]);
        loadmodel->surfmesh.data_vertex3f = (float *)data;data += meshvertices * sizeof(float[3]);
        loadmodel->surfmesh.data_svector3f = (float *)data;data += meshvertices * sizeof(float[3]);
        loadmodel->surfmesh.data_tvector3f = (float *)data;data += meshvertices * sizeof(float[3]);
        loadmodel->surfmesh.data_normal3f = (float *)data;data += meshvertices * sizeof(float[3]);
        loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += meshvertices * sizeof(float[2]);
-       loadmodel->surfmesh.data_vertexweightindex4i = (int *)data;data += meshvertices * sizeof(int[4]);
-       loadmodel->surfmesh.data_vertexweightinfluence4f = (float *)data;data += meshvertices * sizeof(float[4]);
-       loadmodel->data_poses = (float *)data;data += loadmodel->num_poses * sizeof(float[12]);
        loadmodel->data_baseboneposeinverse = (float *)data;data += loadmodel->num_bones * sizeof(float[12]);
        loadmodel->data_baseboneposeinverse = (float *)data;data += loadmodel->num_bones * sizeof(float[12]);
+       loadmodel->surfmesh.num_blends = 0;
+       loadmodel->surfmesh.blends = (unsigned short *)data;data += meshvertices * sizeof(unsigned short);
        if (loadmodel->surfmesh.num_vertices <= 65536)
        {
                loadmodel->surfmesh.data_element3s = (unsigned short *)data;data += loadmodel->surfmesh.num_triangles * sizeof(unsigned short[3]);
        if (loadmodel->surfmesh.num_vertices <= 65536)
        {
                loadmodel->surfmesh.data_element3s = (unsigned short *)data;data += loadmodel->surfmesh.num_triangles * sizeof(unsigned short[3]);
-               for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
-                       loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
        }
        }
+       loadmodel->data_poses6s = (short *)data;data += loadmodel->num_poses * loadmodel->num_bones * sizeof(short[6]);
+       loadmodel->surfmesh.data_blendweights = NULL;
 
        //zymlump_t lump_poses; // float pose[numposes][numbones][3][4]; // animation data
        poses = (float *) (pheader->lump_poses.start + pbase);
 
        //zymlump_t lump_poses; // float pose[numposes][numbones][3][4]; // animation data
        poses = (float *) (pheader->lump_poses.start + pbase);
-       for (i = 0;i < pheader->lump_poses.length / 4;i++)
-               loadmodel->data_poses[i] = BigFloat(poses[i]);
+       // figure out scale of model from root bone, for compatibility with old zmodel versions
+       tempvec[0] = BigFloat(poses[0]);
+       tempvec[1] = BigFloat(poses[1]);
+       tempvec[2] = BigFloat(poses[2]);
+       modelscale = VectorLength(tempvec);
+       biggestorigin = 0;
+       for (i = 0;i < loadmodel->num_bones * numposes * 12;i++)
+       {
+               f = fabs(BigFloat(poses[i]));
+               biggestorigin = max(biggestorigin, f);
+       }
+       loadmodel->num_posescale = biggestorigin / 32767.0f;
+       loadmodel->num_poseinvscale = 1.0f / loadmodel->num_posescale;
+       for (i = 0;i < numposes;i++)
+       {
+               const float *frameposes = (float *) (pheader->lump_poses.start + pbase) + 12*i*loadmodel->num_bones;
+               for (j = 0;j < loadmodel->num_bones;j++)
+               {
+                       float pose[12];
+                       matrix4x4_t posematrix;
+                       for (k = 0;k < 12;k++)
+                               pose[k] = BigFloat(frameposes[j*12+k]);
+                       //if (j < loadmodel->num_bones)
+                       //      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));
+                       // scale child bones to match the root scale
+                       if (loadmodel->data_bones[j].parent >= 0)
+                       {
+                               pose[3] *= modelscale;
+                               pose[7] *= modelscale;
+                               pose[11] *= modelscale;
+                       }
+                       // normalize rotation matrix
+                       VectorNormalize(pose + 0);
+                       VectorNormalize(pose + 4);
+                       VectorNormalize(pose + 8);
+                       Matrix4x4_FromArray12FloatD3D(&posematrix, pose);
+                       Matrix4x4_ToBonePose6s(&posematrix, loadmodel->num_poseinvscale, loadmodel->data_poses6s + 6*(i*loadmodel->num_bones+j));
+               }
+       }
 
        //zymlump_t lump_verts; // zymvertex_t vert[numvertices]; // see vertex struct
        verts = (zymvertex_t *)Mem_Alloc(loadmodel->mempool, pheader->lump_verts.length);
 
        //zymlump_t lump_verts; // zymvertex_t vert[numvertices]; // see vertex struct
        verts = (zymvertex_t *)Mem_Alloc(loadmodel->mempool, pheader->lump_verts.length);
@@ -1734,7 +1957,9 @@ void Mod_ZYMOTICMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        bonepose = (float *)Z_Malloc(loadmodel->num_bones * sizeof(float[12]));
        for (i = 0;i < loadmodel->num_bones;i++)
        {
        bonepose = (float *)Z_Malloc(loadmodel->num_bones * sizeof(float[12]));
        for (i = 0;i < loadmodel->num_bones;i++)
        {
-               const float *m = loadmodel->data_poses + i * 12;
+               float m[12];
+               for (k = 0;k < 12;k++)
+                       m[k] = BigFloat(poses[i*12+k]);
                if (loadmodel->data_bones[i].parent >= 0)
                        R_ConcatTransforms(bonepose + 12 * loadmodel->data_bones[i].parent, m, bonepose + 12 * i);
                else
                if (loadmodel->data_bones[i].parent >= 0)
                        R_ConcatTransforms(bonepose + 12 * loadmodel->data_bones[i].parent, m, bonepose + 12 * i);
                else
@@ -1757,8 +1982,7 @@ void Mod_ZYMOTICMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
                loadmodel->surfmesh.data_vertex3f[j*3+1] = relativeorigin[0] * m[4] + relativeorigin[1] * m[5] + relativeorigin[2] * m[ 6] + m[ 7];
                loadmodel->surfmesh.data_vertex3f[j*3+2] = relativeorigin[0] * m[8] + relativeorigin[1] * m[9] + relativeorigin[2] * m[10] + m[11];
                // store the weight as the primary weight on this vertex
                loadmodel->surfmesh.data_vertex3f[j*3+1] = relativeorigin[0] * m[4] + relativeorigin[1] * m[5] + relativeorigin[2] * m[ 6] + m[ 7];
                loadmodel->surfmesh.data_vertex3f[j*3+2] = relativeorigin[0] * m[8] + relativeorigin[1] * m[9] + relativeorigin[2] * m[10] + m[11];
                // store the weight as the primary weight on this vertex
-               loadmodel->surfmesh.data_vertexweightindex4i[j*4+0] = boneindex;
-               loadmodel->surfmesh.data_vertexweightinfluence4f[j*4+0] = 1;
+               loadmodel->surfmesh.blends[j] = boneindex;
        }
        Z_Free(bonepose);
        // normals and tangents are calculated after elements are loaded
        }
        Z_Free(bonepose);
        // normals and tangents are calculated after elements are loaded
@@ -1795,7 +2019,7 @@ void Mod_ZYMOTICMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
                if (renderlist + count * 3 > renderlistend || (i == pheader->numshaders - 1 && renderlist + count * 3 != renderlistend))
                        Host_Error("%s corrupt renderlist (wrong size)", loadmodel->name);
 
                if (renderlist + count * 3 > renderlistend || (i == pheader->numshaders - 1 && renderlist + count * 3 != renderlistend))
                        Host_Error("%s corrupt renderlist (wrong size)", loadmodel->name);
 
-               loadmodel->surfacelist[i] = i;
+               loadmodel->sortedmodelsurfaces[i] = i;
                surface = loadmodel->data_surfaces + i;
                surface->texture = loadmodel->data_textures + i;
                surface->num_firsttriangle = meshtriangles;
                surface = loadmodel->data_surfaces + i;
                surface->texture = loadmodel->data_textures + i;
                surface->num_firsttriangle = meshtriangles;
@@ -1831,21 +2055,43 @@ void Mod_ZYMOTICMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        Mod_FreeSkinFiles(skinfiles);
        Mem_Free(vertbonecounts);
        Mem_Free(verts);
        Mod_FreeSkinFiles(skinfiles);
        Mem_Free(vertbonecounts);
        Mem_Free(verts);
+       Mod_MakeSortedSurfaces(loadmodel);
 
        // compute all the mesh information that was not loaded from the file
 
        // compute all the mesh information that was not loaded from the file
+       if (loadmodel->surfmesh.data_element3s)
+               for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
+                       loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
        Mod_ValidateElements(loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles, 0, loadmodel->surfmesh.num_vertices, __FILE__, __LINE__);
        Mod_BuildBaseBonePoses();
        Mod_ValidateElements(loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles, 0, loadmodel->surfmesh.num_vertices, __FILE__, __LINE__);
        Mod_BuildBaseBonePoses();
-       Mod_BuildNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_normal3f, true);
-       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, true);
-       Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
+       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);
+       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);
+       if (loadmodel->surfmesh.data_neighbor3i)
+               Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
+
+       if (!loadmodel->surfmesh.isanimated)
+       {
+               Mod_MakeCollisionBIH(loadmodel, true, &loadmodel->collision_bih);
+               loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
+               loadmodel->TraceBrush = Mod_CollisionBIH_TraceBrush;
+               loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
+               loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
+               loadmodel->PointSuperContents = Mod_CollisionBIH_PointSuperContents_Mesh;
+       }
 
 
-       loadmodel->surfmesh.isanimated = loadmodel->numframes > 1 || loadmodel->animscenes[0].framecount > 1;
+       // because shaders can do somewhat unexpected things, check for unusual features now
+       for (i = 0;i < loadmodel->num_textures;i++)
+       {
+               if (loadmodel->data_textures[i].basematerialflags & (MATERIALFLAG_SKY))
+                       mod->DrawSky = R_Q1BSP_DrawSky;
+               if (loadmodel->data_textures[i].basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
+                       mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
+       }
 }
 
 void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
 {
        dpmheader_t *pheader;
 }
 
 void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
 {
        dpmheader_t *pheader;
-       dpmframe_t *frame;
+       dpmframe_t *frames;
        dpmbone_t *bone;
        dpmmesh_t *dpmmesh;
        unsigned char *pbase;
        dpmbone_t *bone;
        dpmmesh_t *dpmmesh;
        unsigned char *pbase;
@@ -1853,6 +2099,9 @@ void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        skinfile_t *skinfiles;
        unsigned char *data;
        float *bonepose;
        skinfile_t *skinfiles;
        unsigned char *data;
        float *bonepose;
+       float biggestorigin, tempvec[3], modelscale;
+       float f;
+       float *poses;
 
        pheader = (dpmheader_t *)buffer;
        pbase = (unsigned char *)buffer;
 
        pheader = (dpmheader_t *)buffer;
        pbase = (unsigned char *)buffer;
@@ -1895,16 +2144,19 @@ void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
                return;
        }
 
                return;
        }
 
-       loadmodel->AnimateVertices = Mod_Skeletal_AnimateVertices;
        loadmodel->DrawSky = NULL;
        loadmodel->DrawAddWaterPlanes = NULL;
        loadmodel->Draw = R_Q1BSP_Draw;
        loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
        loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
        loadmodel->DrawSky = NULL;
        loadmodel->DrawAddWaterPlanes = NULL;
        loadmodel->Draw = R_Q1BSP_Draw;
        loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
        loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
+       loadmodel->DrawPrepass = R_Q1BSP_DrawPrepass;
+       loadmodel->CompileShadowMap = R_Q1BSP_CompileShadowMap;
+       loadmodel->DrawShadowMap = R_Q1BSP_DrawShadowMap;
        loadmodel->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
        loadmodel->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
        loadmodel->DrawLight = R_Q1BSP_DrawLight;
        loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
        loadmodel->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
        loadmodel->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
        loadmodel->DrawLight = R_Q1BSP_DrawLight;
        loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
+       loadmodel->TraceLine = Mod_MDLMD2MD3_TraceLine;
        loadmodel->PointSuperContents = NULL;
 
        // model bbox
        loadmodel->PointSuperContents = NULL;
 
        // model bbox
@@ -1940,37 +2192,41 @@ void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
        loadmodel->numframes = pheader->num_frames;
        loadmodel->num_bones = pheader->num_bones;
 
        loadmodel->numframes = pheader->num_frames;
        loadmodel->num_bones = pheader->num_bones;
-       loadmodel->num_poses = loadmodel->num_bones * loadmodel->numframes;
+       loadmodel->num_poses = loadmodel->numframes;
        loadmodel->nummodelsurfaces = loadmodel->num_surfaces = pheader->num_meshs;
        loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
        loadmodel->num_texturesperskin = loadmodel->num_surfaces;
        loadmodel->nummodelsurfaces = loadmodel->num_surfaces = pheader->num_meshs;
        loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
        loadmodel->num_texturesperskin = loadmodel->num_surfaces;
+       loadmodel->surfmesh.isanimated = loadmodel->num_bones > 1 || loadmodel->numframes > 1 || (loadmodel->animscenes && loadmodel->animscenes[0].framecount > 1);
+       loadmodel->AnimateVertices = loadmodel->surfmesh.isanimated ? Mod_Skeletal_AnimateVertices : NULL;
        // do most allocations as one merged chunk
        // do most allocations as one merged chunk
-       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) + meshtriangles * sizeof(int[3]) + meshvertices * (sizeof(float[14]) + sizeof(int[4]) + sizeof(float[4])) + loadmodel->num_poses * sizeof(float[12]) + loadmodel->num_bones * sizeof(float[12]) + loadmodel->numskins * sizeof(animscene_t) + loadmodel->num_bones * sizeof(aliasbone_t) + loadmodel->numframes * sizeof(animscene_t));
+       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) + (r_enableshadowvolumes.integer ? meshtriangles * sizeof(int[3]) : 0) + meshvertices * (sizeof(float[14]) + sizeof(unsigned short)) + loadmodel->num_poses * loadmodel->num_bones * sizeof(short[6]) + loadmodel->num_bones * sizeof(float[12]) + loadmodel->numskins * sizeof(animscene_t) + loadmodel->num_bones * sizeof(aliasbone_t) + loadmodel->numframes * sizeof(animscene_t));
        loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
        loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
-       loadmodel->surfacelist = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
+       loadmodel->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
        loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
        loadmodel->surfmesh.num_vertices = meshvertices;
        loadmodel->surfmesh.num_triangles = meshtriangles;
        loadmodel->surfmesh.data_element3i = (int *)data;data += meshtriangles * sizeof(int[3]);
        loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
        loadmodel->surfmesh.num_vertices = meshvertices;
        loadmodel->surfmesh.num_triangles = meshtriangles;
        loadmodel->surfmesh.data_element3i = (int *)data;data += meshtriangles * sizeof(int[3]);
-       loadmodel->surfmesh.data_neighbor3i = (int *)data;data += meshtriangles * sizeof(int[3]);
+       if (r_enableshadowvolumes.integer)
+       {
+               loadmodel->surfmesh.data_neighbor3i = (int *)data;data += meshtriangles * sizeof(int[3]);
+       }
        loadmodel->surfmesh.data_vertex3f = (float *)data;data += meshvertices * sizeof(float[3]);
        loadmodel->surfmesh.data_svector3f = (float *)data;data += meshvertices * sizeof(float[3]);
        loadmodel->surfmesh.data_tvector3f = (float *)data;data += meshvertices * sizeof(float[3]);
        loadmodel->surfmesh.data_normal3f = (float *)data;data += meshvertices * sizeof(float[3]);
        loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += meshvertices * sizeof(float[2]);
        loadmodel->surfmesh.data_vertex3f = (float *)data;data += meshvertices * sizeof(float[3]);
        loadmodel->surfmesh.data_svector3f = (float *)data;data += meshvertices * sizeof(float[3]);
        loadmodel->surfmesh.data_tvector3f = (float *)data;data += meshvertices * sizeof(float[3]);
        loadmodel->surfmesh.data_normal3f = (float *)data;data += meshvertices * sizeof(float[3]);
        loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += meshvertices * sizeof(float[2]);
-       loadmodel->surfmesh.data_vertexweightindex4i = (int *)data;data += meshvertices * sizeof(int[4]);
-       loadmodel->surfmesh.data_vertexweightinfluence4f = (float *)data;data += meshvertices * sizeof(float[4]);
-       loadmodel->data_poses = (float *)data;data += loadmodel->num_poses * sizeof(float[12]);
        loadmodel->data_baseboneposeinverse = (float *)data;data += loadmodel->num_bones * sizeof(float[12]);
        loadmodel->skinscenes = (animscene_t *)data;data += loadmodel->numskins * sizeof(animscene_t);
        loadmodel->data_bones = (aliasbone_t *)data;data += loadmodel->num_bones * sizeof(aliasbone_t);
        loadmodel->animscenes = (animscene_t *)data;data += loadmodel->numframes * sizeof(animscene_t);
        loadmodel->data_baseboneposeinverse = (float *)data;data += loadmodel->num_bones * sizeof(float[12]);
        loadmodel->skinscenes = (animscene_t *)data;data += loadmodel->numskins * sizeof(animscene_t);
        loadmodel->data_bones = (aliasbone_t *)data;data += loadmodel->num_bones * sizeof(aliasbone_t);
        loadmodel->animscenes = (animscene_t *)data;data += loadmodel->numframes * sizeof(animscene_t);
+       loadmodel->surfmesh.num_blends = 0;
+       loadmodel->surfmesh.blends = (unsigned short *)data;data += meshvertices * sizeof(unsigned short);
        if (meshvertices <= 65536)
        {
                loadmodel->surfmesh.data_element3s = (unsigned short *)data;data += meshtriangles * sizeof(unsigned short[3]);
        if (meshvertices <= 65536)
        {
                loadmodel->surfmesh.data_element3s = (unsigned short *)data;data += meshtriangles * sizeof(unsigned short[3]);
-               for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
-                       loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
        }
        }
+       loadmodel->data_poses6s = (short *)data;data += loadmodel->num_poses * loadmodel->num_bones * sizeof(short[6]);
+       loadmodel->surfmesh.data_blendweights = (blendweights_t *)Mem_Alloc(loadmodel->mempool, meshvertices * sizeof(blendweights_t));
 
        for (i = 0;i < loadmodel->numskins;i++)
        {
 
        for (i = 0;i < loadmodel->numskins;i++)
        {
@@ -1992,21 +2248,55 @@ void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        }
 
        // load the frames
        }
 
        // load the frames
-       frame = (dpmframe_t *) (pbase + pheader->ofs_frames);
+       frames = (dpmframe_t *) (pbase + pheader->ofs_frames);
+       // figure out scale of model from root bone, for compatibility with old dpmodel versions
+       poses = (float *) (pbase + BigLong(frames[0].ofs_bonepositions));
+       tempvec[0] = BigFloat(poses[0]);
+       tempvec[1] = BigFloat(poses[1]);
+       tempvec[2] = BigFloat(poses[2]);
+       modelscale = VectorLength(tempvec);
+       biggestorigin = 0;
        for (i = 0;i < loadmodel->numframes;i++)
        {
        for (i = 0;i < loadmodel->numframes;i++)
        {
-               const float *poses;
-               memcpy(loadmodel->animscenes[i].name, frame->name, sizeof(frame->name));
+               memcpy(loadmodel->animscenes[i].name, frames[i].name, sizeof(frames[i].name));
                loadmodel->animscenes[i].firstframe = i;
                loadmodel->animscenes[i].framecount = 1;
                loadmodel->animscenes[i].loop = true;
                loadmodel->animscenes[i].framerate = 10;
                // load the bone poses for this frame
                loadmodel->animscenes[i].firstframe = i;
                loadmodel->animscenes[i].framecount = 1;
                loadmodel->animscenes[i].loop = true;
                loadmodel->animscenes[i].framerate = 10;
                // load the bone poses for this frame
-               poses = (float *) (pbase + BigLong(frame->ofs_bonepositions));
+               poses = (float *) (pbase + BigLong(frames[i].ofs_bonepositions));
                for (j = 0;j < loadmodel->num_bones*12;j++)
                for (j = 0;j < loadmodel->num_bones*12;j++)
-                       loadmodel->data_poses[i * loadmodel->num_bones*12 + j] = BigFloat(poses[j]);
+               {
+                       f = fabs(BigFloat(poses[j]));
+                       biggestorigin = max(biggestorigin, f);
+               }
                // stuff not processed here: mins, maxs, yawradius, allradius
                // stuff not processed here: mins, maxs, yawradius, allradius
-               frame++;
+       }
+       loadmodel->num_posescale = biggestorigin / 32767.0f;
+       loadmodel->num_poseinvscale = 1.0f / loadmodel->num_posescale;
+       for (i = 0;i < loadmodel->numframes;i++)
+       {
+               const float *frameposes = (float *) (pbase + BigLong(frames[i].ofs_bonepositions));
+               for (j = 0;j < loadmodel->num_bones;j++)
+               {
+                       float pose[12];
+                       matrix4x4_t posematrix;
+                       for (k = 0;k < 12;k++)
+                               pose[k] = BigFloat(frameposes[j*12+k]);
+                       // scale child bones to match the root scale
+                       if (loadmodel->data_bones[j].parent >= 0)
+                       {
+                               pose[3] *= modelscale;
+                               pose[7] *= modelscale;
+                               pose[11] *= modelscale;
+                       }
+                       // normalize rotation matrix
+                       VectorNormalize(pose + 0);
+                       VectorNormalize(pose + 4);
+                       VectorNormalize(pose + 8);
+                       Matrix4x4_FromArray12FloatD3D(&posematrix, pose);
+                       Matrix4x4_ToBonePose6s(&posematrix, loadmodel->num_poseinvscale, loadmodel->data_poses6s + 6*(i*loadmodel->num_bones+j));
+               }
        }
 
        // load the meshes now
        }
 
        // load the meshes now
@@ -2016,10 +2306,13 @@ void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        // reconstruct frame 0 matrices to allow reconstruction of the base mesh
        // (converting from weight-blending skeletal animation to
        //  deformation-based skeletal animation)
        // reconstruct frame 0 matrices to allow reconstruction of the base mesh
        // (converting from weight-blending skeletal animation to
        //  deformation-based skeletal animation)
+       poses = (float *) (pbase + BigLong(frames[0].ofs_bonepositions));
        bonepose = (float *)Z_Malloc(loadmodel->num_bones * sizeof(float[12]));
        for (i = 0;i < loadmodel->num_bones;i++)
        {
        bonepose = (float *)Z_Malloc(loadmodel->num_bones * sizeof(float[12]));
        for (i = 0;i < loadmodel->num_bones;i++)
        {
-               const float *m = loadmodel->data_poses + i * 12;
+               float m[12];
+               for (k = 0;k < 12;k++)
+                       m[k] = BigFloat(poses[i*12+k]);
                if (loadmodel->data_bones[i].parent >= 0)
                        R_ConcatTransforms(bonepose + 12 * loadmodel->data_bones[i].parent, m, bonepose + 12 * i);
                else
                if (loadmodel->data_bones[i].parent >= 0)
                        R_ConcatTransforms(bonepose + 12 * loadmodel->data_bones[i].parent, m, bonepose + 12 * i);
                else
@@ -2033,7 +2326,7 @@ void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
                const float *intexcoord;
                msurface_t *surface;
 
                const float *intexcoord;
                msurface_t *surface;
 
-               loadmodel->surfacelist[i] = i;
+               loadmodel->sortedmodelsurfaces[i] = i;
                surface = loadmodel->data_surfaces + i;
                surface->texture = loadmodel->data_textures + i;
                surface->num_firsttriangle = meshtriangles;
                surface = loadmodel->data_surfaces + i;
                surface->texture = loadmodel->data_textures + i;
                surface->num_firsttriangle = meshtriangles;
@@ -2062,7 +2355,8 @@ void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
                data = (unsigned char *) (pbase + BigLong(dpmmesh->ofs_verts));
                for (j = surface->num_firstvertex;j < surface->num_firstvertex + surface->num_vertices;j++)
                {
                data = (unsigned char *) (pbase + BigLong(dpmmesh->ofs_verts));
                for (j = surface->num_firstvertex;j < surface->num_firstvertex + surface->num_vertices;j++)
                {
-                       float sum;
+                       int weightindex[4] = { 0, 0, 0, 0 };
+                       float weightinfluence[4] = { 0, 0, 0, 0 };
                        int l;
                        int numweights = BigLong(((dpmvertex_t *)data)->numbones);
                        data += sizeof(dpmvertex_t);
                        int l;
                        int numweights = BigLong(((dpmvertex_t *)data)->numbones);
                        data += sizeof(dpmvertex_t);
@@ -2089,8 +2383,8 @@ void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
                                if (!k)
                                {
                                        // store the first (and often only) weight
                                if (!k)
                                {
                                        // store the first (and often only) weight
-                                       loadmodel->surfmesh.data_vertexweightinfluence4f[j*4+0] = influence;
-                                       loadmodel->surfmesh.data_vertexweightindex4i[j*4+0] = boneindex;
+                                       weightinfluence[0] = influence;
+                                       weightindex[0] = boneindex;
                                }
                                else
                                {
                                }
                                else
                                {
@@ -2098,33 +2392,25 @@ void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
                                        // (which only accepts up to 4 bones per vertex)
                                        for (l = 0;l < 4;l++)
                                        {
                                        // (which only accepts up to 4 bones per vertex)
                                        for (l = 0;l < 4;l++)
                                        {
-                                               if (loadmodel->surfmesh.data_vertexweightinfluence4f[j*4+l] < influence)
+                                               if (weightinfluence[l] < influence)
                                                {
                                                        // move weaker influence weights out of the way first
                                                        int l2;
                                                        for (l2 = 3;l2 > l;l2--)
                                                        {
                                                {
                                                        // move weaker influence weights out of the way first
                                                        int l2;
                                                        for (l2 = 3;l2 > l;l2--)
                                                        {
-                                                               loadmodel->surfmesh.data_vertexweightinfluence4f[j*4+l2] = loadmodel->surfmesh.data_vertexweightinfluence4f[j*4+l2-1];
-                                                               loadmodel->surfmesh.data_vertexweightindex4i[j*4+l2] = loadmodel->surfmesh.data_vertexweightindex4i[j*4+l2-1];
+                                                               weightinfluence[l2] = weightinfluence[l2-1];
+                                                               weightindex[l2] = weightindex[l2-1];
                                                        }
                                                        // store the new weight
                                                        }
                                                        // store the new weight
-                                                       loadmodel->surfmesh.data_vertexweightinfluence4f[j*4+l] = influence;
-                                                       loadmodel->surfmesh.data_vertexweightindex4i[j*4+l] = boneindex;
+                                                       weightinfluence[l] = influence;
+                                                       weightindex[l] = boneindex;
                                                        break;
                                                }
                                        }
                                }
                                data += sizeof(dpmbonevert_t);
                        }
                                                        break;
                                                }
                                        }
                                }
                                data += sizeof(dpmbonevert_t);
                        }
-                       sum = 0;
-                       for (l = 0;l < 4;l++)
-                               sum += loadmodel->surfmesh.data_vertexweightinfluence4f[j*4+l];
-                       if (sum && fabs(sum - 1) > (1.0f / 256.0f))
-                       {
-                               float f = 1.0f / sum;
-                               for (l = 0;l < 4;l++)
-                                       loadmodel->surfmesh.data_vertexweightinfluence4f[j*4+l] *= f;
-                       }
+                       loadmodel->surfmesh.blends[j] = Mod_Skeletal_CompressBlend(loadmodel, weightindex, weightinfluence);
                }
 
                // since dpm models do not have named sections, reuse their shader name as the section name
                }
 
                // since dpm models do not have named sections, reuse their shader name as the section name
@@ -2132,15 +2418,39 @@ void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
                Mod_ValidateElements(loadmodel->surfmesh.data_element3i + surface->num_firsttriangle * 3, surface->num_triangles, surface->num_firstvertex, surface->num_vertices, __FILE__, __LINE__);
        }
 
                Mod_ValidateElements(loadmodel->surfmesh.data_element3i + surface->num_firsttriangle * 3, surface->num_triangles, surface->num_firstvertex, surface->num_vertices, __FILE__, __LINE__);
        }
+       if (loadmodel->surfmesh.num_blends < meshvertices)
+               loadmodel->surfmesh.data_blendweights = (blendweights_t *)Mem_Realloc(loadmodel->mempool, loadmodel->surfmesh.data_blendweights, loadmodel->surfmesh.num_blends * sizeof(blendweights_t));
        Z_Free(bonepose);
        Mod_FreeSkinFiles(skinfiles);
        Z_Free(bonepose);
        Mod_FreeSkinFiles(skinfiles);
+       Mod_MakeSortedSurfaces(loadmodel);
 
        // compute all the mesh information that was not loaded from the file
 
        // compute all the mesh information that was not loaded from the file
+       if (loadmodel->surfmesh.data_element3s)
+               for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
+                       loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
        Mod_BuildBaseBonePoses();
        Mod_BuildBaseBonePoses();
-       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, true);
-       Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
+       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);
+       if (loadmodel->surfmesh.data_neighbor3i)
+               Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
 
 
-       loadmodel->surfmesh.isanimated = loadmodel->numframes > 1 || loadmodel->animscenes[0].framecount > 1;
+       if (!loadmodel->surfmesh.isanimated)
+       {
+               Mod_MakeCollisionBIH(loadmodel, true, &loadmodel->collision_bih);
+               loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
+               loadmodel->TraceBrush = Mod_CollisionBIH_TraceBrush;
+               loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
+               loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
+               loadmodel->PointSuperContents = Mod_CollisionBIH_PointSuperContents_Mesh;
+       }
+
+       // because shaders can do somewhat unexpected things, check for unusual features now
+       for (i = 0;i < loadmodel->num_textures;i++)
+       {
+               if (loadmodel->data_textures[i].basematerialflags & (MATERIALFLAG_SKY))
+                       mod->DrawSky = R_Q1BSP_DrawSky;
+               if (loadmodel->data_textures[i].basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
+                       mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
+       }
 }
 
 // no idea why PSK/PSA files contain weird quaternions but they do...
 }
 
 // no idea why PSK/PSA files contain weird quaternions but they do...
@@ -2156,7 +2466,7 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        pskmatt_t *matts;
        pskboneinfo_t *bones;
        pskrawweights_t *rawweights;
        pskmatt_t *matts;
        pskboneinfo_t *bones;
        pskrawweights_t *rawweights;
-       pskboneinfo_t *animbones;
+       //pskboneinfo_t *animbones;
        pskaniminfo_t *anims;
        pskanimkeys_t *animkeys;
        void *animfilebuffer, *animbuffer, *animbufferend;
        pskaniminfo_t *anims;
        pskanimkeys_t *animkeys;
        void *animfilebuffer, *animbuffer, *animbufferend;
@@ -2165,6 +2475,7 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        skinfile_t *skinfiles;
        char animname[MAX_QPATH];
        size_t size;
        skinfile_t *skinfiles;
        char animname[MAX_QPATH];
        size_t size;
+       float biggestorigin;
 
        pchunk = (pskchunk_t *)buffer;
        if (strcmp(pchunk->id, "ACTRHEAD"))
 
        pchunk = (pskchunk_t *)buffer;
        if (strcmp(pchunk->id, "ACTRHEAD"))
@@ -2173,16 +2484,19 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->modeldatatypestring = "PSK";
 
        loadmodel->type = mod_alias;
        loadmodel->modeldatatypestring = "PSK";
 
        loadmodel->type = mod_alias;
-       loadmodel->AnimateVertices = Mod_Skeletal_AnimateVertices;
        loadmodel->DrawSky = NULL;
        loadmodel->DrawAddWaterPlanes = NULL;
        loadmodel->Draw = R_Q1BSP_Draw;
        loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
        loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
        loadmodel->DrawSky = NULL;
        loadmodel->DrawAddWaterPlanes = NULL;
        loadmodel->Draw = R_Q1BSP_Draw;
        loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
        loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
+       loadmodel->DrawPrepass = R_Q1BSP_DrawPrepass;
+       loadmodel->CompileShadowMap = R_Q1BSP_CompileShadowMap;
+       loadmodel->DrawShadowMap = R_Q1BSP_DrawShadowMap;
        loadmodel->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
        loadmodel->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
        loadmodel->DrawLight = R_Q1BSP_DrawLight;
        loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
        loadmodel->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
        loadmodel->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
        loadmodel->DrawLight = R_Q1BSP_DrawLight;
        loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
+       loadmodel->TraceLine = Mod_MDLMD2MD3_TraceLine;
        loadmodel->PointSuperContents = NULL;
        loadmodel->synctype = ST_RAND;
 
        loadmodel->PointSuperContents = NULL;
        loadmodel->synctype = ST_RAND;
 
@@ -2190,8 +2504,8 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        strlcat(animname, ".psa", sizeof(animname));
        animbuffer = animfilebuffer = FS_LoadFile(animname, loadmodel->mempool, false, &filesize);
        animbufferend = (void *)((unsigned char*)animbuffer + (int)filesize);
        strlcat(animname, ".psa", sizeof(animname));
        animbuffer = animfilebuffer = FS_LoadFile(animname, loadmodel->mempool, false, &filesize);
        animbufferend = (void *)((unsigned char*)animbuffer + (int)filesize);
-       if (animbuffer == NULL)
-               Host_Error("%s: can't find .psa file (%s)", loadmodel->name, animname);
+       if (!animbuffer)
+               animbufferend = animbuffer;
 
        numpnts = 0;
        pnts = NULL;
 
        numpnts = 0;
        pnts = NULL;
@@ -2217,8 +2531,8 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
                version = LittleLong(pchunk->version);
                recordsize = LittleLong(pchunk->recordsize);
                numrecords = LittleLong(pchunk->numrecords);
                version = LittleLong(pchunk->version);
                recordsize = LittleLong(pchunk->recordsize);
                numrecords = LittleLong(pchunk->numrecords);
-               if (developer.integer >= 100)
-                       Con_Printf("%s: %s %x: %i * %i = %i\n", loadmodel->name, pchunk->id, version, recordsize, numrecords, recordsize * numrecords);
+               if (developer_extra.integer)
+                       Con_DPrintf("%s: %s %x: %i * %i = %i\n", loadmodel->name, pchunk->id, version, recordsize, numrecords, recordsize * numrecords);
                if (version != 0x1e83b9 && version != 0x1e9179 && version != 0x2e && version != 0x12f2bc && version != 0x12f2f0)
                        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);
                if (!strcmp(pchunk->id, "ACTRHEAD"))
                if (version != 0x1e83b9 && version != 0x1e9179 && version != 0x2e && version != 0x12f2bc && version != 0x12f2f0)
                        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);
                if (!strcmp(pchunk->id, "ACTRHEAD"))
@@ -2388,8 +2702,8 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
                version = LittleLong(pchunk->version);
                recordsize = LittleLong(pchunk->recordsize);
                numrecords = LittleLong(pchunk->numrecords);
                version = LittleLong(pchunk->version);
                recordsize = LittleLong(pchunk->recordsize);
                numrecords = LittleLong(pchunk->numrecords);
-               if (developer.integer >= 100)
-                       Con_Printf("%s: %s %x: %i * %i = %i\n", animname, pchunk->id, version, recordsize, numrecords, recordsize * numrecords);
+               if (developer_extra.integer)
+                       Con_DPrintf("%s: %s %x: %i * %i = %i\n", animname, pchunk->id, version, recordsize, numrecords, recordsize * numrecords);
                if (version != 0x1e83b9 && version != 0x1e9179 && version != 0x2e && version != 0x12f2bc && version != 0x12f2f0)
                        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);
                if (!strcmp(pchunk->id, "ANIMHEAD"))
                if (version != 0x1e83b9 && version != 0x1e9179 && version != 0x2e && version != 0x12f2bc && version != 0x12f2f0)
                        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);
                if (!strcmp(pchunk->id, "ANIMHEAD"))
@@ -2403,7 +2717,7 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
                                Host_Error("%s: %s has unsupported recordsize", animname, pchunk->id);
                        // byteswap in place and keep the pointer
                        numanimbones = numrecords;
                                Host_Error("%s: %s has unsupported recordsize", animname, pchunk->id);
                        // byteswap in place and keep the pointer
                        numanimbones = numrecords;
-                       animbones = (pskboneinfo_t *)animbuffer;
+                       //animbones = (pskboneinfo_t *)animbuffer;
                        // NOTE: supposedly psa does not need to match the psk model, the
                        // bones missing from the psa would simply use their base
                        // positions from the psk, but this is hard for me to implement
                        // NOTE: supposedly psa does not need to match the psk model, the
                        // bones missing from the psa would simply use their base
                        // positions from the psk, but this is hard for me to implement
@@ -2509,15 +2823,19 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
                        Con_Printf("%s: unknown chunk ID \"%s\"\n", animname, pchunk->id);
        }
 
                        Con_Printf("%s: unknown chunk ID \"%s\"\n", animname, pchunk->id);
        }
 
-       if (!numpnts || !pnts || !numvtxw || !vtxw || !numfaces || !faces || !nummatts || !matts || !numbones || !bones || !numrawweights || !rawweights || !numanims || !anims || !numanimkeys || !animkeys)
+       if (!numpnts || !pnts || !numvtxw || !vtxw || !numfaces || !faces || !nummatts || !matts || !numbones || !bones || !numrawweights || !rawweights)
                Host_Error("%s: missing required chunks", loadmodel->name);
 
                Host_Error("%s: missing required chunks", loadmodel->name);
 
-       loadmodel->numframes = 0;
-       for (index = 0;index < numanims;index++)
-               loadmodel->numframes += anims[index].numframes;
-
-       if (numanimkeys != numbones * loadmodel->numframes)
-               Host_Error("%s: %s has incorrect number of animation keys", animname, pchunk->id);
+       if (numanims)
+       {
+               loadmodel->numframes = 0;
+               for (index = 0;index < numanims;index++)
+                       loadmodel->numframes += anims[index].numframes;
+               if (numanimkeys != numbones * loadmodel->numframes)
+                       Host_Error("%s: %s has incorrect number of animation keys", animname, pchunk->id);
+       }
+       else
+               loadmodel->numframes = loadmodel->num_poses = 1;
 
        meshvertices = numvtxw;
        meshtriangles = numfaces;
 
        meshvertices = numvtxw;
        meshtriangles = numfaces;
@@ -2527,39 +2845,42 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        if (loadmodel->numskins < 1)
                loadmodel->numskins = 1;
        loadmodel->num_bones = numbones;
        if (loadmodel->numskins < 1)
                loadmodel->numskins = 1;
        loadmodel->num_bones = numbones;
-       loadmodel->num_poses = loadmodel->num_bones * loadmodel->numframes;
+       loadmodel->num_poses = loadmodel->numframes;
        loadmodel->nummodelsurfaces = loadmodel->num_surfaces = nummatts;
        loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
        loadmodel->num_texturesperskin = loadmodel->num_surfaces;
        loadmodel->surfmesh.num_vertices = meshvertices;
        loadmodel->surfmesh.num_triangles = meshtriangles;
        loadmodel->nummodelsurfaces = loadmodel->num_surfaces = nummatts;
        loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
        loadmodel->num_texturesperskin = loadmodel->num_surfaces;
        loadmodel->surfmesh.num_vertices = meshvertices;
        loadmodel->surfmesh.num_triangles = meshtriangles;
+       loadmodel->surfmesh.isanimated = loadmodel->num_bones > 1 || loadmodel->numframes > 1 || (loadmodel->animscenes && loadmodel->animscenes[0].framecount > 1);
+       loadmodel->AnimateVertices = loadmodel->surfmesh.isanimated ? Mod_Skeletal_AnimateVertices : NULL;
        // do most allocations as one merged chunk
        // do most allocations as one merged chunk
-       size = loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t) + loadmodel->surfmesh.num_triangles * sizeof(int[3]) + loadmodel->surfmesh.num_triangles * sizeof(int[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[2]) + loadmodel->surfmesh.num_vertices * sizeof(int[4]) + loadmodel->surfmesh.num_vertices * sizeof(float[4]) + /*loadmodel->num_poses * sizeof(float[12]) + */loadmodel->num_bones * sizeof(float[12]) + loadmodel->numskins * sizeof(animscene_t) + loadmodel->num_bones * sizeof(aliasbone_t) + loadmodel->numframes * sizeof(animscene_t) + ((loadmodel->surfmesh.num_vertices <= 65536) ? (loadmodel->surfmesh.num_triangles * sizeof(unsigned short[3])) : 0);
+       size = loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t) + loadmodel->surfmesh.num_triangles * sizeof(int[3]) + (r_enableshadowvolumes.integer ? loadmodel->surfmesh.num_triangles * sizeof(int[3]) : 0)  + loadmodel->surfmesh.num_vertices * sizeof(float[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[2]) + loadmodel->surfmesh.num_vertices * sizeof(unsigned short) + loadmodel->num_poses * loadmodel->num_bones * sizeof(short[6]) + loadmodel->num_bones * sizeof(float[12]) + loadmodel->numskins * sizeof(animscene_t) + loadmodel->num_bones * sizeof(aliasbone_t) + loadmodel->numframes * sizeof(animscene_t) + ((loadmodel->surfmesh.num_vertices <= 65536) ? (loadmodel->surfmesh.num_triangles * sizeof(unsigned short[3])) : 0);
        data = (unsigned char *)Mem_Alloc(loadmodel->mempool, size);
        loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
        data = (unsigned char *)Mem_Alloc(loadmodel->mempool, size);
        loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
-       loadmodel->surfacelist = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
+       loadmodel->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
        loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
        loadmodel->surfmesh.data_element3i = (int *)data;data += loadmodel->surfmesh.num_triangles * sizeof(int[3]);
        loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
        loadmodel->surfmesh.data_element3i = (int *)data;data += loadmodel->surfmesh.num_triangles * sizeof(int[3]);
-       loadmodel->surfmesh.data_neighbor3i = (int *)data;data += loadmodel->surfmesh.num_triangles * sizeof(int[3]);
+       if (r_enableshadowvolumes.integer)
+       {
+               loadmodel->surfmesh.data_neighbor3i = (int *)data;data += loadmodel->surfmesh.num_triangles * sizeof(int[3]);
+       }
        loadmodel->surfmesh.data_vertex3f = (float *)data;data += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
        loadmodel->surfmesh.data_svector3f = (float *)data;data += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
        loadmodel->surfmesh.data_tvector3f = (float *)data;data += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
        loadmodel->surfmesh.data_normal3f = (float *)data;data += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
        loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
        loadmodel->surfmesh.data_vertex3f = (float *)data;data += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
        loadmodel->surfmesh.data_svector3f = (float *)data;data += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
        loadmodel->surfmesh.data_tvector3f = (float *)data;data += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
        loadmodel->surfmesh.data_normal3f = (float *)data;data += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
        loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
-       loadmodel->surfmesh.data_vertexweightindex4i = (int *)data;data += loadmodel->surfmesh.num_vertices * sizeof(int[4]);
-       loadmodel->surfmesh.data_vertexweightinfluence4f = (float *)data;data += loadmodel->surfmesh.num_vertices * sizeof(float[4]);
-       //loadmodel->data_poses = (float *)data;data += loadmodel->num_poses * sizeof(float[12]);
        loadmodel->data_baseboneposeinverse = (float *)data;data += loadmodel->num_bones * sizeof(float[12]);
        loadmodel->skinscenes = (animscene_t *)data;data += loadmodel->numskins * sizeof(animscene_t);
        loadmodel->data_bones = (aliasbone_t *)data;data += loadmodel->num_bones * sizeof(aliasbone_t);
        loadmodel->animscenes = (animscene_t *)data;data += loadmodel->numframes * sizeof(animscene_t);
        loadmodel->data_baseboneposeinverse = (float *)data;data += loadmodel->num_bones * sizeof(float[12]);
        loadmodel->skinscenes = (animscene_t *)data;data += loadmodel->numskins * sizeof(animscene_t);
        loadmodel->data_bones = (aliasbone_t *)data;data += loadmodel->num_bones * sizeof(aliasbone_t);
        loadmodel->animscenes = (animscene_t *)data;data += loadmodel->numframes * sizeof(animscene_t);
+       loadmodel->surfmesh.num_blends = 0;
+       loadmodel->surfmesh.blends = (unsigned short *)data;data += meshvertices * sizeof(unsigned short);
        if (loadmodel->surfmesh.num_vertices <= 65536)
        {
                loadmodel->surfmesh.data_element3s = (unsigned short *)data;data += loadmodel->surfmesh.num_triangles * sizeof(unsigned short[3]);
        if (loadmodel->surfmesh.num_vertices <= 65536)
        {
                loadmodel->surfmesh.data_element3s = (unsigned short *)data;data += loadmodel->surfmesh.num_triangles * sizeof(unsigned short[3]);
-               for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
-                       loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
        }
        }
-       loadmodel->data_poses = Mem_Alloc(loadmodel->mempool, loadmodel->num_poses * sizeof(float[12]));
+       loadmodel->data_poses6s = (short *)data;data += loadmodel->num_poses * loadmodel->num_bones * sizeof(short[6]);
+       loadmodel->surfmesh.data_blendweights = (blendweights_t *)Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_vertices * sizeof(blendweights_t));
 
        for (i = 0;i < loadmodel->numskins;i++)
        {
 
        for (i = 0;i < loadmodel->numskins;i++)
        {
@@ -2574,7 +2895,7 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        {
                // since psk models do not have named sections, reuse their shader name as the section name
                Mod_BuildAliasSkinsFromSkinFiles(loadmodel->data_textures + index, skinfiles, matts[index].name, matts[index].name);
        {
                // since psk models do not have named sections, reuse their shader name as the section name
                Mod_BuildAliasSkinsFromSkinFiles(loadmodel->data_textures + index, skinfiles, matts[index].name, matts[index].name);
-               loadmodel->surfacelist[index] = index;
+               loadmodel->sortedmodelsurfaces[index] = index;
                loadmodel->data_surfaces[index].texture = loadmodel->data_textures + index;
                loadmodel->data_surfaces[index].num_firstvertex = 0;
                loadmodel->data_surfaces[index].num_vertices = loadmodel->surfmesh.num_vertices;
                loadmodel->data_surfaces[index].texture = loadmodel->data_textures + index;
                loadmodel->data_surfaces[index].num_firstvertex = 0;
                loadmodel->data_surfaces[index].num_vertices = loadmodel->surfmesh.num_vertices;
@@ -2616,12 +2937,37 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
                        Host_Error("%s bone[%i].parent >= %i", loadmodel->name, index, index);
        }
 
                        Host_Error("%s bone[%i].parent >= %i", loadmodel->name, index, index);
        }
 
+       // convert the basepose data
+       if (loadmodel->num_bones)
+       {
+               int boneindex;
+               matrix4x4_t *basebonepose;
+               float *outinvmatrix = loadmodel->data_baseboneposeinverse;
+               matrix4x4_t bonematrix;
+               matrix4x4_t tempbonematrix;
+               basebonepose = (matrix4x4_t *)Mem_Alloc(tempmempool, loadmodel->num_bones * sizeof(matrix4x4_t));
+               for (boneindex = 0;boneindex < loadmodel->num_bones;boneindex++)
+               {
+                       Matrix4x4_FromOriginQuat(&bonematrix, bones[boneindex].basepose.origin[0], bones[boneindex].basepose.origin[1], bones[boneindex].basepose.origin[2], bones[boneindex].basepose.quat[0], bones[boneindex].basepose.quat[1], bones[boneindex].basepose.quat[2], bones[boneindex].basepose.quat[3]);
+                       if (loadmodel->data_bones[boneindex].parent >= 0)
+                       {
+                               tempbonematrix = bonematrix;
+                               Matrix4x4_Concat(&bonematrix, basebonepose + loadmodel->data_bones[boneindex].parent, &tempbonematrix);
+                       }
+                       basebonepose[boneindex] = bonematrix;
+                       Matrix4x4_Invert_Simple(&tempbonematrix, basebonepose + boneindex);
+                       Matrix4x4_ToArray12FloatD3D(&tempbonematrix, outinvmatrix + 12*boneindex);
+               }
+               Mem_Free(basebonepose);
+       }
+
        // sort the psk point weights into the vertex weight tables
        // (which only accept up to 4 bones per vertex)
        for (index = 0;index < numvtxw;index++)
        {
        // sort the psk point weights into the vertex weight tables
        // (which only accept up to 4 bones per vertex)
        for (index = 0;index < numvtxw;index++)
        {
+               int weightindex[4] = { 0, 0, 0, 0 };
+               float weightinfluence[4] = { 0, 0, 0, 0 };
                int l;
                int l;
-               float sum;
                for (j = 0;j < numrawweights;j++)
                {
                        if (rawweights[j].pntsindex == vtxw[index].pntsindex)
                for (j = 0;j < numrawweights;j++)
                {
                        if (rawweights[j].pntsindex == vtxw[index].pntsindex)
@@ -2630,67 +2976,885 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
                                float influence = rawweights[j].weight;
                                for (l = 0;l < 4;l++)
                                {
                                float influence = rawweights[j].weight;
                                for (l = 0;l < 4;l++)
                                {
-                                       if (loadmodel->surfmesh.data_vertexweightinfluence4f[index*4+l] < influence)
+                                       if (weightinfluence[l] < influence)
                                        {
                                                // move lower influence weights out of the way first
                                                int l2;
                                                for (l2 = 3;l2 > l;l2--)
                                                {
                                        {
                                                // move lower influence weights out of the way first
                                                int l2;
                                                for (l2 = 3;l2 > l;l2--)
                                                {
-                                                       loadmodel->surfmesh.data_vertexweightinfluence4f[index*4+l2] = loadmodel->surfmesh.data_vertexweightinfluence4f[index*4+l2-1];
-                                                       loadmodel->surfmesh.data_vertexweightindex4i[index*4+l2] = loadmodel->surfmesh.data_vertexweightindex4i[index*4+l2-1];
+                                                       weightinfluence[l2] = weightinfluence[l2-1];
+                                                       weightindex[l2] = weightindex[l2-1];
                                                }
                                                // store the new weight
                                                }
                                                // store the new weight
-                                               loadmodel->surfmesh.data_vertexweightinfluence4f[index*4+l] = influence;
-                                               loadmodel->surfmesh.data_vertexweightindex4i[index*4+l] = boneindex;
+                                               weightinfluence[l] = influence;
+                                               weightindex[l] = boneindex;
                                                break;
                                        }
                                }
                        }
                }
                                                break;
                                        }
                                }
                        }
                }
-               sum = 0;
-               for (l = 0;l < 4;l++)
-                       sum += loadmodel->surfmesh.data_vertexweightinfluence4f[index*4+l];
-               if (sum && fabs(sum - 1) > (1.0f / 256.0f))
-               {
-                       float f = 1.0f / sum;
-                       for (l = 0;l < 4;l++)
-                               loadmodel->surfmesh.data_vertexweightinfluence4f[index*4+l] *= f;
-               }
+               loadmodel->surfmesh.blends[index] = Mod_Skeletal_CompressBlend(loadmodel, weightindex, weightinfluence);
        }
        }
+       if (loadmodel->surfmesh.num_blends < loadmodel->surfmesh.num_vertices)
+               loadmodel->surfmesh.data_blendweights = (blendweights_t *)Mem_Realloc(loadmodel->mempool, loadmodel->surfmesh.data_blendweights, loadmodel->surfmesh.num_blends * sizeof(blendweights_t));
 
        // set up the animscenes based on the anims
 
        // set up the animscenes based on the anims
-       for (index = 0, i = 0;index < numanims;index++)
+       if (numanims)
        {
        {
-               for (j = 0;j < anims[index].numframes;j++, i++)
+               for (index = 0, i = 0;index < numanims;index++)
+               {
+                       for (j = 0;j < anims[index].numframes;j++, i++)
+                       {
+                               dpsnprintf(loadmodel->animscenes[i].name, sizeof(loadmodel->animscenes[i].name), "%s_%d", anims[index].name, j);
+                               loadmodel->animscenes[i].firstframe = i;
+                               loadmodel->animscenes[i].framecount = 1;
+                               loadmodel->animscenes[i].loop = true;
+                               loadmodel->animscenes[i].framerate = anims[index].fps;
+                       }
+               }
+               // calculate the scaling value for bone origins so they can be compressed to short
+               biggestorigin = 0;
+               for (index = 0;index < numanimkeys;index++)
+               {
+                       pskanimkeys_t *k = animkeys + index;
+                       biggestorigin = max(biggestorigin, fabs(k->origin[0]));
+                       biggestorigin = max(biggestorigin, fabs(k->origin[1]));
+                       biggestorigin = max(biggestorigin, fabs(k->origin[2]));
+               }
+               loadmodel->num_posescale = biggestorigin / 32767.0f;
+               loadmodel->num_poseinvscale = 1.0f / loadmodel->num_posescale;
+       
+               // load the poses from the animkeys
+               for (index = 0;index < numanimkeys;index++)
                {
                {
-                       dpsnprintf(loadmodel->animscenes[i].name, sizeof(loadmodel->animscenes[i].name), "%s_%d", anims[index].name, j);
-                       loadmodel->animscenes[i].firstframe = i;
-                       loadmodel->animscenes[i].framecount = 1;
-                       loadmodel->animscenes[i].loop = true;
-                       loadmodel->animscenes[i].framerate = 10;
+                       pskanimkeys_t *k = animkeys + index;
+                       float quat[4];
+                       Vector4Copy(k->quat, quat);
+                       if (quat[3] > 0)
+                               Vector4Negate(quat, quat);
+                       Vector4Normalize2(quat, quat);
+                       // compress poses to the short[6] format for longterm storage
+                       loadmodel->data_poses6s[index*6+0] = k->origin[0] * loadmodel->num_poseinvscale;
+                       loadmodel->data_poses6s[index*6+1] = k->origin[1] * loadmodel->num_poseinvscale;
+                       loadmodel->data_poses6s[index*6+2] = k->origin[2] * loadmodel->num_poseinvscale;
+                       loadmodel->data_poses6s[index*6+3] = quat[0] * 32767.0f;
+                       loadmodel->data_poses6s[index*6+4] = quat[1] * 32767.0f;
+                       loadmodel->data_poses6s[index*6+5] = quat[2] * 32767.0f;
                }
        }
                }
        }
-
-       // load the poses from the animkeys
-       for (index = 0;index < numanimkeys;index++)
+       else
        {
        {
-               pskanimkeys_t *k = animkeys + index;
-               matrix4x4_t matrix;
-               Matrix4x4_FromOriginQuat(&matrix, k->origin[0], k->origin[1], k->origin[2], k->quat[0], k->quat[1], k->quat[2], k->quat[3]);
-               Matrix4x4_ToArray12FloatD3D(&matrix, loadmodel->data_poses + index*12);
+               strlcpy(loadmodel->animscenes[0].name, "base", sizeof(loadmodel->animscenes[0].name));
+               loadmodel->animscenes[0].firstframe = 0;
+               loadmodel->animscenes[0].framecount = 1;
+               loadmodel->animscenes[0].loop = true;
+               loadmodel->animscenes[0].framerate = 10;
+
+               // calculate the scaling value for bone origins so they can be compressed to short
+               biggestorigin = 0;
+               for (index = 0;index < numbones;index++)
+               {
+                       pskboneinfo_t *p = bones + index;
+                       biggestorigin = max(biggestorigin, fabs(p->basepose.origin[0]));
+                       biggestorigin = max(biggestorigin, fabs(p->basepose.origin[1]));
+                       biggestorigin = max(biggestorigin, fabs(p->basepose.origin[2]));
+               }
+               loadmodel->num_posescale = biggestorigin / 32767.0f;
+               loadmodel->num_poseinvscale = 1.0f / loadmodel->num_posescale;
+       
+               // load the basepose as a frame
+               for (index = 0;index < numbones;index++)
+               {
+                       pskboneinfo_t *p = bones + index;
+                       float quat[4];
+                       Vector4Copy(p->basepose.quat, quat);
+                       if (quat[3] > 0)
+                               Vector4Negate(quat, quat);
+                       Vector4Normalize2(quat, quat);
+                       // compress poses to the short[6] format for longterm storage
+                       loadmodel->data_poses6s[index*6+0] = p->basepose.origin[0] * loadmodel->num_poseinvscale;
+                       loadmodel->data_poses6s[index*6+1] = p->basepose.origin[1] * loadmodel->num_poseinvscale;
+                       loadmodel->data_poses6s[index*6+2] = p->basepose.origin[2] * loadmodel->num_poseinvscale;
+                       loadmodel->data_poses6s[index*6+3] = quat[0] * 32767.0f;
+                       loadmodel->data_poses6s[index*6+4] = quat[1] * 32767.0f;
+                       loadmodel->data_poses6s[index*6+5] = quat[2] * 32767.0f;
+               }
        }
        }
+
        Mod_FreeSkinFiles(skinfiles);
        Mod_FreeSkinFiles(skinfiles);
-       Mem_Free(animfilebuffer);
+       if (animfilebuffer)
+               Mem_Free(animfilebuffer);
+       Mod_MakeSortedSurfaces(loadmodel);
 
        // compute all the mesh information that was not loaded from the file
        // TODO: honor smoothing groups somehow?
 
        // compute all the mesh information that was not loaded from the file
        // TODO: honor smoothing groups somehow?
+       if (loadmodel->surfmesh.data_element3s)
+               for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
+                       loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
        Mod_ValidateElements(loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles, 0, loadmodel->surfmesh.num_vertices, __FILE__, __LINE__);
        Mod_ValidateElements(loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles, 0, loadmodel->surfmesh.num_vertices, __FILE__, __LINE__);
-       Mod_BuildBaseBonePoses();
-       Mod_BuildNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_normal3f, true);
-       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, true);
-       Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
+       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);
+       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);
+       if (loadmodel->surfmesh.data_neighbor3i)
+               Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
        Mod_Alias_CalculateBoundingBox();
 
        Mod_Alias_CalculateBoundingBox();
 
-       loadmodel->surfmesh.isanimated = loadmodel->numframes > 1 || loadmodel->animscenes[0].framecount > 1;
+       if (!loadmodel->surfmesh.isanimated)
+       {
+               Mod_MakeCollisionBIH(loadmodel, true, &loadmodel->collision_bih);
+               loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
+               loadmodel->TraceBrush = Mod_CollisionBIH_TraceBrush;
+               loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
+               loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
+               loadmodel->PointSuperContents = Mod_CollisionBIH_PointSuperContents_Mesh;
+       }
+
+       // because shaders can do somewhat unexpected things, check for unusual features now
+       for (i = 0;i < loadmodel->num_textures;i++)
+       {
+               if (loadmodel->data_textures[i].basematerialflags & (MATERIALFLAG_SKY))
+                       mod->DrawSky = R_Q1BSP_DrawSky;
+               if (loadmodel->data_textures[i].basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
+                       mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
+       }
 }
 
 }
 
+void Mod_INTERQUAKEMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
+{
+       unsigned char *data;
+       const char *text;
+       const unsigned char *pbase, *pend;
+       iqmheader_t header;
+       skinfile_t *skinfiles;
+       int i, j, k, meshvertices, meshtriangles;
+       float biggestorigin;
+       const unsigned int *inelements;
+       int *outelements;
+       const int *inneighbors;
+       int *outneighbors;
+       float *outvertex, *outnormal, *outtexcoord, *outsvector, *outtvector, *outcolor;
+       // this pointers into the file data are read only through Little* functions so they can be unaligned memory
+       const float *vnormal = NULL;
+       const float *vposition = NULL;
+       const float *vtangent = NULL;
+       const float *vtexcoord = NULL;
+       const float *vcolor4f = NULL;
+       const unsigned char *vblendindexes = NULL;
+       const unsigned char *vblendweights = NULL;
+       const unsigned char *vcolor4ub = NULL;
+       const unsigned short *framedata = NULL;
+       // temporary memory allocations (because the data in the file may be misaligned)
+       iqmanim_t *anims = NULL;
+       iqmbounds_t *bounds = NULL;
+       iqmjoint1_t *joint1 = NULL;
+       iqmjoint_t *joint = NULL;
+       iqmmesh_t *meshes = NULL;
+       iqmpose1_t *pose1 = NULL;
+       iqmpose_t *pose = NULL;
+       iqmvertexarray_t *vas = NULL;
+
+       pbase = (unsigned char *)buffer;
+       pend = (unsigned char *)bufferend;
+
+       if (pbase + sizeof(iqmheader_t) > pend)
+               Host_Error ("Mod_INTERQUAKEMODEL_Load: %s is not an Inter-Quake Model %d", loadmodel->name, (int)(pend - pbase));
+
+       // copy struct (otherwise it may be misaligned)
+       // LordHavoc: okay it's definitely not misaligned here, but for consistency...
+       memcpy(&header, pbase, sizeof(iqmheader_t));
+
+       if (memcmp(header.id, "INTERQUAKEMODEL", 16))
+               Host_Error ("Mod_INTERQUAKEMODEL_Load: %s is not an Inter-Quake Model", loadmodel->name);
+       if (LittleLong(header.version) != 1 && LittleLong(header.version) != 2)
+               Host_Error ("Mod_INTERQUAKEMODEL_Load: only version 1 and 2 models are currently supported (name = %s)", loadmodel->name);
+
+       loadmodel->modeldatatypestring = "IQM";
+
+       loadmodel->type = mod_alias;
+       loadmodel->synctype = ST_RAND;
+
+       // byteswap header
+       header.version = LittleLong(header.version);
+       header.filesize = LittleLong(header.filesize);
+       header.flags = LittleLong(header.flags);
+       header.num_text = LittleLong(header.num_text);
+       header.ofs_text = LittleLong(header.ofs_text);
+       header.num_meshes = LittleLong(header.num_meshes);
+       header.ofs_meshes = LittleLong(header.ofs_meshes);
+       header.num_vertexarrays = LittleLong(header.num_vertexarrays);
+       header.num_vertexes = LittleLong(header.num_vertexes);
+       header.ofs_vertexarrays = LittleLong(header.ofs_vertexarrays);
+       header.num_triangles = LittleLong(header.num_triangles);
+       header.ofs_triangles = LittleLong(header.ofs_triangles);
+       header.ofs_neighbors = LittleLong(header.ofs_neighbors);
+       header.num_joints = LittleLong(header.num_joints);
+       header.ofs_joints = LittleLong(header.ofs_joints);
+       header.num_poses = LittleLong(header.num_poses);
+       header.ofs_poses = LittleLong(header.ofs_poses);
+       header.num_anims = LittleLong(header.num_anims);
+       header.ofs_anims = LittleLong(header.ofs_anims);
+       header.num_frames = LittleLong(header.num_frames);
+       header.num_framechannels = LittleLong(header.num_framechannels);
+       header.ofs_frames = LittleLong(header.ofs_frames);
+       header.ofs_bounds = LittleLong(header.ofs_bounds);
+       header.num_comment = LittleLong(header.num_comment);
+       header.ofs_comment = LittleLong(header.ofs_comment);
+       header.num_extensions = LittleLong(header.num_extensions);
+       header.ofs_extensions = LittleLong(header.ofs_extensions);
+
+       if (header.version == 1)
+       {
+               if (pbase + header.ofs_joints + header.num_joints*sizeof(iqmjoint1_t) > pend ||
+                       pbase + header.ofs_poses + header.num_poses*sizeof(iqmpose1_t) > pend)
+               {
+                       Con_Printf("%s has invalid size or offset information\n", loadmodel->name);
+                       return;
+               }
+       }
+       else
+       {
+               if (pbase + header.ofs_joints + header.num_joints*sizeof(iqmjoint_t) > pend ||
+                       pbase + header.ofs_poses + header.num_poses*sizeof(iqmpose_t) > pend)
+               {
+                       Con_Printf("%s has invalid size or offset information\n", loadmodel->name);
+                       return;
+               }
+       }
+       if (pbase + header.ofs_text + header.num_text > pend ||
+               pbase + header.ofs_meshes + header.num_meshes*sizeof(iqmmesh_t) > pend ||
+               pbase + header.ofs_vertexarrays + header.num_vertexarrays*sizeof(iqmvertexarray_t) > pend ||
+               pbase + header.ofs_triangles + header.num_triangles*sizeof(int[3]) > pend ||
+               (header.ofs_neighbors && pbase + header.ofs_neighbors + header.num_triangles*sizeof(int[3]) > pend) ||
+               pbase + header.ofs_anims + header.num_anims*sizeof(iqmanim_t) > pend ||
+               pbase + header.ofs_frames + header.num_frames*header.num_framechannels*sizeof(unsigned short) > pend ||
+               (header.ofs_bounds && pbase + header.ofs_bounds + header.num_frames*sizeof(iqmbounds_t) > pend) ||
+               pbase + header.ofs_comment + header.num_comment > pend)
+       {
+               Con_Printf("%s has invalid size or offset information\n", loadmodel->name);
+               return;
+       }
+
+       // copy structs to make them aligned in memory (otherwise we crash on Sparc and PowerPC and others)
+       if (header.num_vertexarrays)
+               vas = (iqmvertexarray_t *)(pbase + header.ofs_vertexarrays);
+       if (header.num_anims)
+               anims = (iqmanim_t *)(pbase + header.ofs_anims);
+       if (header.ofs_bounds)
+               bounds = (iqmbounds_t *)(pbase + header.ofs_bounds);
+       if (header.num_meshes)
+               meshes = (iqmmesh_t *)(pbase + header.ofs_meshes);
+
+       for (i = 0;i < (int)header.num_vertexarrays;i++)
+       {
+               iqmvertexarray_t va;
+               size_t vsize;
+               va.type = LittleLong(vas[i].type);
+               va.flags = LittleLong(vas[i].flags);
+               va.format = LittleLong(vas[i].format);
+               va.size = LittleLong(vas[i].size);
+               va.offset = LittleLong(vas[i].offset);
+               vsize = header.num_vertexes*va.size;
+               switch (va.format)
+               { 
+               case IQM_FLOAT: vsize *= sizeof(float); break;
+               case IQM_UBYTE: vsize *= sizeof(unsigned char); break;
+               default: continue;
+               }
+               if (pbase + va.offset + vsize > pend)
+                       continue;
+               // no need to copy the vertex data for alignment because LittleLong/LittleShort will be invoked on reading them, and the destination is aligned
+               switch (va.type)
+               {
+               case IQM_POSITION:
+                       if (va.format == IQM_FLOAT && va.size == 3)
+                               vposition = (const float *)(pbase + va.offset);
+                       break;
+               case IQM_TEXCOORD:
+                       if (va.format == IQM_FLOAT && va.size == 2)
+                               vtexcoord = (const float *)(pbase + va.offset);
+                       break;
+               case IQM_NORMAL:
+                       if (va.format == IQM_FLOAT && va.size == 3)
+                               vnormal = (const float *)(pbase + va.offset);
+                       break;
+               case IQM_TANGENT:
+                       if (va.format == IQM_FLOAT && va.size == 4)
+                               vtangent = (const float *)(pbase + va.offset);
+                       break;
+               case IQM_BLENDINDEXES:
+                       if (va.format == IQM_UBYTE && va.size == 4)
+                               vblendindexes = (const unsigned char *)(pbase + va.offset);
+                       break;
+               case IQM_BLENDWEIGHTS:
+                       if (va.format == IQM_UBYTE && va.size == 4)
+                               vblendweights = (const unsigned char *)(pbase + va.offset);
+                       break;
+               case IQM_COLOR:
+                       if (va.format == IQM_FLOAT && va.size == 4)
+                               vcolor4f = (const float *)(pbase + va.offset);
+                       if (va.format == IQM_UBYTE && va.size == 4)
+                               vcolor4ub = (const unsigned char *)(pbase + va.offset);
+                       break;
+               }
+       }
+       if (header.num_vertexes > 0 && (!vposition || !vtexcoord || ((header.num_frames > 0 || header.num_anims > 0) && (!vblendindexes || !vblendweights))))
+       {
+               Con_Printf("%s is missing vertex array data\n", loadmodel->name);
+               return;
+       }
+
+       text = header.num_text && header.ofs_text ? (const char *)(pbase + header.ofs_text) : "";
+
+       loadmodel->DrawSky = NULL;
+       loadmodel->DrawAddWaterPlanes = NULL;
+       loadmodel->Draw = R_Q1BSP_Draw;
+       loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
+       loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
+       loadmodel->DrawPrepass = R_Q1BSP_DrawPrepass;
+       loadmodel->CompileShadowMap = R_Q1BSP_CompileShadowMap;
+       loadmodel->DrawShadowMap = R_Q1BSP_DrawShadowMap;
+       loadmodel->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
+       loadmodel->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
+       loadmodel->DrawLight = R_Q1BSP_DrawLight;
+       loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
+       loadmodel->TraceLine = Mod_MDLMD2MD3_TraceLine;
+       loadmodel->PointSuperContents = NULL;
+
+       // load external .skin files if present
+       skinfiles = Mod_LoadSkinFiles();
+       if (loadmodel->numskins < 1)
+               loadmodel->numskins = 1;
+
+       loadmodel->numframes = max(header.num_anims, 1);
+       loadmodel->num_bones = header.num_joints;
+       loadmodel->num_poses = max(header.num_frames, 1);
+       loadmodel->nummodelsurfaces = loadmodel->num_surfaces = header.num_meshes;
+       loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
+       loadmodel->num_texturesperskin = loadmodel->num_surfaces;
+       loadmodel->AnimateVertices = Mod_Skeletal_AnimateVertices; // updated later
+
+       meshvertices = header.num_vertexes;
+       meshtriangles = header.num_triangles;
+
+       // do most allocations as one merged chunk
+       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) + (r_enableshadowvolumes.integer ? meshtriangles * sizeof(int[3]) : 0) + meshvertices * (sizeof(float[14]) + (vcolor4f || vcolor4ub ? sizeof(float[4]) : 0)) + (vblendindexes && vblendweights ? meshvertices * sizeof(unsigned short) : 0) + loadmodel->num_poses * loadmodel->num_bones * sizeof(short[6]) + loadmodel->num_bones * sizeof(float[12]) + loadmodel->numskins * sizeof(animscene_t) + loadmodel->num_bones * sizeof(aliasbone_t) + loadmodel->numframes * sizeof(animscene_t));
+       loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
+       loadmodel->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
+       loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
+       loadmodel->surfmesh.num_vertices = meshvertices;
+       loadmodel->surfmesh.num_triangles = meshtriangles;
+       loadmodel->surfmesh.data_element3i = (int *)data;data += meshtriangles * sizeof(int[3]);
+       if (r_enableshadowvolumes.integer)
+       {
+               loadmodel->surfmesh.data_neighbor3i = (int *)data;data += meshtriangles * sizeof(int[3]);
+       }
+       loadmodel->surfmesh.data_vertex3f = (float *)data;data += meshvertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_svector3f = (float *)data;data += meshvertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_tvector3f = (float *)data;data += meshvertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_normal3f = (float *)data;data += meshvertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += meshvertices * sizeof(float[2]);
+       if (vcolor4f || vcolor4ub)
+       {
+               loadmodel->surfmesh.data_lightmapcolor4f = (float *)data;data += meshvertices * sizeof(float[4]);
+       }
+       loadmodel->data_baseboneposeinverse = (float *)data;data += loadmodel->num_bones * sizeof(float[12]);
+       loadmodel->skinscenes = (animscene_t *)data;data += loadmodel->numskins * sizeof(animscene_t);
+       loadmodel->data_bones = (aliasbone_t *)data;data += loadmodel->num_bones * sizeof(aliasbone_t);
+       loadmodel->animscenes = (animscene_t *)data;data += loadmodel->numframes * sizeof(animscene_t);
+       if (vblendindexes && vblendweights)
+       {
+               loadmodel->surfmesh.num_blends = 0;
+               loadmodel->surfmesh.blends = (unsigned short *)data;data += meshvertices * sizeof(unsigned short);
+       }
+       if (meshvertices <= 65536)
+       {
+               loadmodel->surfmesh.data_element3s = (unsigned short *)data;data += meshtriangles * sizeof(unsigned short[3]);
+       }
+       loadmodel->data_poses6s = (short *)data;data += loadmodel->num_poses * loadmodel->num_bones * sizeof(short[6]);
+       if (vblendindexes && vblendweights)
+               loadmodel->surfmesh.data_blendweights = (blendweights_t *)Mem_Alloc(loadmodel->mempool, meshvertices * sizeof(blendweights_t));
+
+       for (i = 0;i < loadmodel->numskins;i++)
+       {
+               loadmodel->skinscenes[i].firstframe = i;
+               loadmodel->skinscenes[i].framecount = 1;
+               loadmodel->skinscenes[i].loop = true;
+               loadmodel->skinscenes[i].framerate = 10;
+       }
+
+       // load the bone info
+       if (header.version == 1)
+       {
+               iqmjoint1_t *injoint1 = (iqmjoint1_t *)(pbase + header.ofs_joints);
+               if (loadmodel->num_bones)
+                       joint1 = (iqmjoint1_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_bones * sizeof(iqmjoint1_t));
+               for (i = 0;i < loadmodel->num_bones;i++)
+               {
+                       matrix4x4_t relbase, relinvbase, pinvbase, invbase;
+                       joint1[i].name = LittleLong(injoint1[i].name);
+                       joint1[i].parent = LittleLong(injoint1[i].parent);
+                       for (j = 0;j < 3;j++)
+                       {
+                               joint1[i].origin[j] = LittleFloat(injoint1[i].origin[j]);
+                               joint1[i].rotation[j] = LittleFloat(injoint1[i].rotation[j]);
+                               joint1[i].scale[j] = LittleFloat(injoint1[i].scale[j]);
+                       }
+                       strlcpy(loadmodel->data_bones[i].name, &text[joint1[i].name], sizeof(loadmodel->data_bones[i].name));
+                       loadmodel->data_bones[i].parent = joint1[i].parent;
+                       if (loadmodel->data_bones[i].parent >= i)
+                               Host_Error("%s bone[%i].parent >= %i", loadmodel->name, i, i);
+                       Matrix4x4_FromDoom3Joint(&relbase, joint1[i].origin[0], joint1[i].origin[1], joint1[i].origin[2], joint1[i].rotation[0], joint1[i].rotation[1], joint1[i].rotation[2]);
+                       Matrix4x4_Invert_Simple(&relinvbase, &relbase);
+                       if (loadmodel->data_bones[i].parent >= 0)
+                       {
+                               Matrix4x4_FromArray12FloatD3D(&pinvbase, loadmodel->data_baseboneposeinverse + 12*loadmodel->data_bones[i].parent);
+                               Matrix4x4_Concat(&invbase, &relinvbase, &pinvbase);
+                               Matrix4x4_ToArray12FloatD3D(&invbase, loadmodel->data_baseboneposeinverse + 12*i);
+                       }
+                       else Matrix4x4_ToArray12FloatD3D(&relinvbase, loadmodel->data_baseboneposeinverse + 12*i);
+               }
+       }
+       else
+       {
+               iqmjoint_t *injoint = (iqmjoint_t *)(pbase + header.ofs_joints);
+               if (header.num_joints)
+                       joint = (iqmjoint_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_bones * sizeof(iqmjoint_t));
+               for (i = 0;i < loadmodel->num_bones;i++)
+               {
+                       matrix4x4_t relbase, relinvbase, pinvbase, invbase;
+                       joint[i].name = LittleLong(injoint[i].name);
+                       joint[i].parent = LittleLong(injoint[i].parent);
+                       for (j = 0;j < 3;j++)
+                       {
+                               joint[i].origin[j] = LittleFloat(injoint[i].origin[j]);
+                               joint[i].rotation[j] = LittleFloat(injoint[i].rotation[j]);
+                               joint[i].scale[j] = LittleFloat(injoint[i].scale[j]);
+                       }
+                       joint[i].rotation[3] = LittleFloat(injoint[i].rotation[3]);
+                       strlcpy(loadmodel->data_bones[i].name, &text[joint[i].name], sizeof(loadmodel->data_bones[i].name));
+                       loadmodel->data_bones[i].parent = joint[i].parent;
+                       if (loadmodel->data_bones[i].parent >= i)
+                               Host_Error("%s bone[%i].parent >= %i", loadmodel->name, i, i);
+                       if (joint[i].rotation[3] > 0)
+                               Vector4Negate(joint[i].rotation, joint[i].rotation);
+                       Vector4Normalize2(joint[i].rotation, joint[i].rotation);
+                       Matrix4x4_FromDoom3Joint(&relbase, joint[i].origin[0], joint[i].origin[1], joint[i].origin[2], joint[i].rotation[0], joint[i].rotation[1], joint[i].rotation[2]);
+                       Matrix4x4_Invert_Simple(&relinvbase, &relbase);
+                       if (loadmodel->data_bones[i].parent >= 0)
+                       {
+                               Matrix4x4_FromArray12FloatD3D(&pinvbase, loadmodel->data_baseboneposeinverse + 12*loadmodel->data_bones[i].parent);
+                               Matrix4x4_Concat(&invbase, &relinvbase, &pinvbase);
+                               Matrix4x4_ToArray12FloatD3D(&invbase, loadmodel->data_baseboneposeinverse + 12*i);
+                       }       
+                       else Matrix4x4_ToArray12FloatD3D(&relinvbase, loadmodel->data_baseboneposeinverse + 12*i);
+               }
+       }
+
+       // set up the animscenes based on the anims
+       for (i = 0;i < (int)header.num_anims;i++)
+       {
+               iqmanim_t anim;
+               anim.name = LittleLong(anims[i].name);
+               anim.first_frame = LittleLong(anims[i].first_frame);
+               anim.num_frames = LittleLong(anims[i].num_frames);
+               anim.framerate = LittleFloat(anims[i].framerate);
+               anim.flags = LittleLong(anims[i].flags);
+               strlcpy(loadmodel->animscenes[i].name, &text[anim.name], sizeof(loadmodel->animscenes[i].name));
+               loadmodel->animscenes[i].firstframe = anim.first_frame;
+               loadmodel->animscenes[i].framecount = anim.num_frames;
+               loadmodel->animscenes[i].loop = ((anim.flags & IQM_LOOP) != 0);
+               loadmodel->animscenes[i].framerate = anim.framerate;
+       }
+       if (header.num_anims <= 0)
+       {
+               strlcpy(loadmodel->animscenes[0].name, "static", sizeof(loadmodel->animscenes[0].name));
+               loadmodel->animscenes[0].firstframe = 0;
+               loadmodel->animscenes[0].framecount = 1;
+               loadmodel->animscenes[0].loop = true;
+               loadmodel->animscenes[0].framerate = 10;
+       }
+
+       loadmodel->surfmesh.isanimated = loadmodel->num_bones > 1 || loadmodel->numframes > 1 || (loadmodel->animscenes && loadmodel->animscenes[0].framecount > 1);
+       loadmodel->AnimateVertices = loadmodel->surfmesh.isanimated ? Mod_Skeletal_AnimateVertices : NULL;
+
+       biggestorigin = 0;
+       if (header.version == 1)
+       {
+               iqmpose1_t *inpose1 = (iqmpose1_t *)(pbase + header.ofs_poses);
+               if (header.num_poses)
+                       pose1 = (iqmpose1_t *)Mem_Alloc(loadmodel->mempool, header.num_poses * sizeof(iqmpose1_t));
+               for (i = 0;i < (int)header.num_poses;i++)
+               {
+                       float f;
+                       pose1[i].parent = LittleLong(inpose1[i].parent);
+                       pose1[i].channelmask = LittleLong(inpose1[i].channelmask);
+                       for (j = 0;j < 9;j++)
+                       {
+                               pose1[i].channeloffset[j] = LittleFloat(inpose1[i].channeloffset[j]);
+                               pose1[i].channelscale[j] = LittleFloat(inpose1[i].channelscale[j]);
+                       }
+                       f = fabs(pose1[i].channeloffset[0]); biggestorigin = max(biggestorigin, f);
+                       f = fabs(pose1[i].channeloffset[1]); biggestorigin = max(biggestorigin, f);
+                       f = fabs(pose1[i].channeloffset[2]); biggestorigin = max(biggestorigin, f);
+                       f = fabs(pose1[i].channeloffset[0] + 0xFFFF*pose1[i].channelscale[0]); biggestorigin = max(biggestorigin, f);
+                       f = fabs(pose1[i].channeloffset[1] + 0xFFFF*pose1[i].channelscale[1]); biggestorigin = max(biggestorigin, f);
+                       f = fabs(pose1[i].channeloffset[2] + 0xFFFF*pose1[i].channelscale[2]); biggestorigin = max(biggestorigin, f);
+               }
+               if (header.num_frames <= 0)
+               {
+                       for (i = 0;i < loadmodel->num_bones;i++)
+                       {
+                               float f;
+                               f = fabs(joint1[i].origin[0]); biggestorigin = max(biggestorigin, f);
+                               f = fabs(joint1[i].origin[1]); biggestorigin = max(biggestorigin, f);
+                               f = fabs(joint1[i].origin[2]); biggestorigin = max(biggestorigin, f);
+                       }
+               }
+       }
+       else
+       {
+               iqmpose_t *inpose = (iqmpose_t *)(pbase + header.ofs_poses);
+               if (header.num_poses)
+                       pose = (iqmpose_t *)Mem_Alloc(loadmodel->mempool, header.num_poses * sizeof(iqmpose_t));
+               for (i = 0;i < (int)header.num_poses;i++)
+               {
+                       float f;
+                       pose[i].parent = LittleLong(inpose[i].parent);
+                       pose[i].channelmask = LittleLong(inpose[i].channelmask);
+                       for (j = 0;j < 10;j++)
+                       {
+                               pose[i].channeloffset[j] = LittleFloat(inpose[i].channeloffset[j]);
+                               pose[i].channelscale[j] = LittleFloat(inpose[i].channelscale[j]);
+                       }
+                       f = fabs(pose[i].channeloffset[0]); biggestorigin = max(biggestorigin, f);
+                       f = fabs(pose[i].channeloffset[1]); biggestorigin = max(biggestorigin, f);
+                       f = fabs(pose[i].channeloffset[2]); biggestorigin = max(biggestorigin, f);
+                       f = fabs(pose[i].channeloffset[0] + 0xFFFF*pose[i].channelscale[0]); biggestorigin = max(biggestorigin, f);
+                       f = fabs(pose[i].channeloffset[1] + 0xFFFF*pose[i].channelscale[1]); biggestorigin = max(biggestorigin, f);
+                       f = fabs(pose[i].channeloffset[2] + 0xFFFF*pose[i].channelscale[2]); biggestorigin = max(biggestorigin, f);
+               }
+               if (header.num_frames <= 0)
+               {
+                       for (i = 0;i < loadmodel->num_bones;i++)
+                       {
+                               float f;
+                               f = fabs(joint[i].origin[0]); biggestorigin = max(biggestorigin, f);
+                               f = fabs(joint[i].origin[1]); biggestorigin = max(biggestorigin, f);
+                               f = fabs(joint[i].origin[2]); biggestorigin = max(biggestorigin, f);
+                       }
+               }
+       }
+       loadmodel->num_posescale = biggestorigin / 32767.0f;
+       loadmodel->num_poseinvscale = 1.0f / loadmodel->num_posescale;
+
+       // load the pose data
+       // this unaligned memory access is safe (LittleShort reads as bytes)
+       framedata = (const unsigned short *)(pbase + header.ofs_frames);
+       if (header.version == 1)
+       {
+               for (i = 0, k = 0;i < (int)header.num_frames;i++)
+               {
+                       for (j = 0;j < (int)header.num_poses;j++, k++)
+                       {
+                               loadmodel->data_poses6s[k*6 + 0] = loadmodel->num_poseinvscale * (pose1[j].channeloffset[0] + (pose1[j].channelmask&1 ? (unsigned short)LittleShort(*framedata++) * pose1[j].channelscale[0] : 0));
+                               loadmodel->data_poses6s[k*6 + 1] = loadmodel->num_poseinvscale * (pose1[j].channeloffset[1] + (pose1[j].channelmask&2 ? (unsigned short)LittleShort(*framedata++) * pose1[j].channelscale[1] : 0));
+                               loadmodel->data_poses6s[k*6 + 2] = loadmodel->num_poseinvscale * (pose1[j].channeloffset[2] + (pose1[j].channelmask&4 ? (unsigned short)LittleShort(*framedata++) * pose1[j].channelscale[2] : 0));
+                               loadmodel->data_poses6s[k*6 + 3] = 32767.0f * (pose1[j].channeloffset[3] + (pose1[j].channelmask&8 ? (unsigned short)LittleShort(*framedata++) * pose1[j].channelscale[3] : 0));
+                               loadmodel->data_poses6s[k*6 + 4] = 32767.0f * (pose1[j].channeloffset[4] + (pose1[j].channelmask&16 ? (unsigned short)LittleShort(*framedata++) * pose1[j].channelscale[4] : 0));
+                               loadmodel->data_poses6s[k*6 + 5] = 32767.0f * (pose1[j].channeloffset[5] + (pose1[j].channelmask&32 ? (unsigned short)LittleShort(*framedata++) * pose1[j].channelscale[5] : 0));
+                               // skip scale data for now
+                               if(pose1[j].channelmask&64) framedata++;
+                               if(pose1[j].channelmask&128) framedata++;
+                               if(pose1[j].channelmask&256) framedata++;
+                       }
+               }
+               if (header.num_frames <= 0)
+               {
+                       for (i = 0;i < loadmodel->num_bones;i++)
+                       {
+                               loadmodel->data_poses6s[i*6 + 0] = loadmodel->num_poseinvscale * joint1[i].origin[0];
+                               loadmodel->data_poses6s[i*6 + 1] = loadmodel->num_poseinvscale * joint1[i].origin[1];
+                               loadmodel->data_poses6s[i*6 + 2] = loadmodel->num_poseinvscale * joint1[i].origin[2];
+                               loadmodel->data_poses6s[i*6 + 3] = 32767.0f * joint1[i].rotation[0];
+                               loadmodel->data_poses6s[i*6 + 4] = 32767.0f * joint1[i].rotation[1];
+                               loadmodel->data_poses6s[i*6 + 5] = 32767.0f * joint1[i].rotation[2];
+                       }
+               }
+       }
+       else
+       {
+               for (i = 0, k = 0;i < (int)header.num_frames;i++)       
+               {
+                       for (j = 0;j < (int)header.num_poses;j++, k++)
+                       {
+                               float rot[4];
+                               loadmodel->data_poses6s[k*6 + 0] = loadmodel->num_poseinvscale * (pose[j].channeloffset[0] + (pose[j].channelmask&1 ? (unsigned short)LittleShort(*framedata++) * pose[j].channelscale[0] : 0));
+                               loadmodel->data_poses6s[k*6 + 1] = loadmodel->num_poseinvscale * (pose[j].channeloffset[1] + (pose[j].channelmask&2 ? (unsigned short)LittleShort(*framedata++) * pose[j].channelscale[1] : 0));
+                               loadmodel->data_poses6s[k*6 + 2] = loadmodel->num_poseinvscale * (pose[j].channeloffset[2] + (pose[j].channelmask&4 ? (unsigned short)LittleShort(*framedata++) * pose[j].channelscale[2] : 0));
+                               rot[0] = pose[j].channeloffset[3] + (pose[j].channelmask&8 ? (unsigned short)LittleShort(*framedata++) * pose[j].channelscale[3] : 0);
+                               rot[1] = pose[j].channeloffset[4] + (pose[j].channelmask&16 ? (unsigned short)LittleShort(*framedata++) * pose[j].channelscale[4] : 0);
+                               rot[2] = pose[j].channeloffset[5] + (pose[j].channelmask&32 ? (unsigned short)LittleShort(*framedata++) * pose[j].channelscale[5] : 0);
+                               rot[3] = pose[j].channeloffset[6] + (pose[j].channelmask&64 ? (unsigned short)LittleShort(*framedata++) * pose[j].channelscale[6] : 0);
+                               if (rot[3] > 0)
+                                       Vector4Negate(rot, rot);
+                               Vector4Normalize2(rot, rot);
+                               loadmodel->data_poses6s[k*6 + 3] = 32767.0f * rot[0];
+                               loadmodel->data_poses6s[k*6 + 4] = 32767.0f * rot[1];
+                               loadmodel->data_poses6s[k*6 + 5] = 32767.0f * rot[2];
+                               // skip scale data for now
+                               if(pose[j].channelmask&128) framedata++;
+                               if(pose[j].channelmask&256) framedata++;
+                               if(pose[j].channelmask&512) framedata++;
+                       }
+               }
+               if (header.num_frames <= 0)
+               {
+                       for (i = 0;i < loadmodel->num_bones;i++)
+                       {
+                               loadmodel->data_poses6s[i*6 + 0] = loadmodel->num_poseinvscale * joint[i].origin[0];
+                               loadmodel->data_poses6s[i*6 + 1] = loadmodel->num_poseinvscale * joint[i].origin[1];
+                               loadmodel->data_poses6s[i*6 + 2] = loadmodel->num_poseinvscale * joint[i].origin[2];
+                               loadmodel->data_poses6s[i*6 + 3] = 32767.0f * joint[i].rotation[0];
+                               loadmodel->data_poses6s[i*6 + 4] = 32767.0f * joint[i].rotation[1];
+                               loadmodel->data_poses6s[i*6 + 5] = 32767.0f * joint[i].rotation[2];
+                       }
+               }
+       }
+
+       // load bounding box data
+       if (header.ofs_bounds)
+       {
+               float xyradius = 0, radius = 0;
+               VectorClear(loadmodel->normalmins);
+               VectorClear(loadmodel->normalmaxs);
+               for (i = 0; i < (int)header.num_frames;i++)
+               {
+                       iqmbounds_t bound;
+                       bound.mins[0] = LittleFloat(bounds[i].mins[0]);
+                       bound.mins[1] = LittleFloat(bounds[i].mins[1]);
+                       bound.mins[2] = LittleFloat(bounds[i].mins[2]);
+                       bound.maxs[0] = LittleFloat(bounds[i].maxs[0]);                 
+                       bound.maxs[1] = LittleFloat(bounds[i].maxs[1]); 
+                       bound.maxs[2] = LittleFloat(bounds[i].maxs[2]); 
+                       bound.xyradius = LittleFloat(bounds[i].xyradius);
+                       bound.radius = LittleFloat(bounds[i].radius);
+                       if (!i)
+                       {
+                               VectorCopy(bound.mins, loadmodel->normalmins);
+                               VectorCopy(bound.maxs, loadmodel->normalmaxs);
+                       }
+                       else
+                       {
+                               if (loadmodel->normalmins[0] > bound.mins[0]) loadmodel->normalmins[0] = bound.mins[0];
+                               if (loadmodel->normalmins[1] > bound.mins[1]) loadmodel->normalmins[1] = bound.mins[1];
+                               if (loadmodel->normalmins[2] > bound.mins[2]) loadmodel->normalmins[2] = bound.mins[2];
+                               if (loadmodel->normalmaxs[0] < bound.maxs[0]) loadmodel->normalmaxs[0] = bound.maxs[0];
+                               if (loadmodel->normalmaxs[1] < bound.maxs[1]) loadmodel->normalmaxs[1] = bound.maxs[1];
+                               if (loadmodel->normalmaxs[2] < bound.maxs[2]) loadmodel->normalmaxs[2] = bound.maxs[2];
+                       }
+                       if (bound.xyradius > xyradius)
+                               xyradius = bound.xyradius;
+                       if (bound.radius > radius)
+                               radius = bound.radius;
+               }
+               loadmodel->yawmins[0] = loadmodel->yawmins[1] = -xyradius;
+               loadmodel->yawmaxs[0] = loadmodel->yawmaxs[1] = xyradius;
+               loadmodel->yawmins[2] = loadmodel->normalmins[2];
+               loadmodel->yawmaxs[2] = loadmodel->normalmaxs[2];
+               loadmodel->rotatedmins[0] = loadmodel->rotatedmins[1] = loadmodel->rotatedmins[2] = -radius;
+               loadmodel->rotatedmaxs[0] = loadmodel->rotatedmaxs[1] = loadmodel->rotatedmaxs[2] = radius;
+               loadmodel->radius = radius;
+               loadmodel->radius2 = radius * radius;
+       }
+
+       // load triangle data
+       // this unaligned memory access is safe (LittleLong reads as bytes)
+       inelements = (const unsigned int *)(pbase + header.ofs_triangles);
+       outelements = loadmodel->surfmesh.data_element3i;
+       for (i = 0;i < (int)header.num_triangles;i++)
+       {
+               outelements[0] = LittleLong(inelements[0]);
+               outelements[1] = LittleLong(inelements[1]);
+               outelements[2] = LittleLong(inelements[2]);
+               outelements += 3;
+               inelements += 3;
+       }
+       Mod_ValidateElements(loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles, 0, header.num_vertexes, __FILE__, __LINE__);
+
+       if (header.ofs_neighbors && loadmodel->surfmesh.data_neighbor3i)
+       {
+               // this unaligned memory access is safe (LittleLong reads as bytes)
+               inneighbors = (const int *)(pbase + header.ofs_neighbors);
+               outneighbors = loadmodel->surfmesh.data_neighbor3i;
+               for (i = 0;i < (int)header.num_triangles;i++)
+               {
+                       outneighbors[0] = LittleLong(inneighbors[0]);
+                       outneighbors[1] = LittleLong(inneighbors[1]);
+                       outneighbors[2] = LittleLong(inneighbors[2]);
+                       outneighbors += 3;
+                       inneighbors += 3;
+               }
+       }
+
+       // load vertex data
+       // this unaligned memory access is safe (LittleFloat reads as bytes)
+       outvertex = loadmodel->surfmesh.data_vertex3f;
+       for (i = 0;i < (int)header.num_vertexes;i++)
+       {
+               outvertex[0] = LittleFloat(vposition[0]);
+               outvertex[1] = LittleFloat(vposition[1]);
+               outvertex[2] = LittleFloat(vposition[2]);
+               vposition += 3;
+               outvertex += 3;
+       }
+
+       outtexcoord = loadmodel->surfmesh.data_texcoordtexture2f;
+       // this unaligned memory access is safe (LittleFloat reads as bytes)
+       for (i = 0;i < (int)header.num_vertexes;i++)
+       {
+               outtexcoord[0] = LittleFloat(vtexcoord[0]);
+               outtexcoord[1] = LittleFloat(vtexcoord[1]);
+               vtexcoord += 2;
+               outtexcoord += 2;
+       }
+
+       // this unaligned memory access is safe (LittleFloat reads as bytes)
+       if(vnormal)
+       {
+               outnormal = loadmodel->surfmesh.data_normal3f;
+               for (i = 0;i < (int)header.num_vertexes;i++)
+               {
+                       outnormal[0] = LittleFloat(vnormal[0]);
+                       outnormal[1] = LittleFloat(vnormal[1]);
+                       outnormal[2] = LittleFloat(vnormal[2]);
+                       vnormal += 3;
+                       outnormal += 3;
+               }
+       }
+
+       // this unaligned memory access is safe (LittleFloat reads as bytes)
+       if(vnormal && vtangent)
+       {
+               outnormal = loadmodel->surfmesh.data_normal3f;
+               outsvector = loadmodel->surfmesh.data_svector3f;
+               outtvector = loadmodel->surfmesh.data_tvector3f;
+               for (i = 0;i < (int)header.num_vertexes;i++)
+               {
+                       outsvector[0] = LittleFloat(vtangent[0]);
+                       outsvector[1] = LittleFloat(vtangent[1]);
+                       outsvector[2] = LittleFloat(vtangent[2]);
+                       if(LittleFloat(vtangent[3]) < 0)
+                               CrossProduct(outsvector, outnormal, outtvector);
+                       else
+                               CrossProduct(outnormal, outsvector, outtvector);
+                       vtangent += 4;
+                       outnormal += 3;
+                       outsvector += 3;
+                       outtvector += 3;
+               }
+       }
+
+       // this unaligned memory access is safe (all bytes)
+       if (vblendindexes && vblendweights)
+       {
+               for (i = 0; i < (int)header.num_vertexes;i++)
+               {
+                       blendweights_t weights;
+                       memcpy(weights.index, vblendindexes + i*4, 4);
+                       memcpy(weights.influence, vblendweights + i*4, 4);
+                       loadmodel->surfmesh.blends[i] = Mod_Skeletal_AddBlend(loadmodel, &weights);
+               }
+       }
+
+       if (vcolor4f)
+       {
+               outcolor = loadmodel->surfmesh.data_lightmapcolor4f;
+               // this unaligned memory access is safe (LittleFloat reads as bytes)
+               for (i = 0;i < (int)header.num_vertexes;i++)
+               {
+                       outcolor[0] = LittleFloat(vcolor4f[0]);
+                       outcolor[1] = LittleFloat(vcolor4f[1]);
+                       outcolor[2] = LittleFloat(vcolor4f[2]);
+                       outcolor[3] = LittleFloat(vcolor4f[3]);
+                       vcolor4f += 4;
+                       outcolor += 4;
+               }
+       }
+       else if (vcolor4ub)
+       {
+               outcolor = loadmodel->surfmesh.data_lightmapcolor4f;
+               // this unaligned memory access is safe (all bytes)
+               for (i = 0;i < (int)header.num_vertexes;i++)
+               {
+                       outcolor[0] = vcolor4ub[0] * (1.0f / 255.0f);
+                       outcolor[1] = vcolor4ub[1] * (1.0f / 255.0f);
+                       outcolor[2] = vcolor4ub[2] * (1.0f / 255.0f);
+                       outcolor[3] = vcolor4ub[3] * (1.0f / 255.0f);
+                       vcolor4ub += 4;
+                       outcolor += 4;
+               }
+       }
+
+       // load meshes
+       for (i = 0;i < (int)header.num_meshes;i++)
+       {
+               iqmmesh_t mesh;
+               msurface_t *surface;
+
+               mesh.name = LittleLong(meshes[i].name);
+               mesh.material = LittleLong(meshes[i].material);
+               mesh.first_vertex = LittleLong(meshes[i].first_vertex);
+               mesh.num_vertexes = LittleLong(meshes[i].num_vertexes);
+               mesh.first_triangle = LittleLong(meshes[i].first_triangle);
+               mesh.num_triangles = LittleLong(meshes[i].num_triangles);
+
+               loadmodel->sortedmodelsurfaces[i] = i;
+               surface = loadmodel->data_surfaces + i;
+               surface->texture = loadmodel->data_textures + i;
+               surface->num_firsttriangle = mesh.first_triangle;
+               surface->num_triangles = mesh.num_triangles;
+               surface->num_firstvertex = mesh.first_vertex;
+               surface->num_vertices = mesh.num_vertexes;
+
+               Mod_BuildAliasSkinsFromSkinFiles(loadmodel->data_textures + i, skinfiles, &text[mesh.name], &text[mesh.material]);
+       }
+
+       Mod_FreeSkinFiles(skinfiles);
+       Mod_MakeSortedSurfaces(loadmodel);
+
+       // compute all the mesh information that was not loaded from the file
+       if (loadmodel->surfmesh.data_element3s)
+               for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
+                       loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
+       if (!vnormal)
+               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);
+       if (!vnormal || !vtangent)
+               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);
+       if (!header.ofs_neighbors && loadmodel->surfmesh.data_neighbor3i)
+               Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
+       if (!header.ofs_bounds)
+               Mod_Alias_CalculateBoundingBox();
+
+       if (!loadmodel->surfmesh.isanimated && loadmodel->surfmesh.num_triangles >= 1)
+       {
+               Mod_MakeCollisionBIH(loadmodel, true, &loadmodel->collision_bih);
+               loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
+               loadmodel->TraceBrush = Mod_CollisionBIH_TraceBrush;
+               loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
+               loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
+               loadmodel->PointSuperContents = Mod_CollisionBIH_PointSuperContents_Mesh;
+       }
+
+       if (joint        ) Mem_Free(joint        );joint         = NULL;
+       if (joint1       ) Mem_Free(joint1       );joint1        = NULL;
+       if (pose         ) Mem_Free(pose         );pose          = NULL;
+       if (pose1        ) Mem_Free(pose1        );pose1         = NULL;
+
+       // because shaders can do somewhat unexpected things, check for unusual features now
+       for (i = 0;i < loadmodel->num_textures;i++)
+       {
+               if (loadmodel->data_textures[i].basematerialflags & (MATERIALFLAG_SKY))
+                       mod->DrawSky = R_Q1BSP_DrawSky;
+               if (loadmodel->data_textures[i].basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
+                       mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
+       }
+}