]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - model_alias.c
add bih.[ch] to MSVC projects
[xonotic/darkplaces.git] / model_alias.c
index 4cbc06f9c83a5827ca866dca5fdf7f236dee2383..9fd4d1cce97d4d622a7f580ee185f26c18099edc 100644 (file)
@@ -28,6 +28,7 @@ cvar_t r_skeletal_debugbonevalue = {0, "r_skeletal_debugbonevalue", "100", "deve
 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];
 
@@ -40,31 +41,151 @@ void Mod_AliasInit (void)
        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);
 }
 
-void Mod_Alias_GetMesh_Vertices(const model_t *model, const frameblend_t *frameblend, float *vertex3f, float *normal3f, float *svector3f, float *tvector3f)
+int Mod_Skeletal_AddBlend(dp_model_t *model, const int *newindex, const float *newinfluence)
 {
-#define MAX_BONES 256
-       if (model->surfmesh.data_vertexweightindex4i)
+       int i, total;
+       float scale;
+       blendweights_t *weights;
+       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(newweights.influence[i] > 0 && total > 255) 
+                       { 
+                               newweights.influence[i]--;
+                               total--; 
+                       }
+               }
+       }
+       while (total < 255)
+       {
+               for (i = 0; i < 4;i++)
+               {
+                       if(newweights.influence[i] < 255 && total < 255) 
+                       { 
+                               newweights.influence[i]++; 
+                               total++; 
+                       }
+               }
+       }
+       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;
+} 
+                
+static int maxbonepose = 0;
+static float (*bonepose)[12] = NULL;
+
+void Mod_Skeletal_FreeBuffers(void)
+{
+       if(bonepose)
+               Mem_Free(bonepose);
+       maxbonepose = 0;
+       bonepose = NULL;
+}
+
+#if defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1400)
+#define RESTRICT __restrict
+#else
+#define RESTRICT
+#endif
+
+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)
+{
+       // vertex weighted skeletal
+       int i, k;
+       int blends;
+       float m[12];
+       float (*boneposerelative)[12];
+       const blendweights_t * RESTRICT weights;
+
+       if (maxbonepose < model->num_bones*2 + model->surfmesh.num_blends)
+       {
+               if (bonepose)
+                       Mem_Free(bonepose);
+               maxbonepose = model->num_bones*2 + model->surfmesh.num_blends;
+               bonepose = (float (*)[12])Mem_Alloc(r_main_mempool, maxbonepose * sizeof(float[12]));
+       }
+
+       boneposerelative = bonepose + model->num_bones;
+
+       if (skeleton && !skeleton->relativetransforms)
+               skeleton = NULL;
+
+       // interpolate matrices
+       if (skeleton)
        {
-               // vertex weighted skeletal
-               int i, k;
-               float boneposerelative[MAX_BONES][12];
-               // interpolate matrices and concatenate them to their parents
                for (i = 0;i < model->num_bones;i++)
                {
-                       int blends;
-                       float *matrix, m[12], bonepose[MAX_BONES][12];
-                       for (k = 0;k < 12;k++)
-                               m[k] = 0;
-                       for (blends = 0;blends < 4 && frameblend[blends].lerp > 0;blends++)
+                       Matrix4x4_ToArray12FloatD3D(&skeleton->relativetransforms[i], m);
+                       if (model->data_bones[i].parent >= 0)
+                               R_ConcatTransforms(bonepose[model->data_bones[i].parent], m, bonepose[i]);
+                       else
+                               memcpy(bonepose[i], m, sizeof(m));
+
+                       // 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]);
+               }
+       }
+       else
+       {
+               float originscale = model->num_posescale;
+               float x,y,z,w,lerp;
+               const short * RESTRICT pose6s;
+               for (i = 0;i < model->num_bones;i++)
+               {
+                       memset(m, 0, sizeof(m));
+                       for (blends = 0;blends < MAX_FRAMEBLENDS && 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;
+                               pose6s = model->data_poses6s + 6 * (frameblend[blends].subframe * model->num_bones + i);
+                               lerp = frameblend[blends].lerp;
+                               x = pose6s[3] * (1.0f / 32767.0f);
+                               y = pose6s[4] * (1.0f / 32767.0f);
+                               z = pose6s[5] * (1.0f / 32767.0f);
+                               w = 1.0f - (x*x+y*y+z*z);
+                               w = w > 0.0f ? -sqrt(w) : 0.0f;
+                               m[ 0] += (1-2*(y*y+z*z)) * lerp;
+                               m[ 1] += (  2*(x*y-z*w)) * lerp;
+                               m[ 2] += (  2*(x*z+y*w)) * lerp;
+                               m[ 3] += (pose6s[0] * originscale) * lerp;
+                               m[ 4] += (  2*(x*y+z*w)) * lerp;
+                               m[ 5] += (1-2*(x*x+z*z)) * lerp;
+                               m[ 6] += (  2*(y*z-x*w)) * lerp;
+                               m[ 7] += (pose6s[1] * originscale) * lerp;
+                               m[ 8] += (  2*(x*z-y*w)) * lerp;
+                               m[ 9] += (  2*(y*z+x*w)) * lerp;
+                               m[10] += (1-2*(x*x+y*y)) * lerp;
+                               m[11] += (pose6s[2] * originscale) * lerp;
                        }
+                       VectorNormalize(m       );
+                       VectorNormalize(m + 4);
+                       VectorNormalize(m + 8);
                        if (i == r_skeletal_debugbone.integer)
                                m[r_skeletal_debugbonecomponent.integer % 12] += r_skeletal_debugbonevalue.value;
                        m[3] *= r_skeletal_debugtranslatex.value;
@@ -73,163 +194,144 @@ void Mod_Alias_GetMesh_Vertices(const model_t *model, const frameblend_t *frameb
                        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];
+                               memcpy(bonepose[i], m, sizeof(m));
                        // 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
+       }
+       
+       // generate matrices for all blend combinations
+       weights = model->surfmesh.data_blendweights;
+       for (i = 0;i < model->surfmesh.num_blends;i++, weights++)
+       {
+               float * RESTRICT b = boneposerelative[model->num_bones + i];
+               const float * RESTRICT m = boneposerelative[weights->index[0]];
+               float f = weights->influence[0] * (1.0f / 255.0f);
+               b[ 0] = f*m[ 0]; b[ 1] = f*m[ 1]; b[ 2] = f*m[ 2]; b[ 3] = f*m[ 3];
+               b[ 4] = f*m[ 4]; b[ 5] = f*m[ 5]; b[ 6] = f*m[ 6]; b[ 7] = f*m[ 7];
+               b[ 8] = f*m[ 8]; b[ 9] = f*m[ 9]; b[10] = f*m[10]; b[11] = f*m[11];
+               for (k = 1;k < 4 && weights->influence[k];k++)
                {
-                       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]);
-                                       }
-                               }
-                       }
+                       m = boneposerelative[weights->index[k]];
+                       f = weights->influence[k] * (1.0f / 255.0f);
+                       b[ 0] += f*m[ 0]; b[ 1] += f*m[ 1]; b[ 2] += f*m[ 2]; b[ 3] += f*m[ 3];
+                       b[ 4] += f*m[ 4]; b[ 5] += f*m[ 5]; b[ 6] += f*m[ 6]; b[ 7] += f*m[ 7];
+                       b[ 8] += f*m[ 8]; b[ 9] += f*m[ 9]; b[10] += f*m[10]; b[11] += f*m[11];
                }
+       }
+
+       // transform vertex attributes by blended matrices
+       if (vertex3f)
+       {
+               const float * RESTRICT v = model->surfmesh.data_vertex3f;
+               const unsigned short * RESTRICT b = model->surfmesh.blends;
+               // special case common combinations of attributes to avoid repeated loading of matrices
                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)
+                       const float * RESTRICT n = model->surfmesh.data_normal3f;
+                       if (svector3f && tvector3f)
                        {
-                               if (wf[0] == 1)
+                               const float * RESTRICT sv = model->surfmesh.data_svector3f;
+                               const float * RESTRICT tv = model->surfmesh.data_tvector3f;
+                               for (i = 0;i < model->surfmesh.num_vertices;i++, v += 3, n += 3, sv += 3, tv += 3, b++, vertex3f += 3, normal3f += 3, svector3f += 3, tvector3f += 3)
                                {
-                                       const float *m = boneposerelative[wi[0]];
+                                       const float * RESTRICT m = boneposerelative[*b];
+                                       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]);
                                        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 (svector3f)
-               {
-                       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)
-                       {
-                               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]);
+                                       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];
-                                       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]);
-                                       }
-                               }
+                               return;
+                       }
+                       for (i = 0;i < model->surfmesh.num_vertices;i++, v += 3, n += 3, b++, vertex3f += 3, normal3f += 3)
+                       {
+                               const float * RESTRICT m = boneposerelative[*b];
+                               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]);
+                               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]);
                        }
                }
-               if (tvector3f)
+               else
                {
-                       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)
+                       for (i = 0;i < model->surfmesh.num_vertices;i++, v += 3, b++, vertex3f += 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]);
-                                       }
-                               }
+                               const float * RESTRICT m = boneposerelative[*b];
+                               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 if (model->surfmesh.data_morphmd3vertex)
+       else if (normal3f)
        {
-               // vertex morph
-               int i, numblends, blendnum;
-               int numverts = model->surfmesh.num_vertices;
-               numblends = 0;
-               for (blendnum = 0;blendnum < 4;blendnum++)
+               const float * RESTRICT n = model->surfmesh.data_normal3f;
+               const unsigned short * RESTRICT b = model->surfmesh.blends;
+               for (i = 0;i < model->surfmesh.num_vertices;i++, n += 3, b++, normal3f += 3)
                {
-                       //VectorMA(translate, model->surfmesh.num_morphmdlframetranslate, frameblend[blendnum].lerp, translate);
-                       if (frameblend[blendnum].lerp > 0)
-                               numblends = blendnum + 1;
+                       const float * RESTRICT m = boneposerelative[*b];
+                       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]);
                }
-               // 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++)
+       }
+
+       if (svector3f)
+       {
+               const float * RESTRICT sv = model->surfmesh.data_svector3f;
+               const unsigned short * RESTRICT b = model->surfmesh.blends;
+               for (i = 0;i < model->surfmesh.num_vertices;i++, sv += 3, b++, svector3f += 3)
+               {
+                       const float * RESTRICT m = boneposerelative[*b];
+                       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]);
+               }
+       }
+
+       if (tvector3f)
+       {
+               const float * RESTRICT tv = model->surfmesh.data_tvector3f;
+               const unsigned short * RESTRICT b = model->surfmesh.blends;
+               for (i = 0;i < model->surfmesh.num_vertices;i++, tv += 3, b++, tvector3f += 3)
+               {
+                       const float * RESTRICT m = boneposerelative[*b];
+                       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]);
+               }
+       }
+}
+
+void Mod_MD3_AnimateVertices(const dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, float *vertex3f, float *normal3f, float *svector3f, float *tvector3f)
+{
+       // vertex morph
+       int i, numblends, blendnum;
+       int numverts = model->surfmesh.num_vertices;
+       numblends = 0;
+       for (blendnum = 0;blendnum < MAX_FRAMEBLENDS;blendnum++)
+       {
+               //VectorMA(translate, model->surfmesh.num_morphmdlframetranslate, frameblend[blendnum].lerp, translate);
+               if (frameblend[blendnum].lerp > 0)
+                       numblends = blendnum + 1;
+       }
+       // 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].subframe;
+               if (vertex3f)
                {
-                       const md3vertex_t *verts = model->surfmesh.data_morphmd3vertex + numverts * frameblend[blendnum].frame;
                        float scale = frameblend[blendnum].lerp * (1.0f / 64.0f);
                        if (blendnum == 0)
                        {
@@ -249,82 +351,86 @@ void Mod_Alias_GetMesh_Vertices(const model_t *model, const frameblend_t *frameb
                                        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
-                       // decoding them, and since cosine is equivilant 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)
+               }
+               // 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
+               // extra 45 degree rotation, this uses one lookup table for both
+               // sine and cosine with a +64 bias to get cosine.
+               if (normal3f)
+               {
+                       float lerp = frameblend[blendnum].lerp;
+                       if (blendnum == 0)
                        {
-                               float lerp = frameblend[blendnum].lerp;
-                               if (blendnum == 0)
+                               for (i = 0;i < numverts;i++)
                                {
-                                       for (i = 0;i < numverts;i++)
-                                       {
-                                               normal3f[i * 3 + 0] = mod_md3_sin[verts[i].yaw + 64] * mod_md3_sin[verts[i].pitch     ] * lerp;
-                                               normal3f[i * 3 + 1] = mod_md3_sin[verts[i].yaw     ] * mod_md3_sin[verts[i].pitch     ] * lerp;
-                                               normal3f[i * 3 + 2] =                                  mod_md3_sin[verts[i].pitch + 64] * lerp;
-                                       }
+                                       normal3f[i * 3 + 0] = mod_md3_sin[verts[i].yaw + 64] * mod_md3_sin[verts[i].pitch     ] * lerp;
+                                       normal3f[i * 3 + 1] = mod_md3_sin[verts[i].yaw     ] * mod_md3_sin[verts[i].pitch     ] * lerp;
+                                       normal3f[i * 3 + 2] =                                  mod_md3_sin[verts[i].pitch + 64] * lerp;
                                }
-                               else
+                       }
+                       else
+                       {
+                               for (i = 0;i < numverts;i++)
                                {
-                                       for (i = 0;i < numverts;i++)
-                                       {
-                                               normal3f[i * 3 + 0] += mod_md3_sin[verts[i].yaw + 64] * mod_md3_sin[verts[i].pitch     ] * lerp;
-                                               normal3f[i * 3 + 1] += mod_md3_sin[verts[i].yaw     ] * mod_md3_sin[verts[i].pitch     ] * lerp;
-                                               normal3f[i * 3 + 2] +=                                  mod_md3_sin[verts[i].pitch + 64] * lerp;
-                                       }
+                                       normal3f[i * 3 + 0] += mod_md3_sin[verts[i].yaw + 64] * mod_md3_sin[verts[i].pitch     ] * lerp;
+                                       normal3f[i * 3 + 1] += mod_md3_sin[verts[i].yaw     ] * mod_md3_sin[verts[i].pitch     ] * lerp;
+                                       normal3f[i * 3 + 2] +=                                  mod_md3_sin[verts[i].pitch + 64] * lerp;
                                }
                        }
-                       if (svector3f)
+               }
+               if (svector3f)
+               {
+                       const texvecvertex_t *texvecvert = model->surfmesh.data_morphtexvecvertex + numverts * frameblend[blendnum].subframe;
+                       float f = frameblend[blendnum].lerp * (1.0f / 127.0f);
+                       if (blendnum == 0)
                        {
-                               const texvecvertex_t *texvecvert = model->surfmesh.data_morphtexvecvertex + numverts * frameblend[blendnum].frame;
-                               float f = frameblend[blendnum].lerp * (1.0f / 127.0f);
-                               if (blendnum == 0)
+                               for (i = 0;i < numverts;i++, texvecvert++)
                                {
-                                       for (i = 0;i < numverts;i++, texvecvert++)
-                                       {
-                                               VectorScale(texvecvert->svec, f, svector3f + i*3);
-                                               VectorScale(texvecvert->tvec, f, tvector3f + i*3);
-                                       }
+                                       VectorScale(texvecvert->svec, f, svector3f + i*3);
+                                       VectorScale(texvecvert->tvec, f, tvector3f + i*3);
                                }
-                               else
+                       }
+                       else
+                       {
+                               for (i = 0;i < numverts;i++, texvecvert++)
                                {
-                                       for (i = 0;i < numverts;i++, texvecvert++)
-                                       {
-                                               VectorMA(svector3f + i*3, f, texvecvert->svec, svector3f + i*3);
-                                               VectorMA(tvector3f + i*3, f, texvecvert->tvec, tvector3f + i*3);
-                                       }
+                                       VectorMA(svector3f + i*3, f, texvecvert->svec, svector3f + i*3);
+                                       VectorMA(tvector3f + i*3, f, texvecvert->tvec, tvector3f + i*3);
                                }
                        }
                }
        }
-       else if (model->surfmesh.data_morphmdlvertex)
-       {
-               // vertex morph
-               int i, numblends, blendnum;
-               int numverts = model->surfmesh.num_vertices;
-               float translate[3];
-               VectorClear(translate);
-               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++)
-               {
-                       if (model->surfmesh.data_morphmd2framesize6f)
-                               VectorMA(translate, frameblend[blendnum].lerp, model->surfmesh.data_morphmd2framesize6f + frameblend[blendnum].frame * 6 + 3, translate);
-                       else
-                               VectorMA(translate, frameblend[blendnum].lerp, model->surfmesh.num_morphmdlframetranslate, translate);
-                       if (frameblend[blendnum].lerp > 0)
-                               numblends = blendnum + 1;
-               }
-               // 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++)
+}
+
+void Mod_MDL_AnimateVertices(const dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, float *vertex3f, float *normal3f, float *svector3f, float *tvector3f)
+{
+       // vertex morph
+       int i, numblends, blendnum;
+       int numverts = model->surfmesh.num_vertices;
+       float translate[3];
+       VectorClear(translate);
+       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 < MAX_FRAMEBLENDS;blendnum++)
+       {
+               if (model->surfmesh.data_morphmd2framesize6f)
+                       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)
+                       numblends = blendnum + 1;
+       }
+       // 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].subframe;
+               if (vertex3f)
                {
-                       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);
+                               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)
@@ -345,98 +451,184 @@ void Mod_Alias_GetMesh_Vertices(const model_t *model, const frameblend_t *frameb
                                        vertex3f[i * 3 + 2] += verts[i].v[2] * scale[2];
                                }
                        }
-                       // the vertex normals in mdl models are an index into a table of
-                       // 162 unique values, this very crude quantization reduces the
-                       // vertex normal to only one byte, which saves a lot of space but
-                       // also makes lighting pretty coarse
-                       if (normal3f)
+               }
+               // the vertex normals in mdl models are an index into a table of
+               // 162 unique values, this very crude quantization reduces the
+               // vertex normal to only one byte, which saves a lot of space but
+               // also makes lighting pretty coarse
+               if (normal3f)
+               {
+                       float lerp = frameblend[blendnum].lerp;
+                       if (blendnum == 0)
                        {
-                               float lerp = frameblend[blendnum].lerp;
-                               if (blendnum == 0)
+                               for (i = 0;i < numverts;i++)
                                {
-                                       for (i = 0;i < numverts;i++)
-                                       {
-                                               const float *vn = m_bytenormals[verts[i].lightnormalindex];
-                                               VectorScale(vn, lerp, normal3f + i*3);
-                                       }
+                                       const float *vn = m_bytenormals[verts[i].lightnormalindex];
+                                       VectorScale(vn, lerp, normal3f + i*3);
                                }
-                               else
+                       }
+                       else
+                       {
+                               for (i = 0;i < numverts;i++)
                                {
-                                       for (i = 0;i < numverts;i++)
-                                       {
-                                               const float *vn = m_bytenormals[verts[i].lightnormalindex];
-                                               VectorMA(normal3f + i*3, lerp, vn, normal3f + i*3);
-                                       }
+                                       const float *vn = m_bytenormals[verts[i].lightnormalindex];
+                                       VectorMA(normal3f + i*3, lerp, vn, normal3f + i*3);
                                }
                        }
-                       if (svector3f)
+               }
+               if (svector3f)
+               {
+                       const texvecvertex_t *texvecvert = model->surfmesh.data_morphtexvecvertex + numverts * frameblend[blendnum].subframe;
+                       float f = frameblend[blendnum].lerp * (1.0f / 127.0f);
+                       if (blendnum == 0)
                        {
-                               const texvecvertex_t *texvecvert = model->surfmesh.data_morphtexvecvertex + numverts * frameblend[blendnum].frame;
-                               float f = frameblend[blendnum].lerp * (1.0f / 127.0f);
-                               if (blendnum == 0)
+                               for (i = 0;i < numverts;i++, texvecvert++)
                                {
-                                       for (i = 0;i < numverts;i++, texvecvert++)
-                                       {
-                                               VectorScale(texvecvert->svec, f, svector3f + i*3);
-                                               VectorScale(texvecvert->tvec, f, tvector3f + i*3);
-                                       }
+                                       VectorScale(texvecvert->svec, f, svector3f + i*3);
+                                       VectorScale(texvecvert->tvec, f, tvector3f + i*3);
                                }
-                               else
+                       }
+                       else
+                       {
+                               for (i = 0;i < numverts;i++, texvecvert++)
                                {
-                                       for (i = 0;i < numverts;i++, texvecvert++)
-                                       {
-                                               VectorMA(svector3f + i*3, f, texvecvert->svec, svector3f + i*3);
-                                               VectorMA(tvector3f + i*3, f, texvecvert->tvec, tvector3f + i*3);
-                                       }
+                                       VectorMA(svector3f + i*3, f, texvecvert->svec, svector3f + i*3);
+                                       VectorMA(tvector3f + i*3, f, texvecvert->tvec, tvector3f + i*3);
                                }
                        }
                }
        }
-       else
-               Host_Error("model %s has no skeletal or vertex morph animation data", model->name);
 }
 
-int Mod_Alias_GetTagMatrix(const 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;
-       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 (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;
-               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;
 }
 
-int Mod_Alias_GetTagIndexForName(const model_t *model, unsigned int skin, const char *tagname)
+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;
        if(skin >= (unsigned int)model->numskins)
                skin = 0;
-       if (model->data_overridetagnamesforskin && skin < (unsigned int)model->numskins && model->data_overridetagnamesforskin[(unsigned int)skin].num_overridetagnames)
-               for (i = 0;i < model->data_overridetagnamesforskin[skin].num_overridetagnames;i++)
-                       if (!strcasecmp(tagname, model->data_overridetagnamesforskin[skin].data_overridetagnames[i].name))
-                               return i + 1;
        if (model->num_bones)
                for (i = 0;i < model->num_bones;i++)
                        if (!strcasecmp(tagname, model->data_bones[i].name))
@@ -450,96 +642,74 @@ int Mod_Alias_GetTagIndexForName(const model_t *model, unsigned int skin, const
 
 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);
 }
 
 static void Mod_Alias_CalculateBoundingBox(void)
 {
-       int i, j;
        int vnum;
        qboolean firstvertex = true;
        float dist, yawradius, radius;
        float *v;
        float *vertex3f;
-       frameblend_t frameblend[4];
+       frameblend_t frameblend[MAX_FRAMEBLENDS];
        memset(frameblend, 0, sizeof(frameblend));
        frameblend[0].lerp = 1;
-       vertex3f = Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_vertices * sizeof(float[3]));
+       vertex3f = (float *) Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_vertices * sizeof(float[3]));
        VectorClear(loadmodel->normalmins);
        VectorClear(loadmodel->normalmaxs);
        yawradius = 0;
        radius = 0;
-       for (i = 0;i < loadmodel->numframes;i++)
+       for (frameblend[0].subframe = 0;frameblend[0].subframe < loadmodel->num_poses;frameblend[0].subframe++)
        {
-               for (j = 0, frameblend[0].frame = loadmodel->animscenes[i].firstframe;j < loadmodel->animscenes[i].framecount;j++, frameblend[0].frame++)
+               loadmodel->AnimateVertices(loadmodel, frameblend, NULL, vertex3f, NULL, NULL, NULL);
+               for (vnum = 0, v = vertex3f;vnum < loadmodel->surfmesh.num_vertices;vnum++, v += 3)
                {
-                       Mod_Alias_GetMesh_Vertices(loadmodel, frameblend, vertex3f, NULL, NULL, NULL);
-                       for (vnum = 0, v = vertex3f;vnum < loadmodel->surfmesh.num_vertices;vnum++, v += 3)
+                       if (firstvertex)
                        {
-                               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;
+                               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;
                }
        }
-       Mem_Free(vertex3f);
+       if (vertex3f)
+               Mem_Free(vertex3f);
        radius = sqrt(radius);
        yawradius = sqrt(yawradius);
        loadmodel->yawmins[0] = loadmodel->yawmins[1] = -yawradius;
@@ -555,8 +725,10 @@ static void Mod_Alias_CalculateBoundingBox(void)
 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;
+       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]);
@@ -566,23 +738,22 @@ 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--)
        {
-               frameblend[0].frame = i;
-               Mod_Alias_GetMesh_Vertices(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++)
                {
-                       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(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];
-       frameblend_t frameblend[4];
        msurface_t *surface;
        static int maxvertices = 0;
        static float *vertex3f = NULL;
@@ -590,9 +761,6 @@ static void Mod_MDLMD2MD3_TraceBox(model_t *model, int frame, trace_t *trace, co
        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)
@@ -600,51 +768,72 @@ static void Mod_MDLMD2MD3_TraceBox(model_t *model, int frame, trace_t *trace, co
                maxvertices = (model->surfmesh.num_vertices + 255) & ~255;
                vertex3f = (float *)Z_Malloc(maxvertices * sizeof(float[3]));
        }
-       if (VectorLength2(boxmins) + VectorLength2(boxmaxs) == 0)
+       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);
+}
+
+static int maxvertices = 0;
+static float *vertex3f = NULL;
+
+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;
+       colboxbrushf_t thisbrush_start, thisbrush_end;
+       vec3_t boxstartmins, boxstartmaxs, boxendmins, boxendmaxs;
+
+       if (VectorCompare(boxmins, boxmaxs))
        {
-               // 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++)
-               {
-                       Mod_Alias_GetMesh_Vertices(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);
-               }
+               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;
        }
-       else
+
+       // box trace, performed as brush trace
+       memset(trace, 0, sizeof(*trace));
+       trace->fraction = 1;
+       trace->realfraction = 1;
+       trace->hitsupercontentsmask = hitsupercontentsmask;
+       if (maxvertices < model->surfmesh.num_vertices)
        {
-               // 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]));
-                       }
-                       Mod_Alias_GetMesh_Vertices(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);
-               }
+               if (vertex3f)
+                       Z_Free(vertex3f);
+               maxvertices = (model->surfmesh.num_vertices + 255) & ~255;
+               vertex3f = (float *)Z_Malloc(maxvertices * 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);
+       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, 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);
 }
 
 static void Mod_ConvertAliasVerts (int inverts, trivertx_t *v, trivertx_t *out, int *vertremap)
@@ -748,12 +937,18 @@ static void Mod_BuildAliasSkinFromSkinFrame(texture_t *texture, skinframe_t *ski
        //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->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;
        skinfileitem_t *skinfileitem;
@@ -769,7 +964,7 @@ 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))
                                {
-                                       Mod_LoadTextureFromQ3Shader(skin, skinfileitem->replacement, true, true, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP | TEXF_COMPRESS);
+                                       Mod_LoadTextureFromQ3Shader(skin, skinfileitem->replacement, true, true, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PICMIP | TEXF_COMPRESS);
                                        break;
                                }
                        }
@@ -777,17 +972,17 @@ static void Mod_BuildAliasSkinsFromSkinFiles(texture_t *skin, skinfile_t *skinfi
                        {
                                // don't render unmentioned meshes
                                Mod_BuildAliasSkinFromSkinFrame(skin, NULL);
-                               skin->basematerialflags = skin->currentmaterialflags = 0;
+                               skin->basematerialflags = skin->currentmaterialflags = MATERIALFLAG_NOSHADOW | MATERIALFLAG_NODRAW;
                        }
                }
        }
        else
-               Mod_LoadTextureFromQ3Shader(skin, shadername, true, true, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP | TEXF_COMPRESS);
+               Mod_LoadTextureFromQ3Shader(skin, shadername, 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 BOUNDF(VALUE,MIN,MAX) if (VALUE < MIN || VALUE >= MAX) Host_Error("model %s has an invalid ##VALUE (%f exceeds %f - %f)", loadmodel->name, VALUE, MIN, MAX);
-void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
+void Mod_IDP0_Load(dp_model_t *mod, void *buffer, void *bufferend)
 {
        int i, j, version, totalskins, skinwidth, skinheight, groupframes, groupskins, numverts;
        float scales, scalet, interval;
@@ -822,23 +1017,28 @@ void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
        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->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;
 
        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);
@@ -853,7 +1053,7 @@ void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
        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);
@@ -916,6 +1116,7 @@ void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
                        loadmodel->surfmesh.num_morphframes++;
                }
        }
+       loadmodel->num_poses = loadmodel->surfmesh.num_morphframes;
 
        // store texture coordinates into temporary array, they will be stored
        // after usage is determined (triangle data)
@@ -981,6 +1182,13 @@ void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
                loadmodel->surfmesh.data_texcoordtexture2f[i*2+1] = vertst[i*2+1];
        }
 
+       // 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);
+       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];
+
 // 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);
@@ -995,12 +1203,12 @@ void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
 
        // load the skins
        skinfiles = Mod_LoadSkinFiles();
-       loadmodel->skinscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, loadmodel->numskins * sizeof(animscene_t));
-       loadmodel->num_textures = loadmodel->num_surfaces * totalskins;
-       loadmodel->num_texturesperskin = loadmodel->num_surfaces;
-       loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * totalskins * sizeof(texture_t));
        if (skinfiles)
        {
+               loadmodel->skinscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, loadmodel->numskins * sizeof(animscene_t));
+               loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
+               loadmodel->num_texturesperskin = loadmodel->num_surfaces;
+               loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
                Mod_BuildAliasSkinsFromSkinFiles(loadmodel->data_textures, skinfiles, "default", "");
                Mod_FreeSkinFiles(skinfiles);
                for (i = 0;i < loadmodel->numskins;i++)
@@ -1013,6 +1221,10 @@ void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
        }
        else
        {
+               loadmodel->skinscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, loadmodel->numskins * sizeof(animscene_t));
+               loadmodel->num_textures = loadmodel->num_surfaces * totalskins;
+               loadmodel->num_texturesperskin = loadmodel->num_surfaces;
+               loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * totalskins * sizeof(texture_t));
                totalskins = 0;
                datapointer = startskins;
                for (i = 0;i < loadmodel->numskins;i++)
@@ -1043,7 +1255,7 @@ void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
                                }
                        }
 
-                       sprintf(loadmodel->skinscenes[i].name, "skin %i", i);
+                       dpsnprintf(loadmodel->skinscenes[i].name, sizeof(loadmodel->skinscenes[i].name), "skin %i", i);
                        loadmodel->skinscenes[i].firstframe = totalskins;
                        loadmodel->skinscenes[i].framecount = groupskins;
                        loadmodel->skinscenes[i].framerate = 1.0f / interval;
@@ -1052,9 +1264,9 @@ void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
                        for (j = 0;j < groupskins;j++)
                        {
                                if (groupskins > 1)
-                                       sprintf (name, "%s_%i_%i", loadmodel->name, i, j);
+                                       dpsnprintf (name, sizeof(name), "%s_%i_%i", loadmodel->name, i, j);
                                else
-                                       sprintf (name, "%s_%i", loadmodel->name, i);
+                                       dpsnprintf (name, sizeof(name), "%s_%i", loadmodel->name, i);
                                if (!Mod_LoadTextureFromQ3Shader(loadmodel->data_textures + totalskins * loadmodel->num_surfaces, name, false, true, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PICMIP | TEXF_COMPRESS))
                                        Mod_BuildAliasSkinFromSkinFrame(loadmodel->data_textures + totalskins * loadmodel->num_surfaces, R_SkinFrame_LoadInternalQuake(name, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_PICMIP, true, r_fullbrights.integer, (unsigned char *)datapointer, skinwidth, skinheight));
                                datapointer += skinwidth * skinheight;
@@ -1104,9 +1316,18 @@ void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
        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->TraceBox = Mod_CollisionBIH_TraceBox;
+               loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
+               loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
+               loadmodel->PointSuperContents = Mod_CollisionBIH_PointSuperContents_Mesh;
+       }
 }
 
-void Mod_IDP2_Load(model_t *mod, void *buffer, void *bufferend)
+void Mod_IDP2_Load(dp_model_t *mod, void *buffer, void *bufferend)
 {
        int i, j, hashindex, numxyz, numst, xyz, st, skinwidth, skinheight, *vertremap, version, end;
        float iskinwidth, iskinheight;
@@ -1138,15 +1359,20 @@ void Mod_IDP2_Load(model_t *mod, void *buffer, void *bufferend)
        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->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;
 
        if (LittleLong(pinmodel->num_tris) < 1 || LittleLong(pinmodel->num_tris) > 65536)
@@ -1176,6 +1402,7 @@ void Mod_IDP2_Load(model_t *mod, void *buffer, void *bufferend)
        loadmodel->surfmesh.num_triangles = LittleLong(pinmodel->num_tris);
        loadmodel->numframes = LittleLong(pinmodel->num_frames);
        loadmodel->surfmesh.num_morphframes = loadmodel->numframes;
+       loadmodel->num_poses = loadmodel->surfmesh.num_morphframes;
        skinwidth = LittleLong(pinmodel->skinwidth);
        skinheight = LittleLong(pinmodel->skinheight);
        iskinwidth = 1.0f / skinwidth;
@@ -1185,8 +1412,8 @@ void Mod_IDP2_Load(model_t *mod, void *buffer, void *bufferend)
        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]));
        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]);
@@ -1212,7 +1439,7 @@ void Mod_IDP2_Load(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)
-                       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
        {
@@ -1296,6 +1523,13 @@ void Mod_IDP2_Load(model_t *mod, void *buffer, void *bufferend)
        Mem_Free(md2verthash);
        Mem_Free(md2verthashdata);
 
+       // 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);
+       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];
+
        // load the frames
        datapointer = (base + LittleLong(pinmodel->ofs_frames));
        for (i = 0;i < loadmodel->surfmesh.num_morphframes;i++)
@@ -1339,9 +1573,18 @@ void Mod_IDP2_Load(model_t *mod, void *buffer, void *bufferend)
        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->TraceBox = Mod_CollisionBIH_TraceBox;
+               loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
+               loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
+               loadmodel->PointSuperContents = Mod_CollisionBIH_PointSuperContents_Mesh;
+       }
 }
 
-void Mod_IDP3_Load(model_t *mod, void *buffer, void *bufferend)
+void Mod_IDP3_Load(dp_model_t *mod, void *buffer, void *bufferend)
 {
        int i, j, k, version, meshvertices, meshtriangles;
        unsigned char *data;
@@ -1368,15 +1611,20 @@ void Mod_IDP3_Load(model_t *mod, void *buffer, void *bufferend)
        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->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;
        loadmodel->synctype = ST_RAND;
        // convert model flags to EF flags (MF_ROCKET becomes EF_ROCKET, etc)
@@ -1438,17 +1686,20 @@ void Mod_IDP3_Load(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;
-       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 * 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]) + meshtriangles * sizeof(int[3]) + (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->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->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]);
 
        meshvertices = 0;
        meshtriangles = 0;
@@ -1456,7 +1707,7 @@ void Mod_IDP3_Load(model_t *mod, void *buffer, void *bufferend)
        {
                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;
@@ -1491,21 +1742,34 @@ void Mod_IDP3_Load(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__);
        }
+       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_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
        Mod_Alias_MorphMesh_CompileFrames();
        Mod_Alias_CalculateBoundingBox();
        Mod_FreeSkinFiles(skinfiles);
+       Mod_MakeSortedSurfaces(loadmodel);
 
-       loadmodel->surfmesh.isanimated = loadmodel->numframes > 1 
+       loadmodel->surfmesh.isanimated = loadmodel->numframes > 1
             || (loadmodel->animscenes && loadmodel->animscenes[0].framecount > 1);
+
+       if (!loadmodel->surfmesh.isanimated)
+       {
+               Mod_MakeCollisionBIH(loadmodel, true);
+               loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
+               loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
+               loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
+               loadmodel->PointSuperContents = Mod_CollisionBIH_PointSuperContents_Mesh;
+       }
 }
 
-void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
+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;
-       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;
@@ -1572,15 +1836,20 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                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->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;
 
        loadmodel->numframes = pheader->numscenes;
@@ -1644,7 +1913,7 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
 
        //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++)
        {
@@ -1665,7 +1934,7 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                        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;
@@ -1673,9 +1942,9 @@ void Mod_ZYMOTICMODEL_Load(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;
-       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 * 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]) + meshtriangles * sizeof(int[3]) + (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->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;
@@ -1686,15 +1955,55 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        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->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]);
+       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);
-       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);
@@ -1705,7 +2014,9 @@ void Mod_ZYMOTICMODEL_Load(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++)
        {
-               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
@@ -1728,8 +2039,7 @@ void Mod_ZYMOTICMODEL_Load(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_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
@@ -1766,7 +2076,7 @@ void Mod_ZYMOTICMODEL_Load(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);
 
-               loadmodel->surfacelist[i] = i;
+               loadmodel->sortedmodelsurfaces[i] = i;
                surface = loadmodel->data_surfaces + i;
                surface->texture = loadmodel->data_textures + i;
                surface->num_firsttriangle = meshtriangles;
@@ -1802,8 +2112,12 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        Mod_FreeSkinFiles(skinfiles);
        Mem_Free(vertbonecounts);
        Mem_Free(verts);
+       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];
        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);
@@ -1811,12 +2125,21 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        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->TraceBox = Mod_CollisionBIH_TraceBox;
+               loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
+               loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
+               loadmodel->PointSuperContents = Mod_CollisionBIH_PointSuperContents_Mesh;
+       }
 }
 
-void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
+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;
@@ -1824,6 +2147,9 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        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;
@@ -1866,15 +2192,20 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                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->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;
 
        // model bbox
@@ -1903,21 +2234,21 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        for (i = 0;i < (int)pheader->num_meshs;i++)
        {
                int numverts = BigLong(dpmmesh->num_verts);
-               meshvertices += numverts;;
+               meshvertices += numverts;
                meshtriangles += BigLong(dpmmesh->num_tris);
                dpmmesh++;
        }
 
        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;
        // 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]) + 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) + meshtriangles * sizeof(int[3]) + 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->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;
@@ -1928,13 +2259,16 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        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->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]);
+       loadmodel->surfmesh.data_blendweights = (blendweights_t *)Mem_Alloc(loadmodel->mempool, meshvertices * sizeof(blendweights_t));
 
        for (i = 0;i < loadmodel->numskins;i++)
        {
@@ -1956,21 +2290,55 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        }
 
        // 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++)
        {
-               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
-               poses = (float *) (pbase + BigLong(frame->ofs_bonepositions));
+               poses = (float *) (pbase + BigLong(frames[i].ofs_bonepositions));
                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
-               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
@@ -1980,10 +2348,13 @@ void Mod_DARKPLACESMODEL_Load(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)
+       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++)
        {
-               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
@@ -1997,7 +2368,7 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                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;
@@ -2026,7 +2397,8 @@ void Mod_DARKPLACESMODEL_Load(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++)
                {
-                       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);
@@ -2053,8 +2425,8 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                                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
                                {
@@ -2062,33 +2434,25 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                                        // (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--)
                                                        {
-                                                               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
-                                                       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);
                        }
-                       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_AddBlend(loadmodel, weightindex, weightinfluence);
                }
 
                // since dpm models do not have named sections, reuse their shader name as the section name
@@ -2096,20 +2460,35 @@ void Mod_DARKPLACESMODEL_Load(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__);
        }
+       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);
+       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];
        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);
 
        loadmodel->surfmesh.isanimated = loadmodel->numframes > 1 || loadmodel->animscenes[0].framecount > 1;
+
+       if (!loadmodel->surfmesh.isanimated)
+       {
+               Mod_MakeCollisionBIH(loadmodel, true);
+               loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
+               loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
+               loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
+               loadmodel->PointSuperContents = Mod_CollisionBIH_PointSuperContents_Mesh;
+       }
 }
 
 // no idea why PSK/PSA files contain weird quaternions but they do...
 #define PSKQUATNEGATIONS
-void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
+void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
 {
        int i, j, index, version, recordsize, numrecords, meshvertices, meshtriangles;
        int numpnts, numvtxw, numfaces, nummatts, numbones, numrawweights, numanimbones, numanims, numanimkeys;
@@ -2120,7 +2499,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        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;
@@ -2128,6 +2507,8 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        pskchunk_t *pchunk;
        skinfile_t *skinfiles;
        char animname[MAX_QPATH];
+       size_t size;
+       float biggestorigin;
 
        pchunk = (pskchunk_t *)buffer;
        if (strcmp(pchunk->id, "ACTRHEAD"))
@@ -2136,15 +2517,20 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        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->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;
        loadmodel->synctype = ST_RAND;
 
@@ -2179,8 +2565,8 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                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"))
@@ -2350,8 +2736,8 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                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"))
@@ -2365,7 +2751,7 @@ void Mod_PSKMODEL_Load(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;
-                       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
@@ -2489,31 +2875,35 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        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;
        // 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]) + 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));
+       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(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);
-       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->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_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->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]);
+       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->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]);
+       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++)
        {
@@ -2528,7 +2918,7 @@ void Mod_PSKMODEL_Load(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);
-               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;
@@ -2574,8 +2964,9 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        // (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;
-               float sum;
                for (j = 0;j < numrawweights;j++)
                {
                        if (rawweights[j].pntsindex == vtxw[index].pntsindex)
@@ -2584,33 +2975,27 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                                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--)
                                                {
-                                                       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
-                                               loadmodel->surfmesh.data_vertexweightinfluence4f[index*4+l] = influence;
-                                               loadmodel->surfmesh.data_vertexweightindex4i[index*4+l] = boneindex;
+                                               weightinfluence[l] = influence;
+                                               weightindex[l] = boneindex;
                                                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_AddBlend(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
        for (index = 0, i = 0;index < numanims;index++)
@@ -2625,19 +3010,44 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                }
        }
 
+       // 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++)
        {
                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);
+               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;
        }
        Mod_FreeSkinFiles(skinfiles);
        Mem_Free(animfilebuffer);
+       Mod_MakeSortedSurfaces(loadmodel);
 
        // 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_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);
@@ -2646,5 +3056,13 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        Mod_Alias_CalculateBoundingBox();
 
        loadmodel->surfmesh.isanimated = loadmodel->numframes > 1 || loadmodel->animscenes[0].framecount > 1;
-}
 
+       if (!loadmodel->surfmesh.isanimated)
+       {
+               Mod_MakeCollisionBIH(loadmodel, true);
+               loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
+               loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
+               loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
+               loadmodel->PointSuperContents = Mod_CollisionBIH_PointSuperContents_Mesh;
+       }
+}