]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - model_alias.c
eliminated model->meshlist, replaced with an embedded model->surfmesh to cut down...
[xonotic/darkplaces.git] / model_alias.c
index f14739e07cafab52d1f3e754ed2834c98c903d44..b5d29195f93690dcb2e199c42ea6a9f872d5248c 100644 (file)
@@ -22,12 +22,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "image.h"
 #include "r_shadow.h"
 
-cvar_t r_skeletal_debugbone = {0, "r_skeletal_debugbone", "-1"};
-cvar_t r_skeletal_debugbonecomponent = {0, "r_skeletal_debugbonecomponent", "3"};
-cvar_t r_skeletal_debugbonevalue = {0, "r_skeletal_debugbonevalue", "100"};
-cvar_t r_skeletal_debugtranslatex = {0, "r_skeletal_debugtranslatex", "1"};
-cvar_t r_skeletal_debugtranslatey = {0, "r_skeletal_debugtranslatey", "1"};
-cvar_t r_skeletal_debugtranslatez = {0, "r_skeletal_debugtranslatez", "1"};
+cvar_t r_skeletal_debugbone = {0, "r_skeletal_debugbone", "-1", "development cvar for testing skeletal model code"};
+cvar_t r_skeletal_debugbonecomponent = {0, "r_skeletal_debugbonecomponent", "3", "development cvar for testing skeletal model code"};
+cvar_t r_skeletal_debugbonevalue = {0, "r_skeletal_debugbonevalue", "100", "development cvar for testing skeletal model code"};
+cvar_t r_skeletal_debugtranslatex = {0, "r_skeletal_debugtranslatex", "1", "development cvar for testing skeletal model code"};
+cvar_t r_skeletal_debugtranslatey = {0, "r_skeletal_debugtranslatey", "1", "development cvar for testing skeletal model code"};
+cvar_t r_skeletal_debugtranslatez = {0, "r_skeletal_debugtranslatez", "1", "development cvar for testing skeletal model code"};
 
 void Mod_AliasInit (void)
 {
@@ -39,9 +39,9 @@ void Mod_AliasInit (void)
        Cvar_RegisterVariable(&r_skeletal_debugtranslatez);
 }
 
-void Mod_Alias_GetMesh_Vertex3f(const model_t *model, const frameblend_t *frameblend, const surfmesh_t *mesh, float *out3f)
+void Mod_Alias_GetMesh_Vertex3f(const model_t *model, const frameblend_t *frameblend, float *out3f)
 {
-       if (mesh->num_vertexboneweights)
+       if (model->surfmesh.num_vertexboneweights)
        {
                int i, k, blends;
                surfmeshvertexboneweight_t *v;
@@ -70,9 +70,9 @@ void Mod_Alias_GetMesh_Vertex3f(const model_t *model, const frameblend_t *frameb
                                        bonepose[i][k] = m[k];
                }
                // blend the vertex bone weights
-               memset(out3f, 0, mesh->num_vertices * sizeof(float[3]));
-               v = mesh->data_vertexboneweights;
-               for (i = 0;i < mesh->num_vertexboneweights;i++, v++)
+               memset(out3f, 0, model->surfmesh.num_vertices * sizeof(float[3]));
+               v = model->surfmesh.data_vertexboneweights;
+               for (i = 0;i < model->surfmesh.num_vertexboneweights;i++, v++)
                {
                        out = out3f + v->vertexindex * 3;
                        matrix = bonepose[v->boneindex];
@@ -88,10 +88,10 @@ void Mod_Alias_GetMesh_Vertex3f(const model_t *model, const frameblend_t *frameb
                float lerp1, lerp2, lerp3, lerp4;
                const float *vertsbase, *verts1, *verts2, *verts3, *verts4;
                // vertex morph
-               if (!mesh->data_morphvertex3f)
-                       Host_Error("model %s has no skeletal or vertex morph animation data\n", model->name);
-               vertsbase = mesh->data_morphvertex3f;
-               vertcount = mesh->num_vertices;
+               if (!model->surfmesh.data_morphvertex3f)
+                       Host_Error("model %s has no skeletal or vertex morph animation data", model->name);
+               vertsbase = model->surfmesh.data_morphvertex3f;
+               vertcount = model->surfmesh.num_vertices;
                verts1 = vertsbase + frameblend[0].frame * vertcount * 3;
                lerp1 = frameblend[0].lerp;
                if (frameblend[1].lerp)
@@ -126,7 +126,7 @@ int Mod_Alias_GetTagMatrix(const model_t *model, int poseframe, int tagindex, ma
 {
        const float *boneframe;
        float tempbonematrix[12], bonematrix[12];
-       Matrix4x4_CreateIdentity(outmatrix);
+       *outmatrix = identitymatrix;
        if (model->num_bones)
        {
                if (tagindex < 0 || tagindex >= model->num_bones)
@@ -187,87 +187,109 @@ int Mod_Alias_GetTagIndexForName(const model_t *model, unsigned int skin, const
        return 0;
 }
 
-static void Mod_Alias_Mesh_CompileFrameZero(surfmesh_t *mesh)
+static void Mod_Alias_Mesh_CompileFrameZero(void)
 {
        frameblend_t frameblend[4] = {{0, 1}, {0, 0}, {0, 0}, {0, 0}};
-       mesh->data_vertex3f = (float *)Mem_Alloc(loadmodel->mempool, mesh->num_vertices * sizeof(float[3][4]));
-       mesh->data_svector3f = mesh->data_vertex3f + mesh->num_vertices * 3;
-       mesh->data_tvector3f = mesh->data_vertex3f + mesh->num_vertices * 6;
-       mesh->data_normal3f = mesh->data_vertex3f + mesh->num_vertices * 9;
-       Mod_Alias_GetMesh_Vertex3f(loadmodel, frameblend, mesh, mesh->data_vertex3f);
-       Mod_BuildTextureVectorsAndNormals(0, mesh->num_vertices, mesh->num_triangles, mesh->data_vertex3f, mesh->data_texcoordtexture2f, mesh->data_element3i, mesh->data_svector3f, mesh->data_tvector3f, mesh->data_normal3f, true);
+       loadmodel->surfmesh.data_vertex3f = (float *)Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_vertices * sizeof(float[3][4]));
+       loadmodel->surfmesh.data_svector3f = loadmodel->surfmesh.data_vertex3f + loadmodel->surfmesh.num_vertices * 3;
+       loadmodel->surfmesh.data_tvector3f = loadmodel->surfmesh.data_vertex3f + loadmodel->surfmesh.num_vertices * 6;
+       loadmodel->surfmesh.data_normal3f = loadmodel->surfmesh.data_vertex3f + loadmodel->surfmesh.num_vertices * 9;
+       Mod_Alias_GetMesh_Vertex3f(loadmodel, frameblend, loadmodel->surfmesh.data_vertex3f);
+       Mod_BuildTextureVectorsAndNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_texcoordtexture2f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_svector3f, loadmodel->surfmesh.data_tvector3f, loadmodel->surfmesh.data_normal3f, true);
 }
 
-static void Mod_MDLMD2MD3_TraceBox(model_t *model, int frame, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask)
+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)
 {
-       int i, linetrace;
+       int i;
        float segmentmins[3], segmentmaxs[3];
        frameblend_t frameblend[4];
        msurface_t *surface;
-       surfmesh_t *mesh;
-       colbrushf_t *thisbrush_start = NULL, *thisbrush_end = NULL;
-       matrix4x4_t startmatrix, endmatrix;
+       static int maxvertices = 0;
+       static float *vertex3f = NULL;
        memset(trace, 0, sizeof(*trace));
        trace->fraction = 1;
        trace->realfraction = 1;
        trace->hitsupercontentsmask = hitsupercontentsmask;
-       segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
-       segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
-       segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
-       segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
-       segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
-       segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
-       linetrace = VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs);
-       if (!linetrace)
-       {
-               // box trace, performed as brush trace
-               Matrix4x4_CreateIdentity(&startmatrix);
-               Matrix4x4_CreateIdentity(&endmatrix);
-               thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
-               thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
-       }
        memset(frameblend, 0, sizeof(frameblend));
        frameblend[0].frame = frame;
        frameblend[0].lerp = 1;
-       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 = Z_Malloc(maxvertices * sizeof(float[3]));
+       }
+       if (VectorLength2(boxmins) + VectorLength2(boxmaxs) == 0)
+       {
+               // line trace
+               segmentmins[0] = min(start[0], end[0]) - 1;
+               segmentmins[1] = min(start[1], end[1]) - 1;
+               segmentmins[2] = min(start[2], end[2]) - 1;
+               segmentmaxs[0] = max(start[0], end[0]) + 1;
+               segmentmaxs[1] = max(start[1], end[1]) + 1;
+               segmentmaxs[2] = max(start[2], end[2]) + 1;
+               for (i = 0, surface = model->data_surfaces;i < model->num_surfaces;i++, surface++)
+               {
+                       Mod_Alias_GetMesh_Vertex3f(model, frameblend, vertex3f);
+                       Collision_TraceLineTriangleMeshFloat(trace, start, end, model->surfmesh.num_triangles, model->surfmesh.data_element3i, vertex3f, SUPERCONTENTS_SOLID, 0, surface->texture, segmentmins, segmentmaxs);
+               }
+       }
+       else
        {
-               mesh = surface->groupmesh;
-               Mod_Alias_GetMesh_Vertex3f(model, frameblend, mesh, varray_vertex3f);
-               if (linetrace)
-                       Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, mesh->num_triangles, mesh->data_element3i, varray_vertex3f, SUPERCONTENTS_SOLID, segmentmins, segmentmaxs);
-               else
-                       Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, mesh->num_triangles, mesh->data_element3i, varray_vertex3f, SUPERCONTENTS_SOLID, segmentmins, segmentmaxs);
+               // 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 = Z_Malloc(maxvertices * sizeof(float[3]));
+                       }
+                       Mod_Alias_GetMesh_Vertex3f(model, frameblend, vertex3f);
+                       Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, model->surfmesh.num_triangles, model->surfmesh.data_element3i, vertex3f, SUPERCONTENTS_SOLID, 0, surface->texture, segmentmins, segmentmaxs);
+               }
        }
 }
 
 static void Mod_CalcAliasModelBBoxes (void)
 {
-       int vnum, meshnum;
+       int vnum;
        float dist, yawradius, radius;
-       surfmesh_t *mesh;
        float *v;
        VectorClear(loadmodel->normalmins);
        VectorClear(loadmodel->normalmaxs);
        yawradius = 0;
        radius = 0;
-       for (meshnum = 0;meshnum < loadmodel->num_surfaces;meshnum++)
-       {
-               mesh = loadmodel->meshlist[meshnum];
-               for (vnum = 0, v = mesh->data_morphvertex3f;vnum < mesh->num_vertices * mesh->num_morphframes;vnum++, v += 3)
-               {
-                       if (loadmodel->normalmins[0] > v[0]) loadmodel->normalmins[0] = v[0];
-                       if (loadmodel->normalmins[1] > v[1]) loadmodel->normalmins[1] = v[1];
-                       if (loadmodel->normalmins[2] > v[2]) loadmodel->normalmins[2] = v[2];
-                       if (loadmodel->normalmaxs[0] < v[0]) loadmodel->normalmaxs[0] = v[0];
-                       if (loadmodel->normalmaxs[1] < v[1]) loadmodel->normalmaxs[1] = v[1];
-                       if (loadmodel->normalmaxs[2] < v[2]) loadmodel->normalmaxs[2] = v[2];
-                       dist = v[0] * v[0] + v[1] * v[1];
-                       if (yawradius < dist)
-                               yawradius = dist;
-                       dist += v[2] * v[2];
-                       if (radius < dist)
-                               radius = dist;
-               }
+       for (vnum = 0, v = loadmodel->surfmesh.data_morphvertex3f;vnum < loadmodel->surfmesh.num_vertices * loadmodel->surfmesh.num_morphframes;vnum++, v += 3)
+       {
+               if (loadmodel->normalmins[0] > v[0]) loadmodel->normalmins[0] = v[0];
+               if (loadmodel->normalmins[1] > v[1]) loadmodel->normalmins[1] = v[1];
+               if (loadmodel->normalmins[2] > v[2]) loadmodel->normalmins[2] = v[2];
+               if (loadmodel->normalmaxs[0] < v[0]) loadmodel->normalmaxs[0] = v[0];
+               if (loadmodel->normalmaxs[1] < v[1]) loadmodel->normalmaxs[1] = v[1];
+               if (loadmodel->normalmaxs[2] < v[2]) loadmodel->normalmaxs[2] = v[2];
+               dist = v[0] * v[0] + v[1] * v[1];
+               if (yawradius < dist)
+                       yawradius = dist;
+               dist += v[2] * v[2];
+               if (radius < dist)
+                       radius = dist;
        }
        radius = sqrt(radius);
        yawradius = sqrt(yawradius);
@@ -301,7 +323,7 @@ static void Mod_ConvertAliasVerts (int inverts, vec3_t scale, vec3_t translate,
        }
 }
 
-static void Mod_MDL_LoadFrames (qbyte* datapointer, int inverts, vec3_t scale, vec3_t translate, int *vertremap)
+static void Mod_MDL_LoadFrames (unsigned char* datapointer, int inverts, vec3_t scale, vec3_t translate, int *vertremap)
 {
        int i, f, pose, groupframes;
        float interval;
@@ -356,7 +378,7 @@ static void Mod_MDL_LoadFrames (qbyte* datapointer, int inverts, vec3_t scale, v
                {
                        pinframe = (daliasframe_t *)datapointer;
                        datapointer += sizeof(daliasframe_t);
-                       Mod_ConvertAliasVerts(inverts, scale, translate, (trivertx_t *)datapointer, loadmodel->meshlist[0]->data_morphvertex3f + pose * loadmodel->meshlist[0]->num_vertices * 3, vertremap);
+                       Mod_ConvertAliasVerts(inverts, scale, translate, (trivertx_t *)datapointer, loadmodel->surfmesh.data_morphvertex3f + pose * loadmodel->surfmesh.num_vertices * 3, vertremap);
                        datapointer += sizeof(trivertx_t) * inverts;
                        pose++;
                }
@@ -432,14 +454,14 @@ 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)\n", 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)\n", loadmodel->name, VALUE, MIN, MAX);
+#define BOUNDI(VALUE,MIN,MAX) if (VALUE < MIN || VALUE >= MAX) Host_Error("model %s has an invalid ##VALUE (%d exceeds %d - %d)", loadmodel->name, VALUE, MIN, MAX);
+#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)
 {
        int i, j, version, totalskins, skinwidth, skinheight, groupframes, groupskins, numverts;
        float scales, scalet, scale[3], translate[3], interval;
        msurface_t *surface;
-       qbyte *data;
+       unsigned char *data;
        mdl_t *pinmodel;
        stvert_t *pinstverts;
        dtriangle_t *pintriangles;
@@ -448,7 +470,7 @@ void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
        daliasskininterval_t *pinskinintervals;
        daliasframetype_t *pinframetype;
        daliasgroup_t *pinframegroup;
-       qbyte *datapointer, *startframes, *startskins;
+       unsigned char *datapointer, *startframes, *startskins;
        char name[MAX_QPATH];
        skinframe_t tempskinframe;
        animscene_t *tempskinscenes;
@@ -457,7 +479,7 @@ void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
        int *vertonseam, *vertremap;
        skinfile_t *skinfiles;
 
-       datapointer = (qbyte *)buffer;
+       datapointer = (unsigned char *)buffer;
        pinmodel = (mdl_t *)datapointer;
        datapointer += sizeof(mdl_t);
 
@@ -476,16 +498,10 @@ void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
 
        loadmodel->num_surfaces = 1;
        loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
-       loadmodel->nummeshes = loadmodel->num_surfaces;
-       data = (qbyte *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->nummeshes * sizeof(surfmesh_t *) + loadmodel->nummeshes * sizeof(surfmesh_t));
+       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->meshlist = (surfmesh_t **)data;data += loadmodel->num_surfaces * sizeof(surfmesh_t *);
-       for (i = 0;i < loadmodel->num_surfaces;i++)
-       {
-               loadmodel->surfacelist[i] = i;
-               loadmodel->meshlist[i] = (surfmesh_t *)data;data += sizeof(surfmesh_t);
-       }
+       loadmodel->surfacelist[0] = 0;
 
        loadmodel->numskins = LittleLong(pinmodel->numskins);
        BOUNDI(loadmodel->numskins,0,65536);
@@ -495,11 +511,11 @@ void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
        BOUNDI(skinheight,0,65536);
        numverts = LittleLong(pinmodel->numverts);
        BOUNDI(numverts,0,65536);
-       loadmodel->meshlist[0]->num_triangles = LittleLong(pinmodel->numtris);
-       BOUNDI(loadmodel->meshlist[0]->num_triangles,0,65536);
+       loadmodel->surfmesh.num_triangles = LittleLong(pinmodel->numtris);
+       BOUNDI(loadmodel->surfmesh.num_triangles,0,65536);
        loadmodel->numframes = LittleLong(pinmodel->numframes);
        BOUNDI(loadmodel->numframes,0,65536);
-       loadmodel->synctype = LittleLong (pinmodel->synctype);
+       loadmodel->synctype = (synctype_t)LittleLong (pinmodel->synctype);
        BOUNDI(loadmodel->synctype,0,2);
        loadmodel->flags = LittleLong (pinmodel->flags);
 
@@ -536,10 +552,10 @@ void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
        datapointer += sizeof(stvert_t) * numverts;
 
        pintriangles = (dtriangle_t *)datapointer;
-       datapointer += sizeof(dtriangle_t) * loadmodel->meshlist[0]->num_triangles;
+       datapointer += sizeof(dtriangle_t) * loadmodel->surfmesh.num_triangles;
 
        startframes = datapointer;
-       loadmodel->meshlist[0]->num_morphframes = 0;
+       loadmodel->surfmesh.num_morphframes = 0;
        for (i = 0;i < loadmodel->numframes;i++)
        {
                pinframetype = (daliasframetype_t *)datapointer;
@@ -558,7 +574,7 @@ void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
                {
                        datapointer += sizeof(daliasframe_t);
                        datapointer += sizeof(trivertx_t) * numverts;
-                       loadmodel->meshlist[0]->num_morphframes++;
+                       loadmodel->surfmesh.num_morphframes++;
                }
        }
 
@@ -580,60 +596,60 @@ void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
        }
 
 // load triangle data
-       loadmodel->meshlist[0]->data_element3i = (int *)Mem_Alloc(loadmodel->mempool, sizeof(int[3]) * loadmodel->meshlist[0]->num_triangles);
+       loadmodel->surfmesh.data_element3i = (int *)Mem_Alloc(loadmodel->mempool, sizeof(int[3]) * loadmodel->surfmesh.num_triangles);
 
        // read the triangle elements
-       for (i = 0;i < loadmodel->meshlist[0]->num_triangles;i++)
+       for (i = 0;i < loadmodel->surfmesh.num_triangles;i++)
                for (j = 0;j < 3;j++)
-                       loadmodel->meshlist[0]->data_element3i[i*3+j] = LittleLong(pintriangles[i].vertindex[j]);
+                       loadmodel->surfmesh.data_element3i[i*3+j] = LittleLong(pintriangles[i].vertindex[j]);
        // validate (note numverts is used because this is the original data)
-       Mod_ValidateElements(loadmodel->meshlist[0]->data_element3i, loadmodel->meshlist[0]->num_triangles, numverts, __FILE__, __LINE__);
+       Mod_ValidateElements(loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles, 0, numverts, __FILE__, __LINE__);
        // now butcher the elements according to vertonseam and tri->facesfront
        // and then compact the vertex set to remove duplicates
-       for (i = 0;i < loadmodel->meshlist[0]->num_triangles;i++)
+       for (i = 0;i < loadmodel->surfmesh.num_triangles;i++)
                if (!LittleLong(pintriangles[i].facesfront)) // backface
                        for (j = 0;j < 3;j++)
-                               if (vertonseam[loadmodel->meshlist[0]->data_element3i[i*3+j]])
-                                       loadmodel->meshlist[0]->data_element3i[i*3+j] += numverts;
+                               if (vertonseam[loadmodel->surfmesh.data_element3i[i*3+j]])
+                                       loadmodel->surfmesh.data_element3i[i*3+j] += numverts;
        // count the usage
        // (this uses vertremap to count usage to save some memory)
        for (i = 0;i < numverts*2;i++)
                vertremap[i] = 0;
-       for (i = 0;i < loadmodel->meshlist[0]->num_triangles*3;i++)
-               vertremap[loadmodel->meshlist[0]->data_element3i[i]]++;
+       for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
+               vertremap[loadmodel->surfmesh.data_element3i[i]]++;
        // build remapping table and compact array
-       loadmodel->meshlist[0]->num_vertices = 0;
+       loadmodel->surfmesh.num_vertices = 0;
        for (i = 0;i < numverts*2;i++)
        {
                if (vertremap[i])
                {
-                       vertremap[i] = loadmodel->meshlist[0]->num_vertices;
-                       vertst[loadmodel->meshlist[0]->num_vertices*2+0] = vertst[i*2+0];
-                       vertst[loadmodel->meshlist[0]->num_vertices*2+1] = vertst[i*2+1];
-                       loadmodel->meshlist[0]->num_vertices++;
+                       vertremap[i] = loadmodel->surfmesh.num_vertices;
+                       vertst[loadmodel->surfmesh.num_vertices*2+0] = vertst[i*2+0];
+                       vertst[loadmodel->surfmesh.num_vertices*2+1] = vertst[i*2+1];
+                       loadmodel->surfmesh.num_vertices++;
                }
                else
                        vertremap[i] = -1; // not used at all
        }
        // remap the elements to the new vertex set
-       for (i = 0;i < loadmodel->meshlist[0]->num_triangles * 3;i++)
-               loadmodel->meshlist[0]->data_element3i[i] = vertremap[loadmodel->meshlist[0]->data_element3i[i]];
+       for (i = 0;i < loadmodel->surfmesh.num_triangles * 3;i++)
+               loadmodel->surfmesh.data_element3i[i] = vertremap[loadmodel->surfmesh.data_element3i[i]];
        // store the texture coordinates
-       loadmodel->meshlist[0]->data_texcoordtexture2f = (float *)Mem_Alloc(loadmodel->mempool, sizeof(float[2]) * loadmodel->meshlist[0]->num_vertices);
-       for (i = 0;i < loadmodel->meshlist[0]->num_vertices;i++)
+       loadmodel->surfmesh.data_texcoordtexture2f = (float *)Mem_Alloc(loadmodel->mempool, sizeof(float[2]) * loadmodel->surfmesh.num_vertices);
+       for (i = 0;i < loadmodel->surfmesh.num_vertices;i++)
        {
-               loadmodel->meshlist[0]->data_texcoordtexture2f[i*2+0] = vertst[i*2+0];
-               loadmodel->meshlist[0]->data_texcoordtexture2f[i*2+1] = vertst[i*2+1];
+               loadmodel->surfmesh.data_texcoordtexture2f[i*2+0] = vertst[i*2+0];
+               loadmodel->surfmesh.data_texcoordtexture2f[i*2+1] = vertst[i*2+1];
        }
 
 // load the frames
        loadmodel->animscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, sizeof(animscene_t) * loadmodel->numframes);
-       loadmodel->meshlist[0]->data_morphvertex3f = (float *)Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * loadmodel->meshlist[0]->num_morphframes * loadmodel->meshlist[0]->num_vertices);
-       loadmodel->meshlist[0]->data_neighbor3i = (int *)Mem_Alloc(loadmodel->mempool, loadmodel->meshlist[0]->num_triangles * sizeof(int[3]));
+       loadmodel->surfmesh.data_morphvertex3f = (float *)Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * loadmodel->surfmesh.num_morphframes * loadmodel->surfmesh.num_vertices);
+       loadmodel->surfmesh.data_neighbor3i = (int *)Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_triangles * sizeof(int[3]));
        Mod_MDL_LoadFrames (startframes, numverts, scale, translate, vertremap);
-       Mod_BuildTriangleNeighbors(loadmodel->meshlist[0]->data_neighbor3i, loadmodel->meshlist[0]->data_element3i, loadmodel->meshlist[0]->num_triangles);
+       Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
        Mod_CalcAliasModelBBoxes();
-       Mod_Alias_Mesh_CompileFrameZero(loadmodel->meshlist[0]);
+       Mod_Alias_Mesh_CompileFrameZero();
 
        Mem_Free(vertst);
        Mem_Free(vertremap);
@@ -700,7 +716,7 @@ void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
                                else
                                        sprintf (name, "%s_%i", loadmodel->name, i);
                                if (!Mod_LoadSkinFrame(&tempskinframe, name, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PICMIP, true, true))
-                                       Mod_LoadSkinFrame_Internal(&tempskinframe, name, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PICMIP, true, r_fullbrights.integer, (qbyte *)datapointer, skinwidth, skinheight);
+                                       Mod_LoadSkinFrame_Internal(&tempskinframe, name, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_PICMIP, true, r_fullbrights.integer, (unsigned char *)datapointer, skinwidth, skinheight, 8, NULL, NULL);
                                Mod_BuildAliasSkinFromSkinFrame(loadmodel->data_textures + totalskins * loadmodel->num_surfaces, &tempskinframe);
                                datapointer += skinwidth * skinheight;
                                totalskins++;
@@ -737,11 +753,10 @@ void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
 
        surface = loadmodel->data_surfaces;
        surface->texture = loadmodel->data_textures;
-       surface->groupmesh = loadmodel->meshlist[0];
        surface->num_firsttriangle = 0;
-       surface->num_triangles = surface->groupmesh->num_triangles;
+       surface->num_triangles = loadmodel->surfmesh.num_triangles;
        surface->num_firstvertex = 0;
-       surface->num_vertices = surface->groupmesh->num_vertices;
+       surface->num_vertices = loadmodel->surfmesh.num_vertices;
 }
 
 static void Mod_MD2_ConvertVerts (vec3_t scale, vec3_t translate, trivertx_t *v, float *out3f, int numverts, int *vertremap)
@@ -761,10 +776,10 @@ void Mod_IDP2_Load(model_t *mod, void *buffer, void *bufferend)
 {
        int i, j, k, hashindex, num, numxyz, numst, xyz, st, skinwidth, skinheight, *vertremap, version, end, numverts;
        float *stverts, s, t, scale[3], translate[3];
-       qbyte *data;
+       unsigned char *data;
        msurface_t *surface;
        md2_t *pinmodel;
-       qbyte *base, *datapointer;
+       unsigned char *base, *datapointer;
        md2frame_t *pinframe;
        char *inskin;
        md2triangle_t *intri;
@@ -780,7 +795,7 @@ void Mod_IDP2_Load(model_t *mod, void *buffer, void *bufferend)
        skinfile_t *skinfiles;
 
        pinmodel = (md2_t *)buffer;
-       base = (qbyte *)buffer;
+       base = (unsigned char *)buffer;
 
        version = LittleLong (pinmodel->version);
        if (version != MD2ALIAS_VERSION)
@@ -818,23 +833,17 @@ void Mod_IDP2_Load(model_t *mod, void *buffer, void *bufferend)
 
        loadmodel->num_surfaces = 1;
        loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
-       loadmodel->nummeshes = loadmodel->num_surfaces;
-       data = (qbyte *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->nummeshes * sizeof(surfmesh_t *) + loadmodel->nummeshes * sizeof(surfmesh_t));
+       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->meshlist = (surfmesh_t **)data;data += loadmodel->num_surfaces * sizeof(surfmesh_t *);
-       for (i = 0;i < loadmodel->num_surfaces;i++)
-       {
-               loadmodel->surfacelist[i] = i;
-               loadmodel->meshlist[i] = (surfmesh_t *)data;data += sizeof(surfmesh_t);
-       }
+       loadmodel->surfacelist[0] = 0;
 
        loadmodel->numskins = LittleLong(pinmodel->num_skins);
        numxyz = LittleLong(pinmodel->num_xyz);
        numst = LittleLong(pinmodel->num_st);
-       loadmodel->meshlist[0]->num_triangles = LittleLong(pinmodel->num_tris);
+       loadmodel->surfmesh.num_triangles = LittleLong(pinmodel->num_tris);
        loadmodel->numframes = LittleLong(pinmodel->num_frames);
-       loadmodel->meshlist[0]->num_morphframes = loadmodel->numframes;
+       loadmodel->surfmesh.num_morphframes = loadmodel->numframes;
        loadmodel->animscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, loadmodel->numframes * sizeof(animscene_t));
 
        loadmodel->flags = 0; // there are no MD2 flags
@@ -908,11 +917,11 @@ void Mod_IDP2_Load(model_t *mod, void *buffer, void *bufferend)
        }
 
        md2verthash = (struct md2verthash_s **)Mem_Alloc(tempmempool, 256 * sizeof(hash));
-       md2verthashdata = (struct md2verthash_s *)Mem_Alloc(tempmempool, loadmodel->meshlist[0]->num_triangles * 3 * sizeof(*hash));
+       md2verthashdata = (struct md2verthash_s *)Mem_Alloc(tempmempool, loadmodel->surfmesh.num_triangles * 3 * sizeof(*hash));
        // swap the triangle list
        num = 0;
-       loadmodel->meshlist[0]->data_element3i = (int *)Mem_Alloc(loadmodel->mempool, loadmodel->meshlist[0]->num_triangles * sizeof(int[3]));
-       for (i = 0;i < loadmodel->meshlist[0]->num_triangles;i++)
+       loadmodel->surfmesh.data_element3i = (int *)Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_triangles * sizeof(int[3]));
+       for (i = 0;i < loadmodel->surfmesh.num_triangles;i++)
        {
                for (j = 0;j < 3;j++)
                {
@@ -943,22 +952,22 @@ void Mod_IDP2_Load(model_t *mod, void *buffer, void *bufferend)
                                hash->next = md2verthash[hashindex];
                                md2verthash[hashindex] = hash;
                        }
-                       loadmodel->meshlist[0]->data_element3i[i*3+j] = (hash - md2verthashdata);
+                       loadmodel->surfmesh.data_element3i[i*3+j] = (hash - md2verthashdata);
                }
        }
 
        Mem_Free(stverts);
 
        numverts = num;
-       loadmodel->meshlist[0]->num_vertices = numverts;
+       loadmodel->surfmesh.num_vertices = numverts;
        vertremap = (int *)Mem_Alloc(loadmodel->mempool, num * sizeof(int));
-       loadmodel->meshlist[0]->data_texcoordtexture2f = (float *)Mem_Alloc(loadmodel->mempool, num * sizeof(float[2]));
+       loadmodel->surfmesh.data_texcoordtexture2f = (float *)Mem_Alloc(loadmodel->mempool, num * sizeof(float[2]));
        for (i = 0;i < num;i++)
        {
                hash = md2verthashdata + i;
                vertremap[i] = hash->xyz;
-               loadmodel->meshlist[0]->data_texcoordtexture2f[i*2+0] = hash->st[0];
-               loadmodel->meshlist[0]->data_texcoordtexture2f[i*2+1] = hash->st[1];
+               loadmodel->surfmesh.data_texcoordtexture2f[i*2+0] = hash->st[0];
+               loadmodel->surfmesh.data_texcoordtexture2f[i*2+1] = hash->st[1];
        }
 
        Mem_Free(md2verthash);
@@ -966,8 +975,8 @@ void Mod_IDP2_Load(model_t *mod, void *buffer, void *bufferend)
 
        // load the frames
        datapointer = (base + LittleLong(pinmodel->ofs_frames));
-       loadmodel->meshlist[0]->data_morphvertex3f = (float *)Mem_Alloc(loadmodel->mempool, numverts * loadmodel->meshlist[0]->num_morphframes * sizeof(float[3]));
-       for (i = 0;i < loadmodel->meshlist[0]->num_morphframes;i++)
+       loadmodel->surfmesh.data_morphvertex3f = (float *)Mem_Alloc(loadmodel->mempool, numverts * loadmodel->surfmesh.num_morphframes * sizeof(float[3]));
+       for (i = 0;i < loadmodel->surfmesh.num_morphframes;i++)
        {
                pinframe = (md2frame_t *)datapointer;
                datapointer += sizeof(md2frame_t);
@@ -976,7 +985,7 @@ void Mod_IDP2_Load(model_t *mod, void *buffer, void *bufferend)
                        scale[j] = LittleFloat(pinframe->scale[j]);
                        translate[j] = LittleFloat(pinframe->translate[j]);
                }
-               Mod_MD2_ConvertVerts(scale, translate, (trivertx_t *)datapointer, loadmodel->meshlist[0]->data_morphvertex3f + i * numverts * 3, numverts, vertremap);
+               Mod_MD2_ConvertVerts(scale, translate, (trivertx_t *)datapointer, loadmodel->surfmesh.data_morphvertex3f + i * numverts * 3, numverts, vertremap);
                datapointer += numxyz * sizeof(trivertx_t);
 
                strcpy(loadmodel->animscenes[i].name, pinframe->name);
@@ -988,26 +997,24 @@ void Mod_IDP2_Load(model_t *mod, void *buffer, void *bufferend)
 
        Mem_Free(vertremap);
 
-       loadmodel->meshlist[0]->data_neighbor3i = (int *)Mem_Alloc(loadmodel->mempool, loadmodel->meshlist[0]->num_triangles * sizeof(int[3]));
-       Mod_BuildTriangleNeighbors(loadmodel->meshlist[0]->data_neighbor3i, loadmodel->meshlist[0]->data_element3i, loadmodel->meshlist[0]->num_triangles);
+       loadmodel->surfmesh.data_neighbor3i = (int *)Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_triangles * sizeof(int[3]));
+       Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
        Mod_CalcAliasModelBBoxes();
-       Mod_Alias_Mesh_CompileFrameZero(loadmodel->meshlist[0]);
+       Mod_Alias_Mesh_CompileFrameZero();
 
        surface = loadmodel->data_surfaces;
-       surface->groupmesh = loadmodel->meshlist[0];
        surface->texture = loadmodel->data_textures;
        surface->num_firsttriangle = 0;
-       surface->num_triangles = surface->groupmesh->num_triangles;
+       surface->num_triangles = loadmodel->surfmesh.num_triangles;
        surface->num_firstvertex = 0;
-       surface->num_vertices = surface->groupmesh->num_vertices;
+       surface->num_vertices = loadmodel->surfmesh.num_vertices;
 }
 
 void Mod_IDP3_Load(model_t *mod, void *buffer, void *bufferend)
 {
-       int i, j, k, version;
-       qbyte *data;
+       int i, j, k, version, meshvertices, meshtriangles;
+       unsigned char *data;
        msurface_t *surface;
-       surfmesh_t *mesh;
        md3modelheader_t *pinmodel;
        md3frameinfo_t *pinframe;
        md3mesh_t *pinmesh;
@@ -1017,7 +1024,7 @@ void Mod_IDP3_Load(model_t *mod, void *buffer, void *bufferend)
        pinmodel = (md3modelheader_t *)buffer;
 
        if (memcmp(pinmodel->identifier, "IDP3", 4))
-               Host_Error ("%s is not a MD3 (IDP3) file\n", loadmodel->name);
+               Host_Error ("%s is not a MD3 (IDP3) file", loadmodel->name);
        version = LittleLong (pinmodel->version);
        if (version != MD3VERSION)
                Host_Error ("%s has wrong version number (%i should be %i)",
@@ -1053,7 +1060,7 @@ void Mod_IDP3_Load(model_t *mod, void *buffer, void *bufferend)
 
        // load frameinfo
        loadmodel->animscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, loadmodel->numframes * sizeof(animscene_t));
-       for (i = 0, pinframe = (md3frameinfo_t *)((qbyte *)pinmodel + LittleLong(pinmodel->lump_frameinfo));i < loadmodel->numframes;i++, pinframe++)
+       for (i = 0, pinframe = (md3frameinfo_t *)((unsigned char *)pinmodel + LittleLong(pinmodel->lump_frameinfo));i < loadmodel->numframes;i++, pinframe++)
        {
                strcpy(loadmodel->animscenes[i].name, pinframe->name);
                loadmodel->animscenes[i].firstframe = i;
@@ -1066,10 +1073,10 @@ void Mod_IDP3_Load(model_t *mod, void *buffer, void *bufferend)
        loadmodel->num_tagframes = loadmodel->numframes;
        loadmodel->num_tags = LittleLong(pinmodel->num_tags);
        loadmodel->data_tags = (aliastag_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_tagframes * loadmodel->num_tags * sizeof(aliastag_t));
-       for (i = 0, pintag = (md3tag_t *)((qbyte *)pinmodel + LittleLong(pinmodel->lump_tags));i < loadmodel->num_tagframes * loadmodel->num_tags;i++, pintag++)
+       for (i = 0, pintag = (md3tag_t *)((unsigned char *)pinmodel + LittleLong(pinmodel->lump_tags));i < loadmodel->num_tagframes * loadmodel->num_tags;i++, pintag++)
        {
                strcpy(loadmodel->data_tags[i].name, pintag->name);
-               Matrix4x4_CreateIdentity(&loadmodel->data_tags[i].matrix);
+               loadmodel->data_tags[i].matrix = identitymatrix;
                for (j = 0;j < 3;j++)
                {
                        for (k = 0;k < 3;k++)
@@ -1080,63 +1087,72 @@ void Mod_IDP3_Load(model_t *mod, void *buffer, void *bufferend)
        }
 
        // load meshes
+       meshvertices = 0;
+       meshtriangles = 0;
+       for (i = 0, pinmesh = (md3mesh_t *)((unsigned char *)pinmodel + LittleLong(pinmodel->lump_meshes));i < loadmodel->num_surfaces;i++, pinmesh = (md3mesh_t *)((unsigned char *)pinmesh + LittleLong(pinmesh->lump_end)))
+       {
+               if (memcmp(pinmesh->identifier, "IDP3", 4))
+                       Host_Error("Mod_IDP3_Load: invalid mesh identifier (not IDP3)");
+               if (LittleLong(pinmesh->num_frames) != loadmodel->numframes)
+                       Host_Error("Mod_IDP3_Load: mesh numframes differs from header");
+               meshvertices += LittleLong(pinmesh->num_vertices);
+               meshtriangles += LittleLong(pinmesh->num_triangles);
+       }
+
        loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
-       loadmodel->nummeshes = loadmodel->num_surfaces;
        loadmodel->num_textures = loadmodel->num_surfaces;
-       data = (qbyte *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->nummeshes * sizeof(surfmesh_t *) + loadmodel->nummeshes * sizeof(surfmesh_t) + loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_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 * sizeof(float[2]) + meshvertices * loadmodel->numframes * sizeof(float[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->meshlist = (surfmesh_t **)data;data += loadmodel->num_surfaces * sizeof(surfmesh_t *);
        loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
-       for (i = 0;i < loadmodel->num_surfaces;i++)
-       {
-               loadmodel->surfacelist[i] = i;
-               loadmodel->meshlist[i] = (surfmesh_t *)data;data += sizeof(surfmesh_t);
-       }
-       for (i = 0, pinmesh = (md3mesh_t *)((qbyte *)pinmodel + LittleLong(pinmodel->lump_meshes));i < loadmodel->num_surfaces;i++, pinmesh = (md3mesh_t *)((qbyte *)pinmesh + LittleLong(pinmesh->lump_end)))
+       loadmodel->surfmesh.num_vertices = meshvertices;
+       loadmodel->surfmesh.num_triangles = meshtriangles;
+       loadmodel->surfmesh.num_morphframes = loadmodel->numframes; // TODO: remove?
+       loadmodel->surfmesh.data_element3i = (int *)data;data += meshtriangles * sizeof(int[3]);
+       loadmodel->surfmesh.data_neighbor3i = (int *)data;data += meshtriangles * sizeof(int[3]);
+       loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += meshvertices * sizeof(float[2]);
+       loadmodel->surfmesh.data_morphvertex3f = (float *)data;data += meshvertices * loadmodel->numframes * sizeof(float[3]);
+
+       meshvertices = 0;
+       meshtriangles = 0;
+       for (i = 0, pinmesh = (md3mesh_t *)((unsigned char *)pinmodel + LittleLong(pinmodel->lump_meshes));i < loadmodel->num_surfaces;i++, pinmesh = (md3mesh_t *)((unsigned char *)pinmesh + LittleLong(pinmesh->lump_end)))
        {
                if (memcmp(pinmesh->identifier, "IDP3", 4))
-                       Host_Error("Mod_IDP3_Load: invalid mesh identifier (not IDP3)\n");
-               mesh = loadmodel->meshlist[i];
-               mesh->num_morphframes = LittleLong(pinmesh->num_frames);
-               mesh->num_vertices = LittleLong(pinmesh->num_vertices);
-               mesh->num_triangles = LittleLong(pinmesh->num_triangles);
-               mesh->data_element3i = (int *)Mem_Alloc(loadmodel->mempool, mesh->num_triangles * sizeof(int[3]));
-               mesh->data_neighbor3i = (int *)Mem_Alloc(loadmodel->mempool, mesh->num_triangles * sizeof(int[3]));
-               mesh->data_texcoordtexture2f = (float *)Mem_Alloc(loadmodel->mempool, mesh->num_vertices * sizeof(float[2]));
-               mesh->data_morphvertex3f = (float *)Mem_Alloc(loadmodel->mempool, mesh->num_vertices * mesh->num_morphframes * sizeof(float[3]));
-               for (j = 0;j < mesh->num_triangles * 3;j++)
-                       mesh->data_element3i[j] = LittleLong(((int *)((qbyte *)pinmesh + LittleLong(pinmesh->lump_elements)))[j]);
-               for (j = 0;j < mesh->num_vertices;j++)
+                       Host_Error("Mod_IDP3_Load: invalid mesh identifier (not IDP3)");
+               loadmodel->surfacelist[i] = i;
+               surface = loadmodel->data_surfaces + i;
+               surface->texture = loadmodel->data_textures + i;
+               surface->num_firsttriangle = meshtriangles;
+               surface->num_triangles = LittleLong(pinmesh->num_triangles);
+               surface->num_firstvertex = meshvertices;
+               surface->num_vertices = LittleLong(pinmesh->num_vertices);
+               meshvertices += surface->num_vertices;
+               meshtriangles += surface->num_triangles;
+
+               for (j = 0;j < surface->num_triangles * 3;j++)
+                       loadmodel->surfmesh.data_element3i[j + surface->num_firsttriangle * 3] = surface->num_firstvertex + LittleLong(((int *)((unsigned char *)pinmesh + LittleLong(pinmesh->lump_elements)))[j]);
+               for (j = 0;j < surface->num_vertices;j++)
                {
-                       mesh->data_texcoordtexture2f[j * 2 + 0] = LittleFloat(((float *)((qbyte *)pinmesh + LittleLong(pinmesh->lump_texcoords)))[j * 2 + 0]);
-                       mesh->data_texcoordtexture2f[j * 2 + 1] = LittleFloat(((float *)((qbyte *)pinmesh + LittleLong(pinmesh->lump_texcoords)))[j * 2 + 1]);
+                       loadmodel->surfmesh.data_texcoordtexture2f[(j + surface->num_firstvertex) * 2 + 0] = LittleFloat(((float *)((unsigned char *)pinmesh + LittleLong(pinmesh->lump_texcoords)))[j * 2 + 0]);
+                       loadmodel->surfmesh.data_texcoordtexture2f[(j + surface->num_firstvertex) * 2 + 1] = LittleFloat(((float *)((unsigned char *)pinmesh + LittleLong(pinmesh->lump_texcoords)))[j * 2 + 1]);
                }
-               for (j = 0;j < mesh->num_vertices * mesh->num_morphframes;j++)
+               for (j = 0;j < surface->num_vertices * loadmodel->numframes;j++)
                {
-                       mesh->data_morphvertex3f[j * 3 + 0] = LittleShort(((short *)((qbyte *)pinmesh + LittleLong(pinmesh->lump_framevertices)))[j * 4 + 0]) * (1.0f / 64.0f);
-                       mesh->data_morphvertex3f[j * 3 + 1] = LittleShort(((short *)((qbyte *)pinmesh + LittleLong(pinmesh->lump_framevertices)))[j * 4 + 1]) * (1.0f / 64.0f);
-                       mesh->data_morphvertex3f[j * 3 + 2] = LittleShort(((short *)((qbyte *)pinmesh + LittleLong(pinmesh->lump_framevertices)))[j * 4 + 2]) * (1.0f / 64.0f);
+                       loadmodel->surfmesh.data_morphvertex3f[(j + surface->num_firstvertex) * 3 + 0] = LittleShort(((short *)((unsigned char *)pinmesh + LittleLong(pinmesh->lump_framevertices)))[j * 4 + 0]) * (1.0f / 64.0f);
+                       loadmodel->surfmesh.data_morphvertex3f[(j + surface->num_firstvertex) * 3 + 1] = LittleShort(((short *)((unsigned char *)pinmesh + LittleLong(pinmesh->lump_framevertices)))[j * 4 + 1]) * (1.0f / 64.0f);
+                       loadmodel->surfmesh.data_morphvertex3f[(j + surface->num_firstvertex) * 3 + 2] = LittleShort(((short *)((unsigned char *)pinmesh + LittleLong(pinmesh->lump_framevertices)))[j * 4 + 2]) * (1.0f / 64.0f);
                }
 
-               Mod_ValidateElements(mesh->data_element3i, mesh->num_triangles, mesh->num_vertices, __FILE__, __LINE__);
-               Mod_BuildTriangleNeighbors(mesh->data_neighbor3i, mesh->data_element3i, mesh->num_triangles);
-               Mod_Alias_Mesh_CompileFrameZero(mesh);
-
                if (LittleLong(pinmesh->num_shaders) >= 1)
-                       Mod_BuildAliasSkinsFromSkinFiles(loadmodel->data_textures + i, skinfiles, pinmesh->name, ((md3shader_t *)((qbyte *) pinmesh + LittleLong(pinmesh->lump_shaders)))->name);
+                       Mod_BuildAliasSkinsFromSkinFiles(loadmodel->data_textures + i, skinfiles, pinmesh->name, ((md3shader_t *)((unsigned char *) pinmesh + LittleLong(pinmesh->lump_shaders)))->name);
                else
                        for (j = 0;j < loadmodel->numskins;j++)
                                Mod_BuildAliasSkinFromSkinFrame(loadmodel->data_textures + i + j * loadmodel->num_surfaces, NULL);
 
-               surface = loadmodel->data_surfaces + i;
-               surface->groupmesh = mesh;
-               surface->texture = loadmodel->data_textures + i;
-               surface->num_firsttriangle = 0;
-               surface->num_triangles = mesh->num_triangles;
-               surface->num_firstvertex = 0;
-               surface->num_vertices = mesh->num_vertices;
+               Mod_ValidateElements(loadmodel->surfmesh.data_element3i + surface->num_firsttriangle * 3, surface->num_triangles, surface->num_firstvertex, surface->num_vertices, __FILE__, __LINE__);
        }
+       Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
+       Mod_Alias_Mesh_CompileFrameZero();
        Mod_CalcAliasModelBBoxes();
        Mod_FreeSkinFiles(skinfiles);
 }
@@ -1144,32 +1160,25 @@ void Mod_IDP3_Load(model_t *mod, void *buffer, void *bufferend)
 void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
 {
        zymtype1header_t *pinmodel, *pheader;
-       qbyte *pbase;
-       int i, j, k, l, numposes, *bonecount, *vertbonecounts, count, *renderlist, *renderlistend, *outelements, *remapvertices;
+       unsigned char *pbase;
+       int i, j, k, l, numposes, meshvertices, meshtriangles, numvertexboneweights, *bonecount, *vertbonecounts, count, *renderlist, *renderlistend, *outelements;
        float modelradius, corner[2], *poses, *intexcoord2f, *outtexcoord2f;
        zymvertex_t *verts, *vertdata;
        zymscene_t *scene;
        zymbone_t *bone;
        char *shadername;
        skinfile_t *skinfiles;
-       qbyte *data;
+       unsigned char *data;
        msurface_t *surface;
-       surfmesh_t *mesh;
 
        pinmodel = (zymtype1header_t *)buffer;
-       pbase = (qbyte *)buffer;
+       pbase = (unsigned char *)buffer;
        if (memcmp(pinmodel->id, "ZYMOTICMODEL", 12))
-               Host_Error ("Mod_ZYMOTICMODEL_Load: %s is not a zymotic model\n");
+               Host_Error ("Mod_ZYMOTICMODEL_Load: %s is not a zymotic model");
        if (BigLong(pinmodel->type) != 1)
-               Host_Error ("Mod_ZYMOTICMODEL_Load: only type 1 (skeletal pose) models are currently supported (name = %s)\n", loadmodel->name);
+               Host_Error ("Mod_ZYMOTICMODEL_Load: only type 1 (skeletal pose) models are currently supported (name = %s)", loadmodel->name);
 
        loadmodel->type = mod_alias;
-       loadmodel->DrawSky = NULL;
-       loadmodel->Draw = R_Q1BSP_Draw;
-       loadmodel->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
-       loadmodel->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
-       loadmodel->DrawLight = R_Q1BSP_DrawLight;
-       loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
        loadmodel->flags = 0; // there are no flags on zym models
        loadmodel->synctype = ST_RAND;
 
@@ -1208,6 +1217,24 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        pheader->lump_trizone.start = BigLong(pinmodel->lump_trizone.start);
        pheader->lump_trizone.length = BigLong(pinmodel->lump_trizone.length);
 
+       if (pheader->numtris < 1 || pheader->numverts < 3 || pheader->numshaders < 1)
+       {
+               Con_Printf("%s has no geometry\n");
+               return;
+       }
+       if (pheader->numscenes < 1 || pheader->lump_poses.length < (int)sizeof(float[3][4]))
+       {
+               Con_Printf("%s has no animations\n");
+               return;
+       }
+
+       loadmodel->DrawSky = NULL;
+       loadmodel->Draw = R_Q1BSP_Draw;
+       loadmodel->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
+       loadmodel->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
+       loadmodel->DrawLight = R_Q1BSP_DrawLight;
+       loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
+
        loadmodel->numframes = pheader->numscenes;
        loadmodel->num_surfaces = pheader->numshaders;
 
@@ -1259,21 +1286,14 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                loadmodel->animscenes[i].framerate = BigFloat(scene->framerate);
                loadmodel->animscenes[i].loop = (BigLong(scene->flags) & ZYMSCENEFLAG_NOLOOP) == 0;
                if ((unsigned int) loadmodel->animscenes[i].firstframe >= (unsigned int) numposes)
-                       Host_Error("%s scene->firstframe (%i) >= numposes (%i)\n", loadmodel->name, loadmodel->animscenes[i].firstframe, numposes);
+                       Host_Error("%s scene->firstframe (%i) >= numposes (%i)", loadmodel->name, loadmodel->animscenes[i].firstframe, numposes);
                if ((unsigned int) loadmodel->animscenes[i].firstframe + (unsigned int) loadmodel->animscenes[i].framecount > (unsigned int) numposes)
-                       Host_Error("%s scene->firstframe (%i) + framecount (%i) >= numposes (%i)\n", loadmodel->name, loadmodel->animscenes[i].firstframe, loadmodel->animscenes[i].framecount, numposes);
+                       Host_Error("%s scene->firstframe (%i) + framecount (%i) >= numposes (%i)", loadmodel->name, loadmodel->animscenes[i].firstframe, loadmodel->animscenes[i].framecount, numposes);
                if (loadmodel->animscenes[i].framerate < 0)
-                       Host_Error("%s scene->framerate (%f) < 0\n", loadmodel->name, loadmodel->animscenes[i].framerate);
+                       Host_Error("%s scene->framerate (%f) < 0", loadmodel->name, loadmodel->animscenes[i].framerate);
                scene++;
        }
 
-       //zymlump_t lump_poses; // float pose[numposes][numbones][3][4]; // animation data
-       loadmodel->num_poses = pheader->lump_poses.length / sizeof(float[3][4]);
-       loadmodel->data_poses = (float *)Mem_Alloc(loadmodel->mempool, pheader->lump_poses.length);
-       poses = (float *) (pheader->lump_poses.start + pbase);
-       for (i = 0;i < pheader->lump_poses.length / 4;i++)
-               loadmodel->data_poses[i] = BigFloat(poses[i]);
-
        //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));
@@ -1284,7 +1304,7 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                loadmodel->data_bones[i].flags = BigLong(bone[i].flags);
                loadmodel->data_bones[i].parent = BigLong(bone[i].parent);
                if (loadmodel->data_bones[i].parent >= i)
-                       Host_Error("%s bone[%i].parent >= %i\n", loadmodel->name, i, i);
+                       Host_Error("%s bone[%i].parent >= %i", loadmodel->name, i, i);
        }
 
        //zymlump_t lump_vertbonecounts; // int vertbonecounts[numvertices]; // how many bones influence each vertex (separate mainly to make this compress better)
@@ -1294,22 +1314,56 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        {
                vertbonecounts[i] = BigLong(bonecount[i]);
                if (vertbonecounts[i] < 1)
-                       Host_Error("%s bonecount[%i] < 1\n", loadmodel->name, i);
+                       Host_Error("%s bonecount[%i] < 1", loadmodel->name, i);
        }
 
+       loadmodel->num_poses = pheader->lump_poses.length / sizeof(float[3][4]);
+
+       meshvertices = pheader->numverts;
+       meshtriangles = pheader->numtris;
+       numvertexboneweights = pheader->lump_verts.length / sizeof(zymvertex_t);
+
+       loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
+       loadmodel->num_textures = 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]) + numvertexboneweights * sizeof(surfmeshvertexboneweight_t) + loadmodel->num_poses * sizeof(float[3][4]));
+       loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
+       loadmodel->surfacelist = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
+       loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
+       loadmodel->surfmesh.num_vertices = meshvertices;
+       loadmodel->surfmesh.num_triangles = meshtriangles;
+       loadmodel->surfmesh.num_vertexboneweights = numvertexboneweights;
+       loadmodel->surfmesh.data_element3i = (int *)data;data += meshtriangles * sizeof(int[3]);
+       loadmodel->surfmesh.data_neighbor3i = (int *)data;data += meshtriangles * sizeof(int[3]);
+       loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += meshvertices * sizeof(float[2]);
+       loadmodel->surfmesh.data_vertexboneweights = (surfmeshvertexboneweight_t *)data;data += numvertexboneweights * sizeof(surfmeshvertexboneweight_t);
+       loadmodel->data_poses = (float *)data;data += loadmodel->num_poses * sizeof(float[3][4]);
+
+       //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]);
+
        //zymlump_t lump_verts; // zymvertex_t vert[numvertices]; // see vertex struct
        verts = (zymvertex_t *)Mem_Alloc(loadmodel->mempool, pheader->lump_verts.length);
        vertdata = (zymvertex_t *) (pheader->lump_verts.start + pbase);
-       for (i = 0;i < pheader->lump_verts.length / (int) sizeof(zymvertex_t);i++)
+       l = 0;
+       for (j = 0;j < pheader->numverts;j++)
        {
-               verts[i].bonenum = BigLong(vertdata[i].bonenum);
-               verts[i].origin[0] = BigFloat(vertdata[i].origin[0]);
-               verts[i].origin[1] = BigFloat(vertdata[i].origin[1]);
-               verts[i].origin[2] = BigFloat(vertdata[i].origin[2]);
+               for (k = 0;k < vertbonecounts[j];k++, l++)
+               {
+                       // this format really should have had a per vertexweight weight value...
+                       float influence = 1.0f / vertbonecounts[j];
+                       loadmodel->surfmesh.data_vertexboneweights[l].vertexindex = j;
+                       loadmodel->surfmesh.data_vertexboneweights[l].boneindex = BigLong(vertdata[l].bonenum);
+                       loadmodel->surfmesh.data_vertexboneweights[l].origin[0] = BigFloat(vertdata[l].origin[0]) * influence;
+                       loadmodel->surfmesh.data_vertexboneweights[l].origin[1] = BigFloat(vertdata[l].origin[1]) * influence;
+                       loadmodel->surfmesh.data_vertexboneweights[l].origin[2] = BigFloat(vertdata[l].origin[2]) * influence;
+                       loadmodel->surfmesh.data_vertexboneweights[l].origin[3] = influence;
+               }
        }
 
        //zymlump_t lump_texcoords; // float texcoords[numvertices][2];
-       outtexcoord2f = (float *)Mem_Alloc(loadmodel->mempool, pheader->numverts * sizeof(float[2]));
+       outtexcoord2f = loadmodel->surfmesh.data_texcoordtexture2f;
        intexcoord2f = (float *) (pheader->lump_texcoords.start + pbase);
        for (i = 0;i < pheader->numverts;i++)
        {
@@ -1322,41 +1376,36 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        //loadmodel->alias.zymdata_trizone = Mem_Alloc(loadmodel->mempool, pheader->numtris);
        //memcpy(loadmodel->alias.zymdata_trizone, (void *) (pheader->lump_trizone.start + pbase), pheader->numtris);
 
-       loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
-       loadmodel->nummeshes = loadmodel->num_surfaces;
-       loadmodel->num_textures = loadmodel->num_surfaces;
-       data = (qbyte *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->nummeshes * sizeof(surfmesh_t *) + loadmodel->nummeshes * sizeof(surfmesh_t) + loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_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->meshlist = (surfmesh_t **)data;data += loadmodel->num_surfaces * sizeof(surfmesh_t *);
-       loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
-       for (i = 0;i < loadmodel->num_surfaces;i++)
-       {
-               loadmodel->surfacelist[i] = i;
-               loadmodel->meshlist[i] = (surfmesh_t *)data;data += sizeof(surfmesh_t);
-       }
-
        //zymlump_t lump_shaders; // char shadername[numshaders][32]; // shaders used on this model
        //zymlump_t lump_render; // int renderlist[rendersize]; // sorted by shader with run lengths (int count), shaders are sequentially used, each run can be used with glDrawElements (each triangle is 3 int indices)
        // byteswap, validate, and swap winding order of tris
        count = pheader->numshaders * sizeof(int) + pheader->numtris * sizeof(int[3]);
        if (pheader->lump_render.length != count)
-               Host_Error("%s renderlist is wrong size (%i bytes, should be %i bytes)\n", loadmodel->name, pheader->lump_render.length, count);
+               Host_Error("%s renderlist is wrong size (%i bytes, should be %i bytes)", loadmodel->name, pheader->lump_render.length, count);
        renderlist = (int *) (pheader->lump_render.start + pbase);
-       renderlistend = (int *) ((qbyte *) renderlist + pheader->lump_render.length);
+       renderlistend = (int *) ((unsigned char *) renderlist + pheader->lump_render.length);
+       meshtriangles = 0;
        for (i = 0;i < loadmodel->num_surfaces;i++)
        {
+               int lastvertex;
                if (renderlist >= renderlistend)
-                       Host_Error("%s corrupt renderlist (wrong size)\n", loadmodel->name);
+                       Host_Error("%s corrupt renderlist (wrong size)", loadmodel->name);
                count = BigLong(*renderlist);renderlist++;
                if (renderlist + count * 3 > renderlistend || (i == pheader->numshaders - 1 && renderlist + count * 3 != renderlistend))
-                       Host_Error("%s corrupt renderlist (wrong size)\n", loadmodel->name);
-               mesh = loadmodel->meshlist[i];
-               mesh->num_triangles = count;
-               mesh->data_element3i = (int *)Mem_Alloc(loadmodel->mempool, mesh->num_triangles * sizeof(int[3]));
-               mesh->data_neighbor3i = (int *)Mem_Alloc(loadmodel->mempool, mesh->num_triangles * sizeof(int[3]));
-               outelements = mesh->data_element3i;
-               for (j = 0;j < mesh->num_triangles;j++)
+                       Host_Error("%s corrupt renderlist (wrong size)", loadmodel->name);
+
+               loadmodel->surfacelist[i] = i;
+               surface = loadmodel->data_surfaces + i;
+               surface->texture = loadmodel->data_textures + i;
+               surface->num_firsttriangle = meshtriangles;
+               surface->num_triangles = count;
+               surface->num_firstvertex = meshvertices;
+               surface->num_vertices = meshvertices;
+
+               // load the elements and find the used vertex range
+               lastvertex = 0;
+               outelements = loadmodel->surfmesh.data_element3i + surface->num_firsttriangle * 3;
+               for (j = 0;j < surface->num_triangles;j++)
                {
                        outelements[2] = BigLong(renderlist[0]);
                        outelements[1] = BigLong(renderlist[1]);
@@ -1364,78 +1413,36 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                        if ((unsigned int)outelements[0] >= (unsigned int)pheader->numverts
                         || (unsigned int)outelements[1] >= (unsigned int)pheader->numverts
                         || (unsigned int)outelements[2] >= (unsigned int)pheader->numverts)
-                               Host_Error("%s corrupt renderlist (out of bounds index)\n", loadmodel->name);
+                               Host_Error("%s corrupt renderlist (out of bounds index)", loadmodel->name);
                        if (vertbonecounts[outelements[0]] == 0 || vertbonecounts[outelements[1]] == 0 || vertbonecounts[outelements[2]] == 0)
-                               Host_Error("%s corrupt renderlist (references vertex with no bone weights\n", loadmodel->name);
+                               Host_Error("%s corrupt renderlist (references vertex with no bone weights", loadmodel->name);
+                       surface->num_firstvertex = min(surface->num_firstvertex, outelements[0]);
+                       surface->num_firstvertex = min(surface->num_firstvertex, outelements[1]);
+                       surface->num_firstvertex = min(surface->num_firstvertex, outelements[2]);
+                       lastvertex = max(lastvertex, outelements[0]);
+                       lastvertex = max(lastvertex, outelements[1]);
+                       lastvertex = max(lastvertex, outelements[2]);
                        renderlist += 3;
                        outelements += 3;
                }
-               remapvertices = (int *)Mem_Alloc(loadmodel->mempool, pheader->numverts * sizeof(int));
-               mesh->num_vertices = Mod_BuildVertexRemapTableFromElements(mesh->num_triangles * 3, mesh->data_element3i, pheader->numverts, remapvertices);
-               for (j = 0;j < mesh->num_triangles * 3;j++)
-                       mesh->data_element3i[j] = remapvertices[mesh->data_element3i[j]];
-               mesh->data_texcoordtexture2f = (float *)Mem_Alloc(loadmodel->mempool, mesh->num_vertices * sizeof(float[2]));
-               for (j = 0;j < pheader->numverts;j++)
-               {
-                       if (remapvertices[j] >= 0)
-                       {
-                               mesh->data_texcoordtexture2f[remapvertices[j]*2+0] = outtexcoord2f[j*2+0];
-                               mesh->data_texcoordtexture2f[remapvertices[j]*2+1] = outtexcoord2f[j*2+1];
-                       }
-               }
-               mesh->num_vertexboneweights = 0;
-               for (j = 0;j < pheader->numverts;j++)
-                       if (remapvertices[j] >= 0)
-                               mesh->num_vertexboneweights += vertbonecounts[remapvertices[j]];
-               mesh->data_vertexboneweights = (surfmeshvertexboneweight_t *)Mem_Alloc(loadmodel->mempool, mesh->num_vertexboneweights * sizeof(surfmeshvertexboneweight_t));
-               mesh->num_vertexboneweights = 0;
-               // note this vertexboneweight ordering requires that the remapvertices array is sequential numbers (separated by -1 values for omitted vertices)
-               l = 0;
-               for (j = 0;j < pheader->numverts;j++)
-               {
-                       if (remapvertices[j] < 0)
-                       {
-                               l += vertbonecounts[j];
-                               continue;
-                       }
-                       for (k = 0;k < vertbonecounts[j];k++)
-                       {
-                               // this format really should have had a per vertexweight weight value...
-                               mesh->data_vertexboneweights[mesh->num_vertexboneweights].vertexindex = remapvertices[j];
-                               mesh->data_vertexboneweights[mesh->num_vertexboneweights].boneindex = verts[l].bonenum;
-                               mesh->data_vertexboneweights[mesh->num_vertexboneweights].origin[3] = 1.0f / vertbonecounts[j];
-                               mesh->data_vertexboneweights[mesh->num_vertexboneweights].origin[0] = verts[l].origin[0] * mesh->data_vertexboneweights[mesh->num_vertexboneweights].origin[3];
-                               mesh->data_vertexboneweights[mesh->num_vertexboneweights].origin[1] = verts[l].origin[1] * mesh->data_vertexboneweights[mesh->num_vertexboneweights].origin[3];
-                               mesh->data_vertexboneweights[mesh->num_vertexboneweights].origin[2] = verts[l].origin[2] * mesh->data_vertexboneweights[mesh->num_vertexboneweights].origin[3];
-                               mesh->num_vertexboneweights++;
-                               l++;
-                       }
-               }
-               shadername = (char *) (pheader->lump_shaders.start + pbase) + i * 32;
+               surface->num_vertices = lastvertex + 1 - surface->num_firstvertex;
+
                // since zym models do not have named sections, reuse their shader
                // name as the section name
+               shadername = (char *) (pheader->lump_shaders.start + pbase) + i * 32;
                if (shadername[0])
                        Mod_BuildAliasSkinsFromSkinFiles(loadmodel->data_textures + i, skinfiles, shadername, shadername);
                else
                        for (j = 0;j < loadmodel->numskins;j++)
                                Mod_BuildAliasSkinFromSkinFrame(loadmodel->data_textures + i + j * loadmodel->num_surfaces, NULL);
 
-               Mod_ValidateElements(mesh->data_element3i, mesh->num_triangles, mesh->num_vertices, __FILE__, __LINE__);
-               Mod_BuildTriangleNeighbors(mesh->data_neighbor3i, mesh->data_element3i, mesh->num_triangles);
-               Mod_Alias_Mesh_CompileFrameZero(mesh);
-
-               surface = loadmodel->data_surfaces + i;
-               surface->groupmesh = mesh;
-               surface->texture = loadmodel->data_textures + i;
-               surface->num_firsttriangle = 0;
-               surface->num_triangles = mesh->num_triangles;
-               surface->num_firstvertex = 0;
-               surface->num_vertices = mesh->num_vertices;
+               Mod_ValidateElements(loadmodel->surfmesh.data_element3i + surface->num_firsttriangle * 3, surface->num_triangles, surface->num_firstvertex, surface->num_vertices, __FILE__, __LINE__);
        }
-
+       Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
+       Mod_Alias_Mesh_CompileFrameZero();
+       Mod_FreeSkinFiles(skinfiles);
        Mem_Free(vertbonecounts);
        Mem_Free(verts);
-       Mem_Free(outtexcoord2f);
 }
 
 void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
@@ -1444,25 +1451,19 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        dpmframe_t *frame;
        dpmbone_t *bone;
        dpmmesh_t *dpmmesh;
-       qbyte *pbase;
-       int i, j, k;
+       unsigned char *pbase;
+       int i, j, k, meshvertices, meshtriangles, numvertexboneweights;
        skinfile_t *skinfiles;
-       qbyte *data;
+       unsigned char *data;
 
        pheader = (dpmheader_t *)buffer;
-       pbase = (qbyte *)buffer;
+       pbase = (unsigned char *)buffer;
        if (memcmp(pheader->id, "DARKPLACESMODEL\0", 16))
-               Host_Error ("Mod_DARKPLACESMODEL_Load: %s is not a darkplaces model\n");
+               Host_Error ("Mod_DARKPLACESMODEL_Load: %s is not a darkplaces model");
        if (BigLong(pheader->type) != 2)
-               Host_Error ("Mod_DARKPLACESMODEL_Load: only type 2 (hierarchical skeletal pose) models are currently supported (name = %s)\n", loadmodel->name);
+               Host_Error ("Mod_DARKPLACESMODEL_Load: only type 2 (hierarchical skeletal pose) models are currently supported (name = %s)", loadmodel->name);
 
        loadmodel->type = mod_alias;
-       loadmodel->DrawSky = NULL;
-       loadmodel->Draw = R_Q1BSP_Draw;
-       loadmodel->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
-       loadmodel->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
-       loadmodel->DrawLight = R_Q1BSP_DrawLight;
-       loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
        loadmodel->flags = 0; // there are no flags on zym models
        loadmodel->synctype = ST_RAND;
 
@@ -1484,6 +1485,24 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        pheader->ofs_meshs = BigLong(pheader->ofs_meshs);
        pheader->ofs_frames = BigLong(pheader->ofs_frames);
 
+       if (pheader->num_bones < 1 || pheader->num_meshs < 1)
+       {
+               Con_Printf("%s has no geometry\n");
+               return;
+       }
+       if (pheader->num_frames < 1)
+       {
+               Con_Printf("%s has no frames\n");
+               return;
+       }
+
+       loadmodel->DrawSky = NULL;
+       loadmodel->Draw = R_Q1BSP_Draw;
+       loadmodel->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
+       loadmodel->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
+       loadmodel->DrawLight = R_Q1BSP_DrawLight;
+       loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
+
        // model bbox
        for (i = 0;i < 3;i++)
        {
@@ -1502,21 +1521,51 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        if (loadmodel->numskins < 1)
                loadmodel->numskins = 1;
 
+       meshvertices = 0;
+       meshtriangles = 0;
+       numvertexboneweights = 0;
+
+       // load the meshes now
+       dpmmesh = (dpmmesh_t *) (pbase + pheader->ofs_meshs);
+       for (i = 0;i < loadmodel->num_surfaces;i++)
+       {
+               int numverts = BigLong(dpmmesh->num_verts);
+               meshvertices += numverts;;
+               meshtriangles += BigLong(dpmmesh->num_tris);
+
+               // to find out how many weights exist we two a two-stage load...
+               data = (unsigned char *) (pbase + BigLong(dpmmesh->ofs_verts));
+               for (j = 0;j < numverts;j++)
+               {
+                       int numweights = BigLong(((dpmvertex_t *)data)->numbones);
+                       numvertexboneweights += numweights;
+                       data += sizeof(dpmvertex_t);
+                       data += numweights * sizeof(dpmbonevert_t);
+               }
+               dpmmesh++;
+       }
+
        loadmodel->numframes = pheader->num_frames;
        loadmodel->num_bones = pheader->num_bones;
        loadmodel->num_poses = loadmodel->num_bones * loadmodel->numframes;
-       loadmodel->num_textures = loadmodel->nummeshes = loadmodel->nummodelsurfaces = loadmodel->num_surfaces = pheader->num_meshs;
-
+       loadmodel->num_textures = loadmodel->nummodelsurfaces = loadmodel->num_surfaces = pheader->num_meshs;
        // do most allocations as one merged chunk
-       data = (qbyte *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->nummeshes * sizeof(surfmesh_t *) + loadmodel->nummeshes * sizeof(surfmesh_t) + loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t) + loadmodel->numskins * sizeof(animscene_t) + loadmodel->num_bones * sizeof(aliasbone_t) + loadmodel->num_poses * sizeof(float[12]) + 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]) + meshtriangles * sizeof(int[3]) + meshvertices * sizeof(float[2]) + numvertexboneweights * sizeof(surfmeshvertexboneweight_t) + loadmodel->num_poses * sizeof(float[3][4]) + 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->meshlist = (surfmesh_t **)data;data += loadmodel->num_surfaces * sizeof(surfmesh_t *);
        loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
+       loadmodel->surfmesh.num_vertices = meshvertices;
+       loadmodel->surfmesh.num_triangles = meshtriangles;
+       loadmodel->surfmesh.num_vertexboneweights = numvertexboneweights;
+       loadmodel->surfmesh.data_element3i = (int *)data;data += meshtriangles * sizeof(int[3]);
+       loadmodel->surfmesh.data_neighbor3i = (int *)data;data += meshtriangles * sizeof(int[3]);
+       loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += meshvertices * sizeof(float[2]);
+       loadmodel->surfmesh.data_vertexboneweights = (surfmeshvertexboneweight_t *)data;data += numvertexboneweights * sizeof(surfmeshvertexboneweight_t);
+       loadmodel->data_poses = (float *)data;data += loadmodel->num_poses * sizeof(float[3][4]);
        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->data_poses = (float *)data;data += loadmodel->num_poses * sizeof(float[12]);
        loadmodel->animscenes = (animscene_t *)data;data += loadmodel->numframes * sizeof(animscene_t);
+
        for (i = 0;i < loadmodel->numskins;i++)
        {
                loadmodel->skinscenes[i].firstframe = i;
@@ -1524,11 +1573,6 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                loadmodel->skinscenes[i].loop = true;
                loadmodel->skinscenes[i].framerate = 10;
        }
-       for (i = 0;i < loadmodel->num_surfaces;i++)
-       {
-               loadmodel->surfacelist[i] = i;
-               loadmodel->meshlist[i] = (surfmesh_t *)data;data += sizeof(surfmesh_t);
-       }
 
        // load the bone info
        bone = (dpmbone_t *) (pbase + pheader->ofs_bones);
@@ -1538,7 +1582,7 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                loadmodel->data_bones[i].flags = BigLong(bone[i].flags);
                loadmodel->data_bones[i].parent = BigLong(bone[i].parent);
                if (loadmodel->data_bones[i].parent >= i)
-                       Host_Error("%s bone[%i].parent >= %i\n", loadmodel->name, i, i);
+                       Host_Error("%s bone[%i].parent >= %i", loadmodel->name, i, i);
        }
 
        // load the frames
@@ -1561,55 +1605,44 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
 
        // load the meshes now
        dpmmesh = (dpmmesh_t *) (pbase + pheader->ofs_meshs);
-       for (i = 0;i < loadmodel->num_surfaces;i++)
+       meshvertices = 0;
+       meshtriangles = 0;
+       numvertexboneweights = 0;
+       for (i = 0;i < loadmodel->num_surfaces;i++, dpmmesh++)
        {
                const int *inelements;
                int *outelements;
                const float *intexcoord;
-               surfmesh_t *mesh;
                msurface_t *surface;
 
-               mesh = loadmodel->meshlist[i];
-               mesh->num_triangles = BigLong(dpmmesh->num_tris);
-               mesh->num_vertices = BigLong(dpmmesh->num_verts);
-
-               // to find out how many weights exist we two a two-stage load...
-               mesh->num_vertexboneweights = 0;
-               data = (qbyte *) (pbase + BigLong(dpmmesh->ofs_verts));
-               for (j = 0;j < mesh->num_vertices;j++)
-               {
-                       int numweights = BigLong(((dpmvertex_t *)data)->numbones);
-                       mesh->num_vertexboneweights += numweights;
-                       data += sizeof(dpmvertex_t);
-                       data += numweights * sizeof(dpmbonevert_t);
-               }
-
-               // allocate things now that we know how many
-               mesh->data_vertexboneweights = (surfmeshvertexboneweight_t *)Mem_Alloc(loadmodel->mempool, mesh->num_vertexboneweights * sizeof(surfmeshvertexboneweight_t));
-               mesh->data_element3i = (int *)Mem_Alloc(loadmodel->mempool, mesh->num_triangles * sizeof(int[3]));
-               mesh->data_neighbor3i = (int *)Mem_Alloc(loadmodel->mempool, mesh->num_triangles * sizeof(int[3]));
-               mesh->data_texcoordtexture2f = (float *)Mem_Alloc(loadmodel->mempool, mesh->num_vertices * sizeof(float[2]));
+               loadmodel->surfacelist[i] = i;
+               surface = loadmodel->data_surfaces + i;
+               surface->texture = loadmodel->data_textures + i;
+               surface->num_firsttriangle = meshtriangles;
+               surface->num_triangles = BigLong(dpmmesh->num_tris);
+               surface->num_firstvertex = meshvertices;
+               surface->num_vertices = BigLong(dpmmesh->num_verts);
+               meshvertices += surface->num_vertices;
+               meshtriangles += surface->num_triangles;
 
                inelements = (int *) (pbase + BigLong(dpmmesh->ofs_indices));
-               outelements = mesh->data_element3i;
-               for (j = 0;j < mesh->num_triangles;j++)
+               outelements = loadmodel->surfmesh.data_element3i + surface->num_firsttriangle * 3;
+               for (j = 0;j < surface->num_triangles;j++)
                {
                        // swap element order to flip triangles, because Quake uses clockwise (rare) and dpm uses counterclockwise (standard)
-                       outelements[0] = BigLong(inelements[2]);
-                       outelements[1] = BigLong(inelements[1]);
-                       outelements[2] = BigLong(inelements[0]);
+                       outelements[0] = surface->num_firstvertex + BigLong(inelements[2]);
+                       outelements[1] = surface->num_firstvertex + BigLong(inelements[1]);
+                       outelements[2] = surface->num_firstvertex + BigLong(inelements[0]);
                        inelements += 3;
                        outelements += 3;
                }
 
                intexcoord = (float *) (pbase + BigLong(dpmmesh->ofs_texcoords));
-               for (j = 0;j < mesh->num_vertices*2;j++)
-                       mesh->data_texcoordtexture2f[j] = BigFloat(intexcoord[j]);
+               for (j = 0;j < surface->num_vertices*2;j++)
+                       loadmodel->surfmesh.data_texcoordtexture2f[j + surface->num_firstvertex * 2] = BigFloat(intexcoord[j]);
 
-               // now load them for real
-               mesh->num_vertexboneweights = 0;
-               data = (qbyte *) (pbase + BigLong(dpmmesh->ofs_verts));
-               for (j = 0;j < mesh->num_vertices;j++)
+               data = (unsigned char *) (pbase + BigLong(dpmmesh->ofs_verts));
+               for (j = 0;j < surface->num_vertices;j++)
                {
                        int numweights = BigLong(((dpmvertex_t *)data)->numbones);
                        data += sizeof(dpmvertex_t);
@@ -1617,13 +1650,13 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                        {
                                const dpmbonevert_t *vert = (dpmbonevert_t *) data;
                                // stuff not processed here: normal
-                               mesh->data_vertexboneweights[mesh->num_vertexboneweights].vertexindex = j;
-                               mesh->data_vertexboneweights[mesh->num_vertexboneweights].boneindex = BigLong(vert->bonenum);
-                               mesh->data_vertexboneweights[mesh->num_vertexboneweights].origin[0] = BigFloat(vert->origin[0]);
-                               mesh->data_vertexboneweights[mesh->num_vertexboneweights].origin[1] = BigFloat(vert->origin[1]);
-                               mesh->data_vertexboneweights[mesh->num_vertexboneweights].origin[2] = BigFloat(vert->origin[2]);
-                               mesh->data_vertexboneweights[mesh->num_vertexboneweights].origin[3] = BigFloat(vert->influence);
-                               mesh->num_vertexboneweights++;
+                               loadmodel->surfmesh.data_vertexboneweights[numvertexboneweights].vertexindex = j;
+                               loadmodel->surfmesh.data_vertexboneweights[numvertexboneweights].boneindex = BigLong(vert->bonenum);
+                               loadmodel->surfmesh.data_vertexboneweights[numvertexboneweights].origin[0] = BigFloat(vert->origin[0]);
+                               loadmodel->surfmesh.data_vertexboneweights[numvertexboneweights].origin[1] = BigFloat(vert->origin[1]);
+                               loadmodel->surfmesh.data_vertexboneweights[numvertexboneweights].origin[2] = BigFloat(vert->origin[2]);
+                               loadmodel->surfmesh.data_vertexboneweights[numvertexboneweights].origin[3] = BigFloat(vert->influence);
+                               numvertexboneweights++;
                                data += sizeof(dpmbonevert_t);
                        }
                }
@@ -1635,20 +1668,11 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                        for (j = 0;j < loadmodel->numskins;j++)
                                Mod_BuildAliasSkinFromSkinFrame(loadmodel->data_textures + i + j * loadmodel->num_surfaces, NULL);
 
-               Mod_ValidateElements(mesh->data_element3i, mesh->num_triangles, mesh->num_vertices, __FILE__, __LINE__);
-               Mod_BuildTriangleNeighbors(mesh->data_neighbor3i, mesh->data_element3i, mesh->num_triangles);
-               Mod_Alias_Mesh_CompileFrameZero(mesh);
-
-               surface = loadmodel->data_surfaces + i;
-               surface->groupmesh = mesh;
-               surface->texture = loadmodel->data_textures + i;
-               surface->num_firsttriangle = 0;
-               surface->num_triangles = mesh->num_triangles;
-               surface->num_firstvertex = 0;
-               surface->num_vertices = mesh->num_vertices;
-
-               dpmmesh++;
+               Mod_ValidateElements(loadmodel->surfmesh.data_element3i + surface->num_firsttriangle * 3, surface->num_triangles, surface->num_firstvertex, surface->num_vertices, __FILE__, __LINE__);
        }
+       Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
+       Mod_Alias_Mesh_CompileFrameZero();
+       Mod_FreeSkinFiles(skinfiles);
 }
 
 static void Mod_PSKMODEL_AnimKeyToMatrix(float *origin, float *quat, matrix4x4_t *m)
@@ -1664,8 +1688,9 @@ static void Mod_PSKMODEL_AnimKeyToMatrix(float *origin, float *quat, matrix4x4_t
 #define PSKQUATNEGATIONS
 void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
 {
-       int i, j, index, version, recordsize, numrecords;
+       int i, j, index, version, recordsize, numrecords, meshvertices, meshtriangles, numvertexboneweights;
        int numpnts, numvtxw, numfaces, nummatts, numbones, numrawweights, numanimbones, numanims, numanimkeys;
+       fs_offset_t filesize;
        pskpnts_t *pnts;
        pskvtxw_t *vtxw;
        pskface_t *faces;
@@ -1676,14 +1701,14 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        pskaniminfo_t *anims;
        pskanimkeys_t *animkeys;
        void *animfilebuffer, *animbuffer, *animbufferend;
+       unsigned char *data;
        pskchunk_t *pchunk;
-       surfmesh_t *mesh;
        skinfile_t *skinfiles;
        char animname[MAX_QPATH];
 
        pchunk = (pskchunk_t *)buffer;
        if (strcmp(pchunk->id, "ACTRHEAD"))
-               Host_Error ("Mod_PSKMODEL_Load: %s is not a ActorX model\n");
+               Host_Error ("Mod_PSKMODEL_Load: %s is not an Unreal Engine ActorX (.psk + .psa) model");
 
        loadmodel->type = mod_alias;
        loadmodel->DrawSky = NULL;
@@ -1695,25 +1720,12 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        loadmodel->flags = 0; // there are no flags on zym models
        loadmodel->synctype = ST_RAND;
 
-       // load external .skin files if present
-       skinfiles = Mod_LoadSkinFiles();
-       if (loadmodel->numskins < 1)
-               loadmodel->numskins = 1;
-       loadmodel->skinscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, loadmodel->numskins * sizeof(animscene_t));
-       for (i = 0;i < loadmodel->numskins;i++)
-       {
-               loadmodel->skinscenes[i].firstframe = i;
-               loadmodel->skinscenes[i].framecount = 1;
-               loadmodel->skinscenes[i].loop = true;
-               loadmodel->skinscenes[i].framerate = 10;
-       }
-
        FS_StripExtension(loadmodel->name, animname, sizeof(animname));
        strlcat(animname, ".psa", sizeof(animname));
-       animbuffer = animfilebuffer = FS_LoadFile(animname, loadmodel->mempool, false);
-       animbufferend = (void *)((unsigned char*)animbuffer + fs_filesize);
+       animbuffer = animfilebuffer = FS_LoadFile(animname, loadmodel->mempool, false, &filesize);
+       animbufferend = (void *)((unsigned char*)animbuffer + (int)filesize);
        if (animbuffer == NULL)
-               Host_Error("%s: can't find .psa file (%s)\n", loadmodel->name, animname);
+               Host_Error("%s: can't find .psa file (%s)", loadmodel->name, animname);
 
        numpnts = 0;
        pnts = NULL;
@@ -1739,10 +1751,10 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                version = LittleLong(pchunk->version);
                recordsize = LittleLong(pchunk->recordsize);
                numrecords = LittleLong(pchunk->numrecords);
-               if (developer.integer)
+               if (developer.integer >= 100)
                        Con_Printf("%s: %s %x: %i * %i = %i\n", loadmodel->name, pchunk->id, version, recordsize, numrecords, recordsize * numrecords);
-               if (version != 0x1e83b9 && version != 0x1e9179 && version != 0x2e)
-                       Con_Printf ("%s: chunk %s has unknown version %x (0x1e83b9, 0x1e9179 and 0x2e are currently supported), trying to load anyway!\n", loadmodel->name, pchunk->id, version);
+               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"))
                {
                        // nothing to do
@@ -1751,7 +1763,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                {
                        pskpnts_t *p;
                        if (recordsize != sizeof(*p))
-                               Host_Error("%s: %s has unsupported recordsize\n", loadmodel->name, pchunk->id);
+                               Host_Error("%s: %s has unsupported recordsize", loadmodel->name, pchunk->id);
                        // byteswap in place and keep the pointer
                        numpnts = numrecords;
                        pnts = (pskpnts_t *)buffer;
@@ -1767,7 +1779,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                {
                        pskvtxw_t *p;
                        if (recordsize != sizeof(*p))
-                               Host_Error("%s: %s has unsupported recordsize\n", loadmodel->name, pchunk->id);
+                               Host_Error("%s: %s has unsupported recordsize", loadmodel->name, pchunk->id);
                        // byteswap in place and keep the pointer
                        numvtxw = numrecords;
                        vtxw = (pskvtxw_t *)buffer;
@@ -1788,7 +1800,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                {
                        pskface_t *p;
                        if (recordsize != sizeof(*p))
-                               Host_Error("%s: %s has unsupported recordsize\n", loadmodel->name, pchunk->id);
+                               Host_Error("%s: %s has unsupported recordsize", loadmodel->name, pchunk->id);
                        // byteswap in place and keep the pointer
                        numfaces = numrecords;
                        faces = (pskface_t *)buffer;
@@ -1820,7 +1832,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                {
                        pskmatt_t *p;
                        if (recordsize != sizeof(*p))
-                               Host_Error("%s: %s has unsupported recordsize\n", loadmodel->name, pchunk->id);
+                               Host_Error("%s: %s has unsupported recordsize", loadmodel->name, pchunk->id);
                        // byteswap in place and keep the pointer
                        nummatts = numrecords;
                        matts = (pskmatt_t *)buffer;
@@ -1833,7 +1845,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                {
                        pskboneinfo_t *p;
                        if (recordsize != sizeof(*p))
-                               Host_Error("%s: %s has unsupported recordsize\n", loadmodel->name, pchunk->id);
+                               Host_Error("%s: %s has unsupported recordsize", loadmodel->name, pchunk->id);
                        // byteswap in place and keep the pointer
                        numbones = numrecords;
                        bones = (pskboneinfo_t *)buffer;
@@ -1878,7 +1890,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                {
                        pskrawweights_t *p;
                        if (recordsize != sizeof(*p))
-                               Host_Error("%s: %s has unsupported recordsize\n", loadmodel->name, pchunk->id);
+                               Host_Error("%s: %s has unsupported recordsize", loadmodel->name, pchunk->id);
                        // byteswap in place and keep the pointer
                        numrawweights = numrecords;
                        rawweights = (pskrawweights_t *)buffer;
@@ -1909,10 +1921,10 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                version = LittleLong(pchunk->version);
                recordsize = LittleLong(pchunk->recordsize);
                numrecords = LittleLong(pchunk->numrecords);
-               if (developer.integer)
+               if (developer.integer >= 100)
                        Con_Printf("%s: %s %x: %i * %i = %i\n", animname, pchunk->id, version, recordsize, numrecords, recordsize * numrecords);
-               if (version != 0x1e83b9 && version != 0x1e9179 && version != 0x2e)
-                       Con_Printf ("%s: chunk %s has unknown version %x (0x1e83b9, 0x1e9179 and 0x2e are currently supported), trying to load anyway!\n", animname, pchunk->id, version);
+               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"))
                {
                        // nothing to do
@@ -1921,7 +1933,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                {
                        pskboneinfo_t *p;
                        if (recordsize != sizeof(*p))
-                               Host_Error("%s: %s has unsupported recordsize\n", animname, pchunk->id);
+                               Host_Error("%s: %s has unsupported recordsize", animname, pchunk->id);
                        // byteswap in place and keep the pointer
                        numanimbones = numrecords;
                        animbones = (pskboneinfo_t *)animbuffer;
@@ -1930,7 +1942,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                        // positions from the psk, but this is hard for me to implement
                        // and people can easily make animations that match.
                        if (numanimbones != numbones)
-                               Host_Error("%s: this loader only supports animations with the same bones as the mesh\n");
+                               Host_Error("%s: this loader only supports animations with the same bones as the mesh");
                        for (index = 0, p = (pskboneinfo_t *)animbuffer;index < numrecords;index++, p++)
                        {
                                p->numchildren = LittleLong(p->numchildren);
@@ -1967,7 +1979,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                                }
                                // check that bones are the same as in the base
                                if (strcmp(p->name, bones[index].name) || p->parent != bones[index].parent)
-                                       Host_Error("%s: this loader only supports animations with the same bones as the mesh\n", animname);
+                                       Host_Error("%s: this loader only supports animations with the same bones as the mesh", animname);
                        }
                        animbuffer = p;
                }
@@ -1975,7 +1987,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                {
                        pskaniminfo_t *p;
                        if (recordsize != sizeof(*p))
-                               Host_Error("%s: %s has unsupported recordsize\n", animname, pchunk->id);
+                               Host_Error("%s: %s has unsupported recordsize", animname, pchunk->id);
                        // byteswap in place and keep the pointer
                        numanims = numrecords;
                        anims = (pskaniminfo_t *)animbuffer;
@@ -1997,7 +2009,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                {
                        pskanimkeys_t *p;
                        if (recordsize != sizeof(*p))
-                               Host_Error("%s: %s has unsupported recordsize\n", animname, pchunk->id);
+                               Host_Error("%s: %s has unsupported recordsize", animname, pchunk->id);
                        numanimkeys = numrecords;
                        animkeys = (pskanimkeys_t *)animbuffer;
                        for (index = 0, p = (pskanimkeys_t *)animbuffer;index < numrecords;index++, p++)
@@ -2033,7 +2045,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        }
 
        if (!numpnts || !pnts || !numvtxw || !vtxw || !numfaces || !faces || !nummatts || !matts || !numbones || !bones || !numrawweights || !rawweights || !numanims || !anims || !numanimkeys || !animkeys)
-               Host_Error("%s: missing required chunks\n", loadmodel->name);
+               Host_Error("%s: missing required chunks", loadmodel->name);
 
        // FIXME: model bbox
        // model bbox
@@ -2053,27 +2065,48 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        for (index = 0;index < numanims;index++)
                loadmodel->numframes += anims[index].numframes;
 
+       if (numanimkeys != numbones * loadmodel->numframes)
+               Host_Error("%s: %s has incorrect number of animation keys", animname, pchunk->id);
+
+       meshvertices = numvtxw;
+       meshtriangles = numfaces;
+       numvertexboneweights = 0;
+       for (index = 0;index < numvtxw;index++)
+               for (j = 0;j < numrawweights;j++)
+                       if (rawweights[j].pntsindex == vtxw[index].pntsindex)
+                               numvertexboneweights++;
+
+       // load external .skin files if present
+       skinfiles = Mod_LoadSkinFiles();
+       if (loadmodel->numskins < 1)
+               loadmodel->numskins = 1;
        loadmodel->num_bones = numbones;
        loadmodel->num_poses = loadmodel->num_bones * loadmodel->numframes;
-       loadmodel->num_textures = loadmodel->nummeshes = loadmodel->nummodelsurfaces = loadmodel->num_surfaces = nummatts;
-
-       if (numanimkeys != loadmodel->num_bones * loadmodel->numframes)
-               Host_Error("%s: %s has incorrect number of animation keys\n", animname, pchunk->id);
+       loadmodel->num_textures = loadmodel->nummodelsurfaces = loadmodel->num_surfaces = nummatts;
+       // 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[2]) + numvertexboneweights * sizeof(surfmeshvertexboneweight_t) + loadmodel->num_poses * sizeof(float[3][4]) + 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->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
+       loadmodel->surfmesh.num_vertices = meshvertices;
+       loadmodel->surfmesh.num_triangles = meshtriangles;
+       loadmodel->surfmesh.num_vertexboneweights = numvertexboneweights;
+       loadmodel->surfmesh.data_element3i = (int *)data;data += meshtriangles * sizeof(int[3]);
+       loadmodel->surfmesh.data_neighbor3i = (int *)data;data += meshtriangles * sizeof(int[3]);
+       loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += meshvertices * sizeof(float[2]);
+       loadmodel->surfmesh.data_vertexboneweights = (surfmeshvertexboneweight_t *)data;data += numvertexboneweights * sizeof(surfmeshvertexboneweight_t);
+       loadmodel->data_poses = (float *)data;data += loadmodel->num_poses * sizeof(float[3][4]);
+       loadmodel->skinscenes = (animscene_t *)data;data += loadmodel->numskins * sizeof(animscene_t);
+       loadmodel->data_bones = (aliasbone_t *)data;data += loadmodel->num_bones * sizeof(aliasbone_t);
+       loadmodel->animscenes = (animscene_t *)data;data += loadmodel->numframes * sizeof(animscene_t);
 
-       loadmodel->data_poses = (float *)Mem_Alloc(loadmodel->mempool, loadmodel->num_poses * sizeof(float[12]));
-       loadmodel->animscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, loadmodel->numframes * sizeof(animscene_t));
-       loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
-       loadmodel->data_surfaces = (msurface_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t));
-       loadmodel->surfacelist = (int *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(int));
-       loadmodel->data_bones = (aliasbone_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_bones * sizeof(aliasbone_t));
-
-       loadmodel->meshlist = (surfmesh_t **)Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t *));
-       mesh = loadmodel->meshlist[0] = (surfmesh_t *)Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t));
-       mesh->num_vertices = numvtxw;
-       mesh->num_triangles = numfaces;
-       mesh->data_element3i = (int *)Mem_Alloc(loadmodel->mempool, mesh->num_triangles * sizeof(int[3]));
-       mesh->data_neighbor3i = (int *)Mem_Alloc(loadmodel->mempool, mesh->num_triangles * sizeof(int[3]));
-       mesh->data_texcoordtexture2f = (float *)Mem_Alloc(loadmodel->mempool, mesh->num_vertices * sizeof(float[2]));
+       for (i = 0;i < loadmodel->numskins;i++)
+       {
+               loadmodel->skinscenes[i].firstframe = i;
+               loadmodel->skinscenes[i].framecount = 1;
+               loadmodel->skinscenes[i].loop = true;
+               loadmodel->skinscenes[i].framerate = 10;
+       }
 
        // create surfaces
        for (index = 0, i = 0;index < nummatts;index++)
@@ -2085,17 +2118,16 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                        for (j = 0;j < loadmodel->numskins;j++)
                                Mod_BuildAliasSkinFromSkinFrame(loadmodel->data_textures + index + j * loadmodel->num_surfaces, NULL);
                loadmodel->surfacelist[index] = index;
-               loadmodel->data_surfaces[index].groupmesh = loadmodel->meshlist[0];
                loadmodel->data_surfaces[index].texture = loadmodel->data_textures + index;
                loadmodel->data_surfaces[index].num_firstvertex = 0;
-               loadmodel->data_surfaces[index].num_vertices = loadmodel->meshlist[0]->num_vertices;
+               loadmodel->data_surfaces[index].num_vertices = loadmodel->surfmesh.num_vertices;
        }
 
        // copy over the texcoords
        for (index = 0;index < numvtxw;index++)
        {
-               mesh->data_texcoordtexture2f[index*2+0] = vtxw[index].texcoord[0];
-               mesh->data_texcoordtexture2f[index*2+1] = vtxw[index].texcoord[1];
+               loadmodel->surfmesh.data_texcoordtexture2f[index*2+0] = vtxw[index].texcoord[0];
+               loadmodel->surfmesh.data_texcoordtexture2f[index*2+1] = vtxw[index].texcoord[1];
        }
 
        // loading the faces is complicated because we need to sort them into surfaces by mattindex
@@ -2110,9 +2142,9 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        for (index = 0;index < numfaces;index++)
        {
                i = (loadmodel->data_surfaces[faces[index].mattindex].num_firsttriangle + loadmodel->data_surfaces[faces[index].mattindex].num_triangles++)*3;
-               mesh->data_element3i[i+0] = faces[index].vtxwindex[0];
-               mesh->data_element3i[i+1] = faces[index].vtxwindex[1];
-               mesh->data_element3i[i+2] = faces[index].vtxwindex[2];
+               loadmodel->surfmesh.data_element3i[i+0] = faces[index].vtxwindex[0];
+               loadmodel->surfmesh.data_element3i[i+1] = faces[index].vtxwindex[1];
+               loadmodel->surfmesh.data_element3i[i+2] = faces[index].vtxwindex[2];
        }
 
        // copy over the bones
@@ -2121,17 +2153,11 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                strlcpy(loadmodel->data_bones[index].name, bones[index].name, sizeof(loadmodel->data_bones[index].name));
                loadmodel->data_bones[index].parent = (index || bones[index].parent > 0) ? bones[index].parent : -1;
                if (loadmodel->data_bones[index].parent >= index)
-                       Host_Error("%s bone[%i].parent >= %i\n", loadmodel->name, index, index);
+                       Host_Error("%s bone[%i].parent >= %i", loadmodel->name, index, index);
        }
 
        // build bone-relative vertex weights from the psk point weights
-       mesh->num_vertexboneweights = 0;
-       for (index = 0;index < numvtxw;index++)
-               for (j = 0;j < numrawweights;j++)
-                       if (rawweights[j].pntsindex == vtxw[index].pntsindex)
-                               mesh->num_vertexboneweights++;
-       mesh->data_vertexboneweights = (surfmeshvertexboneweight_t *)Mem_Alloc(loadmodel->mempool, mesh->num_vertexboneweights * sizeof(surfmeshvertexboneweight_t));
-       mesh->num_vertexboneweights = 0;
+       numvertexboneweights = 0;
        for (index = 0;index < numvtxw;index++)
        {
                for (j = 0;j < numrawweights;j++)
@@ -2139,10 +2165,10 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                        if (rawweights[j].pntsindex == vtxw[index].pntsindex)
                        {
                                matrix4x4_t matrix, inversematrix;
-                               mesh->data_vertexboneweights[mesh->num_vertexboneweights].vertexindex = index;
-                               mesh->data_vertexboneweights[mesh->num_vertexboneweights].boneindex = rawweights[j].boneindex;
-                               mesh->data_vertexboneweights[mesh->num_vertexboneweights].weight = rawweights[j].weight;
-                               Matrix4x4_CreateIdentity(&matrix);
+                               loadmodel->surfmesh.data_vertexboneweights[numvertexboneweights].vertexindex = index;
+                               loadmodel->surfmesh.data_vertexboneweights[numvertexboneweights].boneindex = rawweights[j].boneindex;
+                               loadmodel->surfmesh.data_vertexboneweights[numvertexboneweights].weight = rawweights[j].weight;
+                               matrix = identitymatrix;
                                for (i = rawweights[j].boneindex;i >= 0;i = loadmodel->data_bones[i].parent)
                                {
                                        matrix4x4_t childmatrix, tempmatrix;
@@ -2151,9 +2177,9 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                                        Matrix4x4_Concat(&matrix, &tempmatrix, &childmatrix);
                                }
                                Matrix4x4_Invert_Simple(&inversematrix, &matrix);
-                               Matrix4x4_Transform(&inversematrix, pnts[rawweights[j].pntsindex].origin, mesh->data_vertexboneweights[mesh->num_vertexboneweights].origin);
-                               VectorScale(mesh->data_vertexboneweights[mesh->num_vertexboneweights].origin, mesh->data_vertexboneweights[mesh->num_vertexboneweights].weight, mesh->data_vertexboneweights[mesh->num_vertexboneweights].origin);
-                               mesh->num_vertexboneweights++;
+                               Matrix4x4_Transform(&inversematrix, pnts[rawweights[j].pntsindex].origin, loadmodel->surfmesh.data_vertexboneweights[numvertexboneweights].origin);
+                               VectorScale(loadmodel->surfmesh.data_vertexboneweights[numvertexboneweights].origin, loadmodel->surfmesh.data_vertexboneweights[numvertexboneweights].weight, loadmodel->surfmesh.data_vertexboneweights[numvertexboneweights].origin);
+                               numvertexboneweights++;
                        }
                }
        }
@@ -2191,9 +2217,10 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        }
 
        // compile extra data we want
-       Mod_ValidateElements(mesh->data_element3i, mesh->num_triangles, mesh->num_vertices, __FILE__, __LINE__);
-       Mod_BuildTriangleNeighbors(mesh->data_neighbor3i, mesh->data_element3i, mesh->num_triangles);
-       Mod_Alias_Mesh_CompileFrameZero(mesh);
+       Mod_ValidateElements(loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles, 0, loadmodel->surfmesh.num_vertices, __FILE__, __LINE__);
+       Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
+       Mod_Alias_Mesh_CompileFrameZero();
+       Mod_FreeSkinFiles(skinfiles);
 
        Mem_Free(animfilebuffer);
 }