]> 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 b1a5bacaa73035e9e06ae5dc14daa09412992c08..9fd4d1cce97d4d622a7f580ee185f26c18099edc 100644 (file)
@@ -46,189 +46,275 @@ void Mod_AliasInit (void)
                mod_md3_sin[i] = sin(i * M_PI * 2.0f / 256.0);
 }
 
-void Mod_Skeletal_AnimateVertices(const dp_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)
+{
+       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)
 {
-#define MAX_BONES 256
        // vertex weighted skeletal
        int i, k;
        int blends;
-       float desiredscale[3];
-       float boneposerelative[MAX_BONES][12];
-       float *matrix, m[12], bonepose[MAX_BONES][12];
+       float m[12];
+       float (*boneposerelative)[12];
+       const blendweights_t * RESTRICT weights;
 
-       // interpolate matrices and concatenate them to their parents
-       for (i = 0;i < model->num_bones;i++)
+       if (maxbonepose < model->num_bones*2 + model->surfmesh.num_blends)
        {
-               for (k = 0;k < 12;k++)
-                       m[k] = 0;
-               VectorClear(desiredscale);
-               for (blends = 0;blends < MAX_FRAMEBLENDS && frameblend[blends].lerp > 0;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)
+       {
+               for (i = 0;i < model->num_bones;i++)
                {
-                       matrix = model->data_poses + (frameblend[blends].subframe * model->num_bones + i) * 12;
-                       for (k = 0;k < 12;k++)
-                               m[k] += matrix[k] * frameblend[blends].lerp;
-                       desiredscale[0] += frameblend[blends].lerp * VectorLength(matrix    );
-                       desiredscale[1] += frameblend[blends].lerp * VectorLength(matrix + 4);
-                       desiredscale[2] += frameblend[blends].lerp * VectorLength(matrix + 8);
+                       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]);
                }
-               VectorNormalize(m    );
-               VectorNormalize(m + 4);
-               VectorNormalize(m + 8);
-               VectorScale(m    , desiredscale[0], m    );
-               VectorScale(m + 4, desiredscale[1], m + 4);
-               VectorScale(m + 8, desiredscale[2], m + 8);
-               if (i == r_skeletal_debugbone.integer)
-                       m[r_skeletal_debugbonecomponent.integer % 12] += r_skeletal_debugbonevalue.value;
-               m[3] *= r_skeletal_debugtranslatex.value;
-               m[7] *= r_skeletal_debugtranslatey.value;
-               m[11] *= r_skeletal_debugtranslatez.value;
-               if (model->data_bones[i].parent >= 0)
-                       R_ConcatTransforms(bonepose[model->data_bones[i].parent], m, bonepose[i]);
-               else
-                       for (k = 0;k < 12;k++)
-                               bonepose[i][k] = m[k];
-               // create a relative deformation matrix to describe displacement
-               // from the base mesh, which is used by the actual weighting
-               R_ConcatTransforms(bonepose[i], model->data_baseboneposeinverse + i * 12, boneposerelative[i]);
-       }
-       // blend the vertex bone weights
-       // special case for the extremely common wf[0] == 1 because it saves 3 multiplies per array when compared to the other case (w[0] is always 1 if only one bone controls this vertex, artists only use multiple bones for certain special cases)
-       // special case for the first bone because it avoids the need to memset the arrays before filling
-       if (vertex3f)
+       }
+       else
        {
-               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)
+               float originscale = model->num_posescale;
+               float x,y,z,w,lerp;
+               const short * RESTRICT pose6s;
+               for (i = 0;i < model->num_bones;i++)
                {
-                       if (wf[0] == 1)
+                       memset(m, 0, sizeof(m));
+                       for (blends = 0;blends < MAX_FRAMEBLENDS && frameblend[blends].lerp > 0;blends++)
                        {
-                               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]);
+                               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;
+                       m[7] *= r_skeletal_debugtranslatey.value;
+                       m[11] *= r_skeletal_debugtranslatez.value;
+                       if (model->data_bones[i].parent >= 0)
+                               R_ConcatTransforms(bonepose[model->data_bones[i].parent], m, bonepose[i]);
                        else
-                       {
-                               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]);
-                               }
-                       }
+                               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]);
                }
        }
-       if (normal3f)
+       
+       // 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++)
+               {
+                       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 *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 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)
                {
-                       if (wf[0] == 1)
+                       const float * RESTRICT n = model->surfmesh.data_normal3f;
+                       if (svector3f && tvector3f)
                        {
-                               const float *m = boneposerelative[wi[0]];
-                               normal3f[0] = (n[0] * m[0] + n[1] * m[1] + n[2] * m[ 2]);
+                               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 * 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]);
+                                       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]);
+                               }
+                               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]);
                        }
-                       else
+               }
+               else
+               {
+                       for (i = 0;i < model->surfmesh.num_vertices;i++, v += 3, b++, vertex3f += 3)
                        {
-                               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]);
-                               }
+                               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 (normal3f)
+       {
+               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)
+               {
+                       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]);
+               }
+       }
+
        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)
+               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)
                {
-                       if (wf[0] == 1)
-                       {
-                               const float *m = boneposerelative[wi[0]];
-                               svector3f[0] = (sv[0] * m[0] + sv[1] * m[1] + sv[2] * m[ 2]);
-                               svector3f[1] = (sv[0] * m[4] + sv[1] * m[5] + sv[2] * m[ 6]);
-                               svector3f[2] = (sv[0] * m[8] + sv[1] * m[9] + sv[2] * m[10]);
-                       }
-                       else
-                       {
-                               const float *m = boneposerelative[wi[0]];
-                               float f = wf[0];
-                               svector3f[0] = f * (sv[0] * m[0] + sv[1] * m[1] + sv[2] * m[ 2]);
-                               svector3f[1] = f * (sv[0] * m[4] + sv[1] * m[5] + sv[2] * m[ 6]);
-                               svector3f[2] = f * (sv[0] * m[8] + sv[1] * m[9] + sv[2] * m[10]);
-                               for (k = 1;k < 4 && wf[k];k++)
-                               {
-                                       const float *m = boneposerelative[wi[k]];
-                                       float f = wf[k];
-                                       svector3f[0] += f * (sv[0] * m[0] + sv[1] * m[1] + sv[2] * m[ 2]);
-                                       svector3f[1] += f * (sv[0] * m[4] + sv[1] * m[5] + sv[2] * m[ 6]);
-                                       svector3f[2] += f * (sv[0] * m[8] + sv[1] * m[9] + sv[2] * m[10]);
-                               }
-                       }
+                       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 *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)
+               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)
                {
-                       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];
+                       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, float *vertex3f, float *normal3f, float *svector3f, float *tvector3f)
+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;
@@ -317,7 +403,7 @@ void Mod_MD3_AnimateVertices(const dp_model_t *model, const frameblend_t *frameb
        }
 }
 
-void Mod_MDL_AnimateVertices(const dp_model_t *model, const frameblend_t *frameblend, float *vertex3f, float *normal3f, float *svector3f, float *tvector3f)
+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;
@@ -414,34 +500,65 @@ void Mod_MDL_AnimateVertices(const dp_model_t *model, const frameblend_t *frameb
        }
 }
 
-int Mod_Alias_GetTagMatrix(const dp_model_t *model, int poseframe, int tagindex, matrix4x4_t *outmatrix)
+int Mod_Alias_GetTagMatrix(const dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, int tagindex, matrix4x4_t *outmatrix)
 {
-       const float *boneframe;
-       float tempbonematrix[12], bonematrix[12];
+       matrix4x4_t temp;
+       matrix4x4_t parentbonematrix;
+       matrix4x4_t tempbonematrix;
+       matrix4x4_t bonematrix;
+       matrix4x4_t blendmatrix;
+       int blendindex;
+       int parenttagindex;
+       int k;
+       float lerp;
+       const float *input;
+       float blendtag[12];
        *outmatrix = identitymatrix;
-       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)
@@ -450,35 +567,57 @@ int Mod_Alias_GetTagMatrix(const dp_model_t *model, int poseframe, int tagindex,
        return 0;
 }
 
-int Mod_Alias_GetExtendedTagInfoForIndex(const dp_model_t *model, unsigned int skin, int poseframe, int tagindex, int *parentindex, const char **tagname, matrix4x4_t *tag_localmatrix)
+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)
 {
-       const float *boneframe;
+       int blendindex;
+       int k;
+       float lerp;
+       matrix4x4_t bonematrix;
+       matrix4x4_t blendmatrix;
+       const float *input;
+       float blendtag[12];
 
-       if(skin >= (unsigned int)model->numskins)
-               skin = 0;
-
-       if (model->num_bones)
+       if (skeleton && skeleton->relativetransforms)
        {
-               if(tagindex >= model->num_bones || tagindex < 0)
+               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;
-               if (poseframe >= model->num_poses)
-                       return 2;
-
-               boneframe = model->data_poses + poseframe * model->num_bones * 12;
                *parentindex = model->data_bones[tagindex].parent;
                *tagname = model->data_bones[tagindex].name;
-               Matrix4x4_FromArray12FloatD3D(tag_localmatrix, boneframe + tagindex * 12);
+               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;
        }
-
-       if (model->num_tags)
+       else if (model->num_tags)
        {
-               if(tagindex >= model->num_tags || tagindex < 0)
+               if (tagindex < 0 || tagindex >= model->num_tags)
                        return 1;
-               if (poseframe >= model->num_tagframes)
-                       return 2;
+               *parentindex = -1;
                *tagname = model->data_tags[tagindex].name;
-               Matrix4x4_FromArray12FloatGL(tag_localmatrix, 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(tag_localmatrix, blendtag);
                return 0;
        }
 
@@ -503,47 +642,25 @@ int Mod_Alias_GetTagIndexForName(const dp_model_t *model, unsigned int skin, con
 
 static void Mod_BuildBaseBonePoses(void)
 {
-       int i, k;
-       double scale;
-       float *basebonepose;
-       float *in12f = loadmodel->data_poses;
-       float *out12f;
-       float *outinv12f = loadmodel->data_baseboneposeinverse;
+       int boneindex;
+       matrix4x4_t *basebonepose;
+       float *outinvmatrix = loadmodel->data_baseboneposeinverse;
+       matrix4x4_t bonematrix;
+       matrix4x4_t tempbonematrix;
        if (!loadmodel->num_bones)
                return;
-       out12f = basebonepose = (float *) Mem_Alloc(tempmempool, loadmodel->num_bones * sizeof(float[12]));
-       for (i = 0;i < loadmodel->num_bones;i++, in12f += 12, out12f += 12, outinv12f += 12)
+       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);
 }
@@ -565,7 +682,7 @@ static void Mod_Alias_CalculateBoundingBox(void)
        radius = 0;
        for (frameblend[0].subframe = 0;frameblend[0].subframe < loadmodel->num_poses;frameblend[0].subframe++)
        {
-               loadmodel->AnimateVertices(loadmodel, frameblend, vertex3f, NULL, NULL, NULL);
+               loadmodel->AnimateVertices(loadmodel, frameblend, NULL, vertex3f, NULL, NULL, NULL);
                for (vnum = 0, v = vertex3f;vnum < loadmodel->surfmesh.num_vertices;vnum++, v += 3)
                {
                        if (firstvertex)
@@ -622,7 +739,7 @@ static void Mod_Alias_MorphMesh_CompileFrames(void)
        for (i = loadmodel->surfmesh.num_morphframes-1;i >= 0;i--)
        {
                frameblend[0].subframe = i;
-               loadmodel->AnimateVertices(loadmodel, frameblend, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_normal3f, NULL, NULL);
+               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++)
@@ -633,11 +750,10 @@ static void Mod_Alias_MorphMesh_CompileFrames(void)
        }
 }
 
-static void Mod_MDLMD2MD3_TraceLine(dp_model_t *model, int frame, trace_t *trace, const vec3_t start, 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[MAX_FRAMEBLENDS];
        msurface_t *surface;
        static int maxvertices = 0;
        static float *vertex3f = NULL;
@@ -645,9 +761,6 @@ static void Mod_MDLMD2MD3_TraceLine(dp_model_t *model, int frame, trace_t *trace
        trace->fraction = 1;
        trace->realfraction = 1;
        trace->hitsupercontentsmask = hitsupercontentsmask;
-       memset(frameblend, 0, sizeof(frameblend));
-       frameblend[0].subframe = frame;
-       frameblend[0].lerp = 1;
        if (maxvertices < model->surfmesh.num_vertices)
        {
                if (vertex3f)
@@ -661,22 +774,20 @@ static void Mod_MDLMD2MD3_TraceLine(dp_model_t *model, int frame, trace_t *trace
        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++)
-       {
-               model->AnimateVertices(model, frameblend, vertex3f, NULL, NULL, NULL);
                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 void Mod_MDLMD2MD3_TraceBox(dp_model_t *model, int frame, trace_t *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask)
+static 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];
-       frameblend_t frameblend[MAX_FRAMEBLENDS];
        msurface_t *surface;
-       static int maxvertices = 0;
-       static float *vertex3f = NULL;
        colboxbrushf_t thisbrush_start, thisbrush_end;
        vec3_t boxstartmins, boxstartmaxs, boxendmins, boxendmaxs;
 
@@ -684,7 +795,7 @@ static void Mod_MDLMD2MD3_TraceBox(dp_model_t *model, int frame, trace_t *trace,
        {
                VectorAdd(start, boxmins, shiftstart);
                VectorAdd(end, boxmins, shiftend);
-               Mod_MDLMD2MD3_TraceLine(model, frame, trace, start, end, hitsupercontentsmask);
+               Mod_MDLMD2MD3_TraceLine(model, frameblend, skeleton, trace, shiftstart, shiftend, hitsupercontentsmask);
                VectorSubtract(trace->endpos, boxmins, trace->endpos);
                return;
        }
@@ -694,9 +805,6 @@ static void Mod_MDLMD2MD3_TraceBox(dp_model_t *model, int frame, trace_t *trace,
        trace->fraction = 1;
        trace->realfraction = 1;
        trace->hitsupercontentsmask = hitsupercontentsmask;
-       memset(frameblend, 0, sizeof(frameblend));
-       frameblend[0].subframe = frame;
-       frameblend[0].lerp = 1;
        if (maxvertices < model->surfmesh.num_vertices)
        {
                if (vertex3f)
@@ -716,18 +824,16 @@ static void Mod_MDLMD2MD3_TraceBox(dp_model_t *model, int frame, trace_t *trace,
        VectorAdd(end, boxmaxs, boxendmaxs);
        Collision_BrushForBox(&thisbrush_start, boxstartmins, boxstartmaxs, 0, 0, NULL);
        Collision_BrushForBox(&thisbrush_end, 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 (maxvertices < model->surfmesh.num_vertices)
-               {
-                       if (vertex3f)
-                               Z_Free(vertex3f);
-                       maxvertices = (model->surfmesh.num_vertices + 255) & ~255;
-                       vertex3f = (float *)Z_Malloc(maxvertices * sizeof(float[3]));
-               }
-               model->AnimateVertices(model, frameblend, vertex3f, NULL, NULL, NULL);
-               Collision_TraceBrushTriangleMeshFloat(trace, &thisbrush_start.brush, &thisbrush_end.brush, model->surfmesh.num_triangles, model->surfmesh.data_element3i, vertex3f, 0, NULL, SUPERCONTENTS_SOLID | (surface->texture->basematerialflags & MATERIALFLAGMASK_TRANSLUCENT ? 0 : SUPERCONTENTS_OPAQUE), 0, surface->texture, segmentmins, segmentmaxs);
+               if (vertex3f)
+                       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)
@@ -831,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, const char *meshname, const char *shadername)
+void Mod_BuildAliasSkinsFromSkinFiles(texture_t *skin, skinfile_t *skinfile, const char *meshname, const char *shadername)
 {
        int i;
        skinfileitem_t *skinfileitem;
@@ -852,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;
                                }
                        }
@@ -865,7 +977,7 @@ static void Mod_BuildAliasSkinsFromSkinFiles(texture_t *skin, skinfile_t *skinfi
                }
        }
        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);
@@ -911,6 +1023,7 @@ void Mod_IDP0_Load(dp_model_t *mod, void *buffer, void *bufferend)
        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;
@@ -940,7 +1053,7 @@ void Mod_IDP0_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->numframes = LittleLong(pinmodel->numframes);
        BOUNDI(loadmodel->numframes,0,65536);
        loadmodel->synctype = (synctype_t)LittleLong (pinmodel->synctype);
-       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);
@@ -1203,6 +1316,15 @@ void Mod_IDP0_Load(dp_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(dp_model_t *mod, void *buffer, void *bufferend)
@@ -1243,6 +1365,7 @@ void Mod_IDP2_Load(dp_model_t *mod, void *buffer, void *bufferend)
        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;
@@ -1316,7 +1439,7 @@ void Mod_IDP2_Load(dp_model_t *mod, void *buffer, void *bufferend)
                loadmodel->num_texturesperskin = loadmodel->num_surfaces;
                loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
                for (i = 0;i < loadmodel->numskins;i++, inskin += MD2_SKINNAME)
-                       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
        {
@@ -1450,6 +1573,15 @@ void Mod_IDP2_Load(dp_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(dp_model_t *mod, void *buffer, void *bufferend)
@@ -1485,6 +1617,7 @@ void Mod_IDP3_Load(dp_model_t *mod, void *buffer, void *bufferend)
        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;
@@ -1620,6 +1753,15 @@ void Mod_IDP3_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
        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(dp_model_t *mod, void *buffer, void *bufferend)
@@ -1627,7 +1769,7 @@ void Mod_ZYMOTICMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        zymtype1header_t *pinmodel, *pheader;
        unsigned char *pbase;
        int i, j, k, numposes, meshvertices, meshtriangles, *bonecount, *vertbonecounts, count, *renderlist, *renderlistend, *outelements;
-       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;
@@ -1700,6 +1842,7 @@ void Mod_ZYMOTICMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        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;
@@ -1799,7 +1942,7 @@ void Mod_ZYMOTICMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
        loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
        loadmodel->num_texturesperskin = loadmodel->num_surfaces;
-       data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t) + meshtriangles * sizeof(int[3]) + meshtriangles * sizeof(int[3]) + (meshvertices <= 65536 ? meshtriangles * sizeof(unsigned short[3]) : 0) + meshvertices * sizeof(float[14]) + meshvertices * sizeof(int[4]) + meshvertices * sizeof(float[4]) + loadmodel->num_poses * loadmodel->num_bones * 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->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
        loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
@@ -1812,17 +1955,55 @@ void Mod_ZYMOTICMODEL_Load(dp_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 * loadmodel->num_bones * sizeof(float[12]);
        loadmodel->data_baseboneposeinverse = (float *)data;data += loadmodel->num_bones * sizeof(float[12]);
+       loadmodel->surfmesh.num_blends = 0;
+       loadmodel->surfmesh.blends = (unsigned short *)data;data += meshvertices * sizeof(unsigned short);
        if (loadmodel->surfmesh.num_vertices <= 65536)
                loadmodel->surfmesh.data_element3s = (unsigned short *)data;data += loadmodel->surfmesh.num_triangles * sizeof(unsigned short[3]);
+       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);
@@ -1833,7 +2014,9 @@ void Mod_ZYMOTICMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        bonepose = (float *)Z_Malloc(loadmodel->num_bones * sizeof(float[12]));
        for (i = 0;i < loadmodel->num_bones;i++)
        {
-               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
@@ -1856,8 +2039,7 @@ void Mod_ZYMOTICMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
                loadmodel->surfmesh.data_vertex3f[j*3+1] = relativeorigin[0] * m[4] + relativeorigin[1] * m[5] + relativeorigin[2] * m[ 6] + m[ 7];
                loadmodel->surfmesh.data_vertex3f[j*3+2] = relativeorigin[0] * m[8] + relativeorigin[1] * m[9] + relativeorigin[2] * m[10] + m[11];
                // store the weight as the primary weight on this vertex
-               loadmodel->surfmesh.data_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
@@ -1943,12 +2125,21 @@ void Mod_ZYMOTICMODEL_Load(dp_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(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;
@@ -1956,6 +2147,9 @@ void Mod_DARKPLACESMODEL_Load(dp_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;
@@ -2004,6 +2198,7 @@ void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        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;
@@ -2051,7 +2246,7 @@ void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        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]) + (meshvertices <= 65536 ? meshtriangles * sizeof(unsigned short[3]) : 0) + meshtriangles * sizeof(int[3]) + meshvertices * (sizeof(float[14]) + sizeof(int[4]) + sizeof(float[4])) + loadmodel->num_poses * loadmodel->num_bones * 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->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
        loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
@@ -2064,15 +2259,16 @@ void Mod_DARKPLACESMODEL_Load(dp_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 * loadmodel->num_bones * 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++)
        {
@@ -2094,21 +2290,55 @@ void Mod_DARKPLACESMODEL_Load(dp_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
@@ -2118,10 +2348,13 @@ void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        // reconstruct frame 0 matrices to allow reconstruction of the base mesh
        // (converting from weight-blending skeletal animation to
        //  deformation-based skeletal animation)
+       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
@@ -2164,7 +2397,8 @@ void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
                data = (unsigned char *) (pbase + BigLong(dpmmesh->ofs_verts));
                for (j = surface->num_firstvertex;j < surface->num_firstvertex + surface->num_vertices;j++)
                {
-                       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);
@@ -2191,8 +2425,8 @@ void Mod_DARKPLACESMODEL_Load(dp_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
                                {
@@ -2200,33 +2434,25 @@ void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
                                        // (which only accepts up to 4 bones per vertex)
                                        for (l = 0;l < 4;l++)
                                        {
-                                               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
@@ -2234,6 +2460,8 @@ void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
                Mod_ValidateElements(loadmodel->surfmesh.data_element3i + surface->num_firsttriangle * 3, surface->num_triangles, surface->num_firstvertex, surface->num_vertices, __FILE__, __LINE__);
        }
+       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);
@@ -2247,6 +2475,15 @@ void Mod_DARKPLACESMODEL_Load(dp_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;
+       }
 }
 
 // no idea why PSK/PSA files contain weird quaternions but they do...
@@ -2262,7 +2499,7 @@ void Mod_PSKMODEL_Load(dp_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;
@@ -2271,6 +2508,7 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        skinfile_t *skinfiles;
        char animname[MAX_QPATH];
        size_t size;
+       float biggestorigin;
 
        pchunk = (pskchunk_t *)buffer;
        if (strcmp(pchunk->id, "ACTRHEAD"))
@@ -2285,6 +2523,7 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        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;
@@ -2326,8 +2565,8 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
                version = LittleLong(pchunk->version);
                recordsize = LittleLong(pchunk->recordsize);
                numrecords = LittleLong(pchunk->numrecords);
-               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"))
@@ -2497,8 +2736,8 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
                version = LittleLong(pchunk->version);
                recordsize = LittleLong(pchunk->recordsize);
                numrecords = LittleLong(pchunk->numrecords);
-               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"))
@@ -2512,7 +2751,7 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
                                Host_Error("%s: %s has unsupported recordsize", animname, pchunk->id);
                        // byteswap in place and keep the pointer
                        numanimbones = numrecords;
-                       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
@@ -2643,7 +2882,7 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->surfmesh.num_vertices = meshvertices;
        loadmodel->surfmesh.num_triangles = meshtriangles;
        // do most allocations as one merged chunk
-       size = loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t) + loadmodel->surfmesh.num_triangles * sizeof(int[3]) + loadmodel->surfmesh.num_triangles * sizeof(int[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[2]) + loadmodel->surfmesh.num_vertices * sizeof(int[4]) + loadmodel->surfmesh.num_vertices * sizeof(float[4]) + loadmodel->num_poses * loadmodel->num_bones * sizeof(float[12]) + loadmodel->num_bones * sizeof(float[12]) + loadmodel->numskins * sizeof(animscene_t) + loadmodel->num_bones * sizeof(aliasbone_t) + loadmodel->numframes * sizeof(animscene_t) + ((loadmodel->surfmesh.num_vertices <= 65536) ? (loadmodel->surfmesh.num_triangles * sizeof(unsigned short[3])) : 0);
+       size = loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t) + loadmodel->surfmesh.num_triangles * sizeof(int[3]) + 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->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
@@ -2655,15 +2894,16 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->surfmesh.data_tvector3f = (float *)data;data += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
        loadmodel->surfmesh.data_normal3f = (float *)data;data += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
        loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
-       loadmodel->surfmesh.data_vertexweightindex4i = (int *)data;data += loadmodel->surfmesh.num_vertices * sizeof(int[4]);
-       loadmodel->surfmesh.data_vertexweightinfluence4f = (float *)data;data += loadmodel->surfmesh.num_vertices * sizeof(float[4]);
-       loadmodel->data_poses = (float *)data;data += loadmodel->num_poses * loadmodel->num_bones * 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 (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++)
        {
@@ -2724,8 +2964,9 @@ void Mod_PSKMODEL_Load(dp_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)
@@ -2734,33 +2975,27 @@ void Mod_PSKMODEL_Load(dp_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++)
@@ -2775,13 +3010,34 @@ void Mod_PSKMODEL_Load(dp_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);
@@ -2800,4 +3056,13 @@ void Mod_PSKMODEL_Load(dp_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;
+       }
 }