]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - model_alias.c
implemented FTE_CSQC_SKELETONOBJECTS (clientside support)
[xonotic/darkplaces.git] / model_alias.c
index 55c62a4533c1fe1224a7f2a6fef41617a50f15f1..e8feab96e5dc3030bc57090f9eaca000075b4ede 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,46 +41,83 @@ 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_Skeletal_AnimateVertices(const model_t *model, const frameblend_t *frameblend, float *vertex3f, float *normal3f, float *svector3f, float *tvector3f)
+void Mod_Skeletal_AnimateVertices(const dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, float *vertex3f, float *normal3f, float *svector3f, float *tvector3f)
 {
 #define MAX_BONES 256
        // vertex weighted skeletal
        int i, k;
+       int blends;
+       float desiredscale[3];
        float boneposerelative[MAX_BONES][12];
-       // interpolate matrices and concatenate them to their parents
-       for (i = 0;i < model->num_bones;i++)
+       float *matrix, m[12], bonepose[MAX_BONES][12];
+
+       if (skeleton && !skeleton->relativetransforms)
+               skeleton = NULL;
+
+       // interpolate matrices
+       if (skeleton)
        {
-               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++)
+               for (i = 0;i < model->num_bones;i++)
                {
-                       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;
+                       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
+                               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]);
                }
-               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
+       }
+       else
+       {
+               for (i = 0;i < model->num_bones;i++)
+               {
                        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]);
+                               m[k] = 0;
+                       VectorClear(desiredscale);
+                       for (blends = 0;blends < MAX_FRAMEBLENDS && frameblend[blends].lerp > 0;blends++)
+                       {
+                               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);
+                       }
+                       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)
        {
                const float *v = model->surfmesh.data_vertex3f;
                const int *wi = model->surfmesh.data_vertexweightindex4i;
@@ -213,13 +251,13 @@ void Mod_Skeletal_AnimateVertices(const model_t *model, const frameblend_t *fram
        }
 }
 
-void Mod_MD3_AnimateVertices(const 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;
        int numverts = model->surfmesh.num_vertices;
        numblends = 0;
-       for (blendnum = 0;blendnum < 4;blendnum++)
+       for (blendnum = 0;blendnum < MAX_FRAMEBLENDS;blendnum++)
        {
                //VectorMA(translate, model->surfmesh.num_morphmdlframetranslate, frameblend[blendnum].lerp, translate);
                if (frameblend[blendnum].lerp > 0)
@@ -228,24 +266,27 @@ void Mod_MD3_AnimateVertices(const model_t *model, const frameblend_t *frameblen
        // special case for the first blend because it avoids some adds and the need to memset the arrays first
        for (blendnum = 0;blendnum < numblends;blendnum++)
        {
-               const md3vertex_t *verts = model->surfmesh.data_morphmd3vertex + numverts * frameblend[blendnum].frame;
-               float scale = frameblend[blendnum].lerp * (1.0f / 64.0f);
-               if (blendnum == 0)
+               const md3vertex_t *verts = model->surfmesh.data_morphmd3vertex + numverts * frameblend[blendnum].subframe;
+               if (vertex3f)
                {
-                       for (i = 0;i < numverts;i++)
+                       float scale = frameblend[blendnum].lerp * (1.0f / 64.0f);
+                       if (blendnum == 0)
                        {
-                               vertex3f[i * 3 + 0] = verts[i].origin[0] * scale;
-                               vertex3f[i * 3 + 1] = verts[i].origin[1] * scale;
-                               vertex3f[i * 3 + 2] = verts[i].origin[2] * scale;
+                               for (i = 0;i < numverts;i++)
+                               {
+                                       vertex3f[i * 3 + 0] = verts[i].origin[0] * scale;
+                                       vertex3f[i * 3 + 1] = verts[i].origin[1] * scale;
+                                       vertex3f[i * 3 + 2] = verts[i].origin[2] * scale;
+                               }
                        }
-               }
-               else
-               {
-                       for (i = 0;i < numverts;i++)
+                       else
                        {
-                               vertex3f[i * 3 + 0] += verts[i].origin[0] * scale;
-                               vertex3f[i * 3 + 1] += verts[i].origin[1] * scale;
-                               vertex3f[i * 3 + 2] += verts[i].origin[2] * scale;
+                               for (i = 0;i < numverts;i++)
+                               {
+                                       vertex3f[i * 3 + 0] += verts[i].origin[0] * scale;
+                                       vertex3f[i * 3 + 1] += verts[i].origin[1] * scale;
+                                       vertex3f[i * 3 + 2] += verts[i].origin[2] * scale;
+                               }
                        }
                }
                // the yaw and pitch stored in md3 models are 8bit quantized angles
@@ -277,7 +318,7 @@ void Mod_MD3_AnimateVertices(const model_t *model, const frameblend_t *frameblen
                }
                if (svector3f)
                {
-                       const texvecvertex_t *texvecvert = model->surfmesh.data_morphtexvecvertex + numverts * frameblend[blendnum].frame;
+                       const texvecvertex_t *texvecvert = model->surfmesh.data_morphtexvecvertex + numverts * frameblend[blendnum].subframe;
                        float f = frameblend[blendnum].lerp * (1.0f / 127.0f);
                        if (blendnum == 0)
                        {
@@ -299,7 +340,7 @@ void Mod_MD3_AnimateVertices(const model_t *model, const frameblend_t *frameblen
        }
 }
 
-void Mod_MDL_AnimateVertices(const 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;
@@ -309,10 +350,10 @@ void Mod_MDL_AnimateVertices(const model_t *model, const frameblend_t *frameblen
        numblends = 0;
        // blend the frame translates to avoid redundantly doing so on each vertex
        // (a bit of a brain twister but it works)
-       for (blendnum = 0;blendnum < 4;blendnum++)
+       for (blendnum = 0;blendnum < MAX_FRAMEBLENDS;blendnum++)
        {
                if (model->surfmesh.data_morphmd2framesize6f)
-                       VectorMA(translate, frameblend[blendnum].lerp, model->surfmesh.data_morphmd2framesize6f + frameblend[blendnum].frame * 6 + 3, translate);
+                       VectorMA(translate, frameblend[blendnum].lerp, model->surfmesh.data_morphmd2framesize6f + frameblend[blendnum].subframe * 6 + 3, translate);
                else
                        VectorMA(translate, frameblend[blendnum].lerp, model->surfmesh.num_morphmdlframetranslate, translate);
                if (frameblend[blendnum].lerp > 0)
@@ -321,28 +362,31 @@ void Mod_MDL_AnimateVertices(const model_t *model, const frameblend_t *frameblen
        // special case for the first blend because it avoids some adds and the need to memset the arrays first
        for (blendnum = 0;blendnum < numblends;blendnum++)
        {
-               const trivertx_t *verts = model->surfmesh.data_morphmdlvertex + numverts * frameblend[blendnum].frame;
-               float scale[3];
-               if (model->surfmesh.data_morphmd2framesize6f)
-                       VectorScale(model->surfmesh.data_morphmd2framesize6f + frameblend[blendnum].frame * 6, frameblend[blendnum].lerp, scale);
-               else
-                       VectorScale(model->surfmesh.num_morphmdlframescale, frameblend[blendnum].lerp, scale);
-               if (blendnum == 0)
+               const trivertx_t *verts = model->surfmesh.data_morphmdlvertex + numverts * frameblend[blendnum].subframe;
+               if (vertex3f)
                {
-                       for (i = 0;i < numverts;i++)
+                       float scale[3];
+                       if (model->surfmesh.data_morphmd2framesize6f)
+                               VectorScale(model->surfmesh.data_morphmd2framesize6f + frameblend[blendnum].subframe * 6, frameblend[blendnum].lerp, scale);
+                       else
+                               VectorScale(model->surfmesh.num_morphmdlframescale, frameblend[blendnum].lerp, scale);
+                       if (blendnum == 0)
                        {
-                               vertex3f[i * 3 + 0] = translate[0] + verts[i].v[0] * scale[0];
-                               vertex3f[i * 3 + 1] = translate[1] + verts[i].v[1] * scale[1];
-                               vertex3f[i * 3 + 2] = translate[2] + verts[i].v[2] * scale[2];
+                               for (i = 0;i < numverts;i++)
+                               {
+                                       vertex3f[i * 3 + 0] = translate[0] + verts[i].v[0] * scale[0];
+                                       vertex3f[i * 3 + 1] = translate[1] + verts[i].v[1] * scale[1];
+                                       vertex3f[i * 3 + 2] = translate[2] + verts[i].v[2] * scale[2];
+                               }
                        }
-               }
-               else
-               {
-                       for (i = 0;i < numverts;i++)
+                       else
                        {
-                               vertex3f[i * 3 + 0] += verts[i].v[0] * scale[0];
-                               vertex3f[i * 3 + 1] += verts[i].v[1] * scale[1];
-                               vertex3f[i * 3 + 2] += verts[i].v[2] * scale[2];
+                               for (i = 0;i < numverts;i++)
+                               {
+                                       vertex3f[i * 3 + 0] += verts[i].v[0] * scale[0];
+                                       vertex3f[i * 3 + 1] += verts[i].v[1] * scale[1];
+                                       vertex3f[i * 3 + 2] += verts[i].v[2] * scale[2];
+                               }
                        }
                }
                // the vertex normals in mdl models are an index into a table of
@@ -371,7 +415,7 @@ void Mod_MDL_AnimateVertices(const model_t *model, const frameblend_t *frameblen
                }
                if (svector3f)
                {
-                       const texvecvertex_t *texvecvert = model->surfmesh.data_morphtexvecvertex + numverts * frameblend[blendnum].frame;
+                       const texvecvertex_t *texvecvert = model->surfmesh.data_morphtexvecvertex + numverts * frameblend[blendnum].subframe;
                        float f = frameblend[blendnum].lerp * (1.0f / 127.0f);
                        if (blendnum == 0)
                        {
@@ -393,47 +437,136 @@ void Mod_MDL_AnimateVertices(const model_t *model, const frameblend_t *frameblen
        }
 }
 
-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)
 {
+       matrix4x4_t temp;
        const float *boneframe;
-       float tempbonematrix[12], bonematrix[12];
+       const float *input;
+       int blendindex;
+       int parenttagindex;
+       int k;
+       float lerp;
+       float tempbonematrix[12], bonematrix[12], blendmatrix[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)
+               for (k = 0;k < 12;k++)
+                       blendmatrix[k] = 0;
+               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;
+                       boneframe = model->data_poses + frameblend[blendindex].subframe * model->num_bones * 12;
+                       input = boneframe + tagindex * 12;
+                       for (k = 0;k < 12;k++)
+                               bonematrix[k] = input[k];
+                       parenttagindex = tagindex;
+                       while ((parenttagindex = model->data_bones[parenttagindex].parent) >= 0)
+                       {
+                               for (k = 0;k < 12;k++)
+                                       tempbonematrix[k] = bonematrix[k];
+                               input = boneframe + parenttagindex * 12;
+                               R_ConcatTransforms(input, tempbonematrix, bonematrix);
+                       }
+                       for (k = 0;k < 12;k++)
+                               blendmatrix[k] += bonematrix[k] * lerp;
                }
-               Matrix4x4_FromArray12FloatD3D(outmatrix, bonematrix);
+               Matrix4x4_FromArray12FloatD3D(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++)
+                       blendmatrix[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++)
+                               blendmatrix[k] += input[k] * lerp;
+               }
+               Matrix4x4_FromArray12FloatGL(outmatrix, blendmatrix);
        }
+
+       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)
+{
+       const float *boneframe;
+       const float *input;
+       int blendindex;
+       int k;
+       float lerp;
+       float blendmatrix[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 >= model->num_bones || tagindex < 0)
+                       return 1;
+               *parentindex = model->data_bones[tagindex].parent;
+               *tagname = model->data_bones[tagindex].name;
+               for (blendindex = 0;blendindex < MAX_FRAMEBLENDS && frameblend[blendindex].lerp > 0;blendindex++)
+               {
+                       lerp = frameblend[blendindex].lerp;
+                       boneframe = model->data_poses + frameblend[blendindex].subframe * model->num_bones * 12;
+                       input = boneframe + tagindex * 12;
+                       for (k = 0;k < 12;k++)
+                               blendmatrix[k] += input[k] * lerp;
+               }
+               Matrix4x4_FromArray12FloatD3D(tag_localmatrix, blendmatrix);
+               return 0;
+       }
+       else if (model->num_tags)
+       {
+               if(tagindex >= model->num_tags || tagindex < 0)
+                       return 1;
+               *parentindex = -1;
+               *tagname = model->data_tags[tagindex].name;
+               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++)
+                               blendmatrix[k] += input[k] * lerp;
+               }
+               Matrix4x4_FromArray12FloatGL(tag_localmatrix, blendmatrix);
+               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))
@@ -449,10 +582,13 @@ static void Mod_BuildBaseBonePoses(void)
 {
        int i, k;
        double scale;
-       float *basebonepose = Mem_Alloc(tempmempool, loadmodel->num_bones * sizeof(float[12]));
+       float *basebonepose;
        float *in12f = loadmodel->data_poses;
-       float *out12f = basebonepose;
+       float *out12f;
        float *outinv12f = loadmodel->data_baseboneposeinverse;
+       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)
        {
                if (loadmodel->data_bones[i].parent >= 0)
@@ -496,17 +632,17 @@ static void Mod_Alias_CalculateBoundingBox(void)
        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 (frameblend[0].frame = 0;frameblend[0].frame < loadmodel->num_poses;frameblend[0].frame++)
+       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)
@@ -532,7 +668,8 @@ static void Mod_Alias_CalculateBoundingBox(void)
                                radius = dist;
                }
        }
-       Mem_Free(vertex3f);
+       if (vertex3f)
+               Mem_Free(vertex3f);
        radius = sqrt(radius);
        yawradius = sqrt(yawradius);
        loadmodel->yawmins[0] = loadmodel->yawmins[1] = -yawradius;
@@ -548,8 +685,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]);
@@ -559,23 +698,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;
-               loadmodel->AnimateVertices(loadmodel, frameblend, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_normal3f, NULL, NULL);
-               Mod_BuildTextureVectorsFromNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_texcoordtexture2f, loadmodel->surfmesh.data_normal3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_svector3f, loadmodel->surfmesh.data_tvector3f, r_smoothnormals_areaweighting.integer);
+               frameblend[0].subframe = i;
+               loadmodel->AnimateVertices(loadmodel, frameblend, NULL, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_normal3f, NULL, NULL);
+               Mod_BuildTextureVectorsFromNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_texcoordtexture2f, loadmodel->surfmesh.data_normal3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_svector3f, loadmodel->surfmesh.data_tvector3f, r_smoothnormals_areaweighting.integer != 0);
                // encode the svector and tvector in 3 byte format for permanent storage
                for (j = 0;j < loadmodel->surfmesh.num_vertices;j++)
                {
-                       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;
@@ -583,9 +721,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)
@@ -593,51 +728,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++)
-               {
-                       model->AnimateVertices(model, frameblend, vertex3f, NULL, NULL, NULL);
-                       Collision_TraceLineTriangleMeshFloat(trace, start, end, model->surfmesh.num_triangles, model->surfmesh.data_element3i, vertex3f, SUPERCONTENTS_SOLID, 0, surface->texture, segmentmins, segmentmaxs);
-               }
+               VectorAdd(start, boxmins, shiftstart);
+               VectorAdd(end, boxmins, shiftend);
+               Mod_MDLMD2MD3_TraceLine(model, frameblend, skeleton, trace, start, end, 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]));
-                       }
-                       model->AnimateVertices(model, frameblend, vertex3f, NULL, NULL, NULL);
-                       Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, model->surfmesh.num_triangles, model->surfmesh.data_element3i, vertex3f, SUPERCONTENTS_SOLID, 0, surface->texture, segmentmins, segmentmaxs);
-               }
+               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)
@@ -746,7 +902,7 @@ static void Mod_BuildAliasSkinFromSkinFrame(texture_t *texture, skinframe_t *ski
        texture->currentmaterialflags = texture->basematerialflags;
 }
 
-static void Mod_BuildAliasSkinsFromSkinFiles(texture_t *skin, skinfile_t *skinfile, char *meshname, char *shadername)
+static void Mod_BuildAliasSkinsFromSkinFiles(texture_t *skin, skinfile_t *skinfile, const char *meshname, const char *shadername)
 {
        int i;
        skinfileitem_t *skinfileitem;
@@ -770,7 +926,7 @@ 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;
                        }
                }
        }
@@ -780,7 +936,7 @@ static void Mod_BuildAliasSkinsFromSkinFiles(texture_t *skin, skinfile_t *skinfi
 
 #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;
@@ -821,18 +977,21 @@ void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
        loadmodel->Draw = R_Q1BSP_Draw;
        loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
        loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
+       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);
@@ -976,6 +1135,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);
@@ -990,12 +1156,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++)
@@ -1008,6 +1174,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++)
@@ -1038,7 +1208,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;
@@ -1047,9 +1217,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;
@@ -1101,7 +1271,7 @@ void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
        loadmodel->surfmesh.isanimated = loadmodel->numframes > 1 || loadmodel->animscenes[0].framecount > 1;
 }
 
-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;
@@ -1139,10 +1309,13 @@ void Mod_IDP2_Load(model_t *mod, void *buffer, void *bufferend)
        loadmodel->Draw = R_Q1BSP_Draw;
        loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
        loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
+       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)
@@ -1182,8 +1355,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]);
@@ -1293,6 +1466,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++)
@@ -1338,7 +1518,7 @@ void Mod_IDP2_Load(model_t *mod, void *buffer, void *bufferend)
        loadmodel->surfmesh.isanimated = loadmodel->numframes > 1 || loadmodel->animscenes[0].framecount > 1;
 }
 
-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;
@@ -1371,10 +1551,13 @@ void Mod_IDP3_Load(model_t *mod, void *buffer, void *bufferend)
        loadmodel->Draw = R_Q1BSP_Draw;
        loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
        loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
+       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)
@@ -1436,9 +1619,9 @@ 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;
@@ -1448,6 +1631,8 @@ void Mod_IDP3_Load(model_t *mod, void *buffer, void *bufferend)
        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;
@@ -1455,7 +1640,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;
@@ -1490,16 +1675,20 @@ 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->animscenes && loadmodel->animscenes[0].framecount > 1);
 }
 
-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;
@@ -1577,10 +1766,13 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        loadmodel->Draw = R_Q1BSP_Draw;
        loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
        loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
+       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 +1836,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 +1857,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 +1865,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(int[4]) + meshvertices * sizeof(float[4]) + loadmodel->num_poses * loadmodel->num_bones * sizeof(float[12]) + 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;
@@ -1688,8 +1880,10 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        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_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]);
+       if (loadmodel->surfmesh.num_vertices <= 65536)
+               loadmodel->surfmesh.data_element3s = (unsigned short *)data;data += loadmodel->surfmesh.num_triangles * sizeof(unsigned short[3]);
 
        //zymlump_t lump_poses; // float pose[numposes][numbones][3][4]; // animation data
        poses = (float *) (pheader->lump_poses.start + pbase);
@@ -1766,7 +1960,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 +1996,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);
@@ -1813,7 +2011,7 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        loadmodel->surfmesh.isanimated = loadmodel->numframes > 1 || loadmodel->animscenes[0].framecount > 1;
 }
 
-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;
@@ -1872,10 +2070,13 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        loadmodel->Draw = R_Q1BSP_Draw;
        loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
        loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
+       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
@@ -1904,21 +2105,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(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));
        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;
@@ -1931,11 +2132,13 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        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_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);
+       if (meshvertices <= 65536)
+               loadmodel->surfmesh.data_element3s = (unsigned short *)data;data += meshtriangles * sizeof(unsigned short[3]);
 
        for (i = 0;i < loadmodel->numskins;i++)
        {
@@ -1998,7 +2201,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;
@@ -2099,8 +2302,12 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        }
        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);
@@ -2110,7 +2317,7 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
 
 // 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;
@@ -2129,6 +2336,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        pskchunk_t *pchunk;
        skinfile_t *skinfiles;
        char animname[MAX_QPATH];
+       size_t size;
 
        pchunk = (pskchunk_t *)buffer;
        if (strcmp(pchunk->id, "ACTRHEAD"))
@@ -2143,10 +2351,13 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        loadmodel->Draw = R_Q1BSP_Draw;
        loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
        loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
+       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;
 
@@ -2491,31 +2702,34 @@ 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(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);
+       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->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);
+       if (loadmodel->surfmesh.num_vertices <= 65536)
+               loadmodel->surfmesh.data_element3s = (unsigned short *)data;data += loadmodel->surfmesh.num_triangles * sizeof(unsigned short[3]);
 
        for (i = 0;i < loadmodel->numskins;i++)
        {
@@ -2530,7 +2744,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;
@@ -2637,9 +2851,13 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        }
        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);
@@ -2649,4 +2867,3 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
 
        loadmodel->surfmesh.isanimated = loadmodel->numframes > 1 || loadmodel->animscenes[0].framecount > 1;
 }
-