]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - model_alias.c
better error handling for overlong srcon commands
[xonotic/darkplaces.git] / model_alias.c
index eb4d7db80e6a48f330be7fdcfbbe83c7b04456c6..dfb43c89f0d6d6ce63f1e79d3c00b9ae97fa21e8 100644 (file)
@@ -28,6 +28,7 @@ cvar_t r_skeletal_debugbonevalue = {0, "r_skeletal_debugbonevalue", "100", "deve
 cvar_t r_skeletal_debugtranslatex = {0, "r_skeletal_debugtranslatex", "1", "development cvar for testing skeletal model code"};
 cvar_t r_skeletal_debugtranslatey = {0, "r_skeletal_debugtranslatey", "1", "development cvar for testing skeletal model code"};
 cvar_t r_skeletal_debugtranslatez = {0, "r_skeletal_debugtranslatez", "1", "development cvar for testing skeletal model code"};
+cvar_t mod_alias_supporttagscale = {0, "mod_alias_supporttagscale", "1", "support scaling factors in bone/tag attachment matrices as supported by MD3"};
 
 float mod_md3_sin[320];
 
@@ -40,6 +41,7 @@ void Mod_AliasInit (void)
        Cvar_RegisterVariable(&r_skeletal_debugtranslatex);
        Cvar_RegisterVariable(&r_skeletal_debugtranslatey);
        Cvar_RegisterVariable(&r_skeletal_debugtranslatez);
+       Cvar_RegisterVariable(&mod_alias_supporttagscale);
        for (i = 0;i < 320;i++)
                mod_md3_sin[i] = sin(i * M_PI * 2.0f / 256.0);
 }
@@ -50,6 +52,7 @@ void Mod_Skeletal_AnimateVertices(const dp_model_t *model, const frameblend_t *f
        // vertex weighted skeletal
        int i, k;
        int blends;
+       float desiredscale[3];
        float boneposerelative[MAX_BONES][12];
        float *matrix, m[12], bonepose[MAX_BONES][12];
 
@@ -58,12 +61,22 @@ void Mod_Skeletal_AnimateVertices(const dp_model_t *model, const frameblend_t *f
        {
                for (k = 0;k < 12;k++)
                        m[k] = 0;
-               for (blends = 0;blends < 4 && frameblend[blends].lerp > 0;blends++)
+               VectorClear(desiredscale);
+               for (blends = 0;blends < MAX_FRAMEBLENDS && frameblend[blends].lerp > 0;blends++)
                {
-                       matrix = model->data_poses + (frameblend[blends].frame * model->num_bones + i) * 12;
+                       matrix = model->data_poses + (frameblend[blends].subframe * model->num_bones + i) * 12;
                        for (k = 0;k < 12;k++)
                                m[k] += matrix[k] * frameblend[blends].lerp;
+                       desiredscale[0] += frameblend[blends].lerp * VectorLength(matrix    );
+                       desiredscale[1] += frameblend[blends].lerp * VectorLength(matrix + 4);
+                       desiredscale[2] += frameblend[blends].lerp * VectorLength(matrix + 8);
                }
+               VectorNormalize(m    );
+               VectorNormalize(m + 4);
+               VectorNormalize(m + 8);
+               VectorScale(m    , desiredscale[0], m    );
+               VectorScale(m + 4, desiredscale[1], m + 4);
+               VectorScale(m + 8, desiredscale[2], m + 8);
                if (i == r_skeletal_debugbone.integer)
                        m[r_skeletal_debugbonecomponent.integer % 12] += r_skeletal_debugbonevalue.value;
                m[3] *= r_skeletal_debugtranslatex.value;
@@ -220,7 +233,7 @@ void Mod_MD3_AnimateVertices(const dp_model_t *model, const frameblend_t *frameb
        int i, numblends, blendnum;
        int numverts = model->surfmesh.num_vertices;
        numblends = 0;
-       for (blendnum = 0;blendnum < 4;blendnum++)
+       for (blendnum = 0;blendnum < MAX_FRAMEBLENDS;blendnum++)
        {
                //VectorMA(translate, model->surfmesh.num_morphmdlframetranslate, frameblend[blendnum].lerp, translate);
                if (frameblend[blendnum].lerp > 0)
@@ -229,7 +242,7 @@ void Mod_MD3_AnimateVertices(const dp_model_t *model, const frameblend_t *frameb
        // special case for the first blend because it avoids some adds and the need to memset the arrays first
        for (blendnum = 0;blendnum < numblends;blendnum++)
        {
-               const md3vertex_t *verts = model->surfmesh.data_morphmd3vertex + numverts * frameblend[blendnum].frame;
+               const md3vertex_t *verts = model->surfmesh.data_morphmd3vertex + numverts * frameblend[blendnum].subframe;
                float scale = frameblend[blendnum].lerp * (1.0f / 64.0f);
                if (blendnum == 0)
                {
@@ -278,7 +291,7 @@ void Mod_MD3_AnimateVertices(const dp_model_t *model, const frameblend_t *frameb
                }
                if (svector3f)
                {
-                       const texvecvertex_t *texvecvert = model->surfmesh.data_morphtexvecvertex + numverts * frameblend[blendnum].frame;
+                       const texvecvertex_t *texvecvert = model->surfmesh.data_morphtexvecvertex + numverts * frameblend[blendnum].subframe;
                        float f = frameblend[blendnum].lerp * (1.0f / 127.0f);
                        if (blendnum == 0)
                        {
@@ -310,10 +323,10 @@ void Mod_MDL_AnimateVertices(const dp_model_t *model, const frameblend_t *frameb
        numblends = 0;
        // blend the frame translates to avoid redundantly doing so on each vertex
        // (a bit of a brain twister but it works)
-       for (blendnum = 0;blendnum < 4;blendnum++)
+       for (blendnum = 0;blendnum < MAX_FRAMEBLENDS;blendnum++)
        {
                if (model->surfmesh.data_morphmd2framesize6f)
-                       VectorMA(translate, frameblend[blendnum].lerp, model->surfmesh.data_morphmd2framesize6f + frameblend[blendnum].frame * 6 + 3, translate);
+                       VectorMA(translate, frameblend[blendnum].lerp, model->surfmesh.data_morphmd2framesize6f + frameblend[blendnum].subframe * 6 + 3, translate);
                else
                        VectorMA(translate, frameblend[blendnum].lerp, model->surfmesh.num_morphmdlframetranslate, translate);
                if (frameblend[blendnum].lerp > 0)
@@ -322,10 +335,10 @@ void Mod_MDL_AnimateVertices(const dp_model_t *model, const frameblend_t *frameb
        // special case for the first blend because it avoids some adds and the need to memset the arrays first
        for (blendnum = 0;blendnum < numblends;blendnum++)
        {
-               const trivertx_t *verts = model->surfmesh.data_morphmdlvertex + numverts * frameblend[blendnum].frame;
+               const trivertx_t *verts = model->surfmesh.data_morphmdlvertex + numverts * frameblend[blendnum].subframe;
                float scale[3];
                if (model->surfmesh.data_morphmd2framesize6f)
-                       VectorScale(model->surfmesh.data_morphmd2framesize6f + frameblend[blendnum].frame * 6, frameblend[blendnum].lerp, scale);
+                       VectorScale(model->surfmesh.data_morphmd2framesize6f + frameblend[blendnum].subframe * 6, frameblend[blendnum].lerp, scale);
                else
                        VectorScale(model->surfmesh.num_morphmdlframescale, frameblend[blendnum].lerp, scale);
                if (blendnum == 0)
@@ -372,7 +385,7 @@ void Mod_MDL_AnimateVertices(const dp_model_t *model, const frameblend_t *frameb
                }
                if (svector3f)
                {
-                       const texvecvertex_t *texvecvert = model->surfmesh.data_morphtexvecvertex + numverts * frameblend[blendnum].frame;
+                       const texvecvertex_t *texvecvert = model->surfmesh.data_morphtexvecvertex + numverts * frameblend[blendnum].subframe;
                        float f = frameblend[blendnum].lerp * (1.0f / 127.0f);
                        if (blendnum == 0)
                        {
@@ -423,9 +436,48 @@ int Mod_Alias_GetTagMatrix(const dp_model_t *model, int poseframe, int tagindex,
                        return 6;
                Matrix4x4_FromArray12FloatGL(outmatrix, model->data_tags[poseframe * model->num_tags + tagindex].matrixgl);
        }
+
+       if(!mod_alias_supporttagscale.integer)
+               Matrix4x4_Normalize3(outmatrix, outmatrix);
+
        return 0;
 }
 
+int Mod_Alias_GetExtendedTagInfoForIndex(const dp_model_t *model, unsigned int skin, int poseframe, int tagindex, int *parentindex, const char **tagname, matrix4x4_t *tag_localmatrix)
+{
+       const float *boneframe;
+
+       if(skin >= (unsigned int)model->numskins)
+               skin = 0;
+
+       if (model->num_bones)
+       {
+               if(tagindex >= model->num_bones || tagindex < 0)
+                       return 1;
+               if (poseframe >= model->num_poses)
+                       return 2;
+
+               boneframe = model->data_poses + poseframe * model->num_bones * 12;
+               *parentindex = model->data_bones[tagindex].parent;
+               *tagname = model->data_bones[tagindex].name;
+               Matrix4x4_FromArray12FloatD3D(tag_localmatrix, boneframe + tagindex * 12);
+               return 0;
+       }
+
+       if (model->num_tags)
+       {
+               if(tagindex >= model->num_tags || tagindex < 0)
+                       return 1;
+               if (poseframe >= model->num_tagframes)
+                       return 2;
+               *tagname = model->data_tags[tagindex].name;
+               Matrix4x4_FromArray12FloatGL(tag_localmatrix, model->data_tags[poseframe * model->num_tags + tagindex].matrixgl);
+               return 0;
+       }
+
+       return 2;
+}
+
 int Mod_Alias_GetTagIndexForName(const dp_model_t *model, unsigned int skin, const char *tagname)
 {
        int i;
@@ -446,7 +498,7 @@ static void Mod_BuildBaseBonePoses(void)
 {
        int i, k;
        double scale;
-       float *basebonepose = Mem_Alloc(tempmempool, loadmodel->num_bones * sizeof(float[12]));
+       float *basebonepose = (float *) Mem_Alloc(tempmempool, loadmodel->num_bones * sizeof(float[12]));
        float *in12f = loadmodel->data_poses;
        float *out12f = basebonepose;
        float *outinv12f = loadmodel->data_baseboneposeinverse;
@@ -493,15 +545,15 @@ static void Mod_Alias_CalculateBoundingBox(void)
        float dist, yawradius, radius;
        float *v;
        float *vertex3f;
-       frameblend_t frameblend[4];
+       frameblend_t frameblend[MAX_FRAMEBLENDS];
        memset(frameblend, 0, sizeof(frameblend));
        frameblend[0].lerp = 1;
-       vertex3f = Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_vertices * sizeof(float[3]));
+       vertex3f = (float *) Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_vertices * sizeof(float[3]));
        VectorClear(loadmodel->normalmins);
        VectorClear(loadmodel->normalmaxs);
        yawradius = 0;
        radius = 0;
-       for (frameblend[0].frame = 0;frameblend[0].frame < loadmodel->num_poses;frameblend[0].frame++)
+       for (frameblend[0].subframe = 0;frameblend[0].subframe < loadmodel->num_poses;frameblend[0].subframe++)
        {
                loadmodel->AnimateVertices(loadmodel, frameblend, vertex3f, NULL, NULL, NULL);
                for (vnum = 0, v = vertex3f;vnum < loadmodel->surfmesh.num_vertices;vnum++, v += 3)
@@ -529,7 +581,8 @@ static void Mod_Alias_CalculateBoundingBox(void)
                                radius = dist;
                }
        }
-       Mem_Free(vertex3f);
+       if (vertex3f)
+               Mem_Free(vertex3f);
        radius = sqrt(radius);
        yawradius = sqrt(yawradius);
        loadmodel->yawmins[0] = loadmodel->yawmins[1] = -yawradius;
@@ -545,8 +598,10 @@ static void Mod_Alias_CalculateBoundingBox(void)
 static void Mod_Alias_MorphMesh_CompileFrames(void)
 {
        int i, j;
-       frameblend_t frameblend[4] = {{0, 1}, {0, 0}, {0, 0}, {0, 0}};
+       frameblend_t frameblend[MAX_FRAMEBLENDS];
        unsigned char *datapointer;
+       memset(frameblend, 0, sizeof(frameblend));
+       frameblend[0].lerp = 1;
        datapointer = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_vertices * (sizeof(float[3]) * 4 + loadmodel->surfmesh.num_morphframes * sizeof(texvecvertex_t)));
        loadmodel->surfmesh.data_vertex3f = (float *)datapointer;datapointer += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
        loadmodel->surfmesh.data_svector3f = (float *)datapointer;datapointer += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
@@ -556,14 +611,14 @@ static void Mod_Alias_MorphMesh_CompileFrames(void)
        // this counts down from the last frame to the first so that the final data in surfmesh is for frame zero (which is what the renderer expects to be there)
        for (i = loadmodel->surfmesh.num_morphframes-1;i >= 0;i--)
        {
-               frameblend[0].frame = i;
+               frameblend[0].subframe = i;
                loadmodel->AnimateVertices(loadmodel, frameblend, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_normal3f, NULL, NULL);
                Mod_BuildTextureVectorsFromNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_texcoordtexture2f, loadmodel->surfmesh.data_normal3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_svector3f, loadmodel->surfmesh.data_tvector3f, r_smoothnormals_areaweighting.integer);
                // encode the svector and tvector in 3 byte format for permanent storage
                for (j = 0;j < loadmodel->surfmesh.num_vertices;j++)
                {
-                       VectorScale(loadmodel->surfmesh.data_svector3f + j * 3, 127.0f, loadmodel->surfmesh.data_morphtexvecvertex[i*loadmodel->surfmesh.num_vertices+j].svec);
-                       VectorScale(loadmodel->surfmesh.data_tvector3f + j * 3, 127.0f, loadmodel->surfmesh.data_morphtexvecvertex[i*loadmodel->surfmesh.num_vertices+j].tvec);
+                       VectorScaleCast(loadmodel->surfmesh.data_svector3f + j * 3, 127.0f, signed char, loadmodel->surfmesh.data_morphtexvecvertex[i*loadmodel->surfmesh.num_vertices+j].svec);
+                       VectorScaleCast(loadmodel->surfmesh.data_tvector3f + j * 3, 127.0f, signed char, loadmodel->surfmesh.data_morphtexvecvertex[i*loadmodel->surfmesh.num_vertices+j].tvec);
                }
        }
 }
@@ -572,7 +627,7 @@ static void Mod_MDLMD2MD3_TraceBox(dp_model_t *model, int frame, trace_t *trace,
 {
        int i;
        float segmentmins[3], segmentmaxs[3];
-       frameblend_t frameblend[4];
+       frameblend_t frameblend[MAX_FRAMEBLENDS];
        msurface_t *surface;
        static int maxvertices = 0;
        static float *vertex3f = NULL;
@@ -581,7 +636,7 @@ static void Mod_MDLMD2MD3_TraceBox(dp_model_t *model, int frame, trace_t *trace,
        trace->realfraction = 1;
        trace->hitsupercontentsmask = hitsupercontentsmask;
        memset(frameblend, 0, sizeof(frameblend));
-       frameblend[0].frame = frame;
+       frameblend[0].subframe = frame;
        frameblend[0].lerp = 1;
        if (maxvertices < model->surfmesh.num_vertices)
        {
@@ -743,7 +798,7 @@ static void Mod_BuildAliasSkinFromSkinFrame(texture_t *texture, skinframe_t *ski
        texture->currentmaterialflags = texture->basematerialflags;
 }
 
-static void Mod_BuildAliasSkinsFromSkinFiles(texture_t *skin, skinfile_t *skinfile, char *meshname, char *shadername)
+static void Mod_BuildAliasSkinsFromSkinFiles(texture_t *skin, skinfile_t *skinfile, const char *meshname, const char *shadername)
 {
        int i;
        skinfileitem_t *skinfileitem;
@@ -767,7 +822,7 @@ static void Mod_BuildAliasSkinsFromSkinFiles(texture_t *skin, skinfile_t *skinfi
                        {
                                // don't render unmentioned meshes
                                Mod_BuildAliasSkinFromSkinFrame(skin, NULL);
-                               skin->basematerialflags = skin->currentmaterialflags = 0;
+                               skin->basematerialflags = skin->currentmaterialflags = MATERIALFLAG_NOSHADOW | MATERIALFLAG_NODRAW;
                        }
                }
        }
@@ -828,8 +883,8 @@ void Mod_IDP0_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
        data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int));
        loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
-       loadmodel->surfacelist = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
-       loadmodel->surfacelist[0] = 0;
+       loadmodel->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
+       loadmodel->sortedmodelsurfaces[0] = 0;
 
        loadmodel->numskins = LittleLong(pinmodel->numskins);
        BOUNDI(loadmodel->numskins,0,65536);
@@ -995,12 +1050,12 @@ void Mod_IDP0_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
        // load the skins
        skinfiles = Mod_LoadSkinFiles();
-       loadmodel->skinscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, loadmodel->numskins * sizeof(animscene_t));
-       loadmodel->num_textures = loadmodel->num_surfaces * totalskins;
-       loadmodel->num_texturesperskin = loadmodel->num_surfaces;
-       loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * totalskins * sizeof(texture_t));
        if (skinfiles)
        {
+               loadmodel->skinscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, loadmodel->numskins * sizeof(animscene_t));
+               loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
+               loadmodel->num_texturesperskin = loadmodel->num_surfaces;
+               loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
                Mod_BuildAliasSkinsFromSkinFiles(loadmodel->data_textures, skinfiles, "default", "");
                Mod_FreeSkinFiles(skinfiles);
                for (i = 0;i < loadmodel->numskins;i++)
@@ -1013,6 +1068,10 @@ void Mod_IDP0_Load(dp_model_t *mod, void *buffer, void *bufferend)
        }
        else
        {
+               loadmodel->skinscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, loadmodel->numskins * sizeof(animscene_t));
+               loadmodel->num_textures = loadmodel->num_surfaces * totalskins;
+               loadmodel->num_texturesperskin = loadmodel->num_surfaces;
+               loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * totalskins * sizeof(texture_t));
                totalskins = 0;
                datapointer = startskins;
                for (i = 0;i < loadmodel->numskins;i++)
@@ -1187,8 +1246,8 @@ void Mod_IDP2_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
        data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->numframes * sizeof(animscene_t) + loadmodel->numframes * sizeof(float[6]) + loadmodel->surfmesh.num_triangles * sizeof(int[3]) + loadmodel->surfmesh.num_triangles * sizeof(int[3]));
        loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
-       loadmodel->surfacelist = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
-       loadmodel->surfacelist[0] = 0;
+       loadmodel->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
+       loadmodel->sortedmodelsurfaces[0] = 0;
        loadmodel->animscenes = (animscene_t *)data;data += loadmodel->numframes * sizeof(animscene_t);
        loadmodel->surfmesh.data_morphmd2framesize6f = (float *)data;data += loadmodel->numframes * sizeof(float[6]);
        loadmodel->surfmesh.data_element3i = (int *)data;data += loadmodel->surfmesh.num_triangles * sizeof(int[3]);
@@ -1451,7 +1510,7 @@ void Mod_IDP3_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->num_texturesperskin = loadmodel->num_surfaces;
        data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t) + meshtriangles * sizeof(int[3]) + meshtriangles * sizeof(int[3]) + (meshvertices <= 65536 ? meshtriangles * sizeof(unsigned short[3]) : 0) + meshvertices * sizeof(float[2]) + meshvertices * loadmodel->numframes * sizeof(md3vertex_t));
        loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
-       loadmodel->surfacelist = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
+       loadmodel->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
        loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
        loadmodel->surfmesh.num_vertices = meshvertices;
        loadmodel->surfmesh.num_triangles = meshtriangles;
@@ -1474,7 +1533,7 @@ void Mod_IDP3_Load(dp_model_t *mod, void *buffer, void *bufferend)
        {
                if (memcmp(pinmesh->identifier, "IDP3", 4))
                        Host_Error("Mod_IDP3_Load: invalid mesh identifier (not IDP3)");
-               loadmodel->surfacelist[i] = i;
+               loadmodel->sortedmodelsurfaces[i] = i;
                surface = loadmodel->data_surfaces + i;
                surface->texture = loadmodel->data_textures + i;
                surface->num_firsttriangle = meshtriangles;
@@ -1513,6 +1572,7 @@ void Mod_IDP3_Load(dp_model_t *mod, void *buffer, void *bufferend)
        Mod_Alias_MorphMesh_CompileFrames();
        Mod_Alias_CalculateBoundingBox();
        Mod_FreeSkinFiles(skinfiles);
+       Mod_MakeSortedSurfaces(loadmodel);
 
        loadmodel->surfmesh.isanimated = loadmodel->numframes > 1
             || (loadmodel->animscenes && loadmodel->animscenes[0].framecount > 1);
@@ -1694,7 +1754,7 @@ void Mod_ZYMOTICMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->num_texturesperskin = loadmodel->num_surfaces;
        data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t) + meshtriangles * sizeof(int[3]) + meshtriangles * sizeof(int[3]) + (meshvertices <= 65536 ? meshtriangles * sizeof(unsigned short[3]) : 0) + meshvertices * sizeof(float[14]) + meshvertices * sizeof(int[4]) + meshvertices * sizeof(float[4]) + loadmodel->num_poses * sizeof(float[12]) + loadmodel->num_bones * sizeof(float[12]));
        loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
-       loadmodel->surfacelist = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
+       loadmodel->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
        loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
        loadmodel->surfmesh.num_vertices = meshvertices;
        loadmodel->surfmesh.num_triangles = meshtriangles;
@@ -1791,7 +1851,7 @@ void Mod_ZYMOTICMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
                if (renderlist + count * 3 > renderlistend || (i == pheader->numshaders - 1 && renderlist + count * 3 != renderlistend))
                        Host_Error("%s corrupt renderlist (wrong size)", loadmodel->name);
 
-               loadmodel->surfacelist[i] = i;
+               loadmodel->sortedmodelsurfaces[i] = i;
                surface = loadmodel->data_surfaces + i;
                surface->texture = loadmodel->data_textures + i;
                surface->num_firsttriangle = meshtriangles;
@@ -1827,6 +1887,7 @@ void Mod_ZYMOTICMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        Mod_FreeSkinFiles(skinfiles);
        Mem_Free(vertbonecounts);
        Mem_Free(verts);
+       Mod_MakeSortedSurfaces(loadmodel);
 
        // compute all the mesh information that was not loaded from the file
        Mod_ValidateElements(loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles, 0, loadmodel->surfmesh.num_vertices, __FILE__, __LINE__);
@@ -1943,7 +2004,7 @@ void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        // do most allocations as one merged chunk
        data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t) + meshtriangles * sizeof(int[3]) + (meshvertices <= 65536 ? meshtriangles * sizeof(unsigned short[3]) : 0) + meshtriangles * sizeof(int[3]) + meshvertices * (sizeof(float[14]) + sizeof(int[4]) + sizeof(float[4])) + loadmodel->num_poses * sizeof(float[12]) + loadmodel->num_bones * sizeof(float[12]) + loadmodel->numskins * sizeof(animscene_t) + loadmodel->num_bones * sizeof(aliasbone_t) + loadmodel->numframes * sizeof(animscene_t));
        loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
-       loadmodel->surfacelist = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
+       loadmodel->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
        loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
        loadmodel->surfmesh.num_vertices = meshvertices;
        loadmodel->surfmesh.num_triangles = meshtriangles;
@@ -2029,7 +2090,7 @@ void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
                const float *intexcoord;
                msurface_t *surface;
 
-               loadmodel->surfacelist[i] = i;
+               loadmodel->sortedmodelsurfaces[i] = i;
                surface = loadmodel->data_surfaces + i;
                surface->texture = loadmodel->data_textures + i;
                surface->num_firsttriangle = meshtriangles;
@@ -2130,6 +2191,7 @@ void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        }
        Z_Free(bonepose);
        Mod_FreeSkinFiles(skinfiles);
+       Mod_MakeSortedSurfaces(loadmodel);
 
        // compute all the mesh information that was not loaded from the file
        Mod_BuildBaseBonePoses();
@@ -2533,7 +2595,7 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        size = loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t) + loadmodel->surfmesh.num_triangles * sizeof(int[3]) + loadmodel->surfmesh.num_triangles * sizeof(int[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[2]) + loadmodel->surfmesh.num_vertices * sizeof(int[4]) + loadmodel->surfmesh.num_vertices * sizeof(float[4]) + /*loadmodel->num_poses * sizeof(float[12]) + */loadmodel->num_bones * sizeof(float[12]) + loadmodel->numskins * sizeof(animscene_t) + loadmodel->num_bones * sizeof(aliasbone_t) + loadmodel->numframes * sizeof(animscene_t) + ((loadmodel->surfmesh.num_vertices <= 65536) ? (loadmodel->surfmesh.num_triangles * sizeof(unsigned short[3])) : 0);
        data = (unsigned char *)Mem_Alloc(loadmodel->mempool, size);
        loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
-       loadmodel->surfacelist = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
+       loadmodel->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
        loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
        loadmodel->surfmesh.data_element3i = (int *)data;data += loadmodel->surfmesh.num_triangles * sizeof(int[3]);
        loadmodel->surfmesh.data_neighbor3i = (int *)data;data += loadmodel->surfmesh.num_triangles * sizeof(int[3]);
@@ -2555,7 +2617,7 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
                for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
                        loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
        }
-       loadmodel->data_poses = Mem_Alloc(loadmodel->mempool, loadmodel->num_poses * sizeof(float[12]));
+       loadmodel->data_poses = (float *) Mem_Alloc(loadmodel->mempool, loadmodel->num_poses * sizeof(float[12]));
 
        for (i = 0;i < loadmodel->numskins;i++)
        {
@@ -2570,7 +2632,7 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        {
                // since psk models do not have named sections, reuse their shader name as the section name
                Mod_BuildAliasSkinsFromSkinFiles(loadmodel->data_textures + index, skinfiles, matts[index].name, matts[index].name);
-               loadmodel->surfacelist[index] = index;
+               loadmodel->sortedmodelsurfaces[index] = index;
                loadmodel->data_surfaces[index].texture = loadmodel->data_textures + index;
                loadmodel->data_surfaces[index].num_firstvertex = 0;
                loadmodel->data_surfaces[index].num_vertices = loadmodel->surfmesh.num_vertices;
@@ -2677,6 +2739,7 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        }
        Mod_FreeSkinFiles(skinfiles);
        Mem_Free(animfilebuffer);
+       Mod_MakeSortedSurfaces(loadmodel);
 
        // compute all the mesh information that was not loaded from the file
        // TODO: honor smoothing groups somehow?
@@ -2690,3 +2753,429 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->surfmesh.isanimated = loadmodel->numframes > 1 || loadmodel->animscenes[0].framecount > 1;
 }
 
+void Mod_OBJ_Load(dp_model_t *mod, void *buffer, void *bufferend)
+{
+#if 0
+       const char *textbase = (char *)buffer, *text = textbase;
+       char *s;
+       char *argv[512];
+       char line[1024];
+       char materialname[MAX_QPATH];
+       int j, index1, index2, index3, first, prev, index;
+       int argc;
+       int linelen;
+       int numtriangles = 0;
+       int maxtriangles = 32768;
+       int *element3i = Mem_Alloc(tempmempool, maxtriangles * sizeof(int[3]));
+       int *oldelement3i;
+       int numsurfaces = 0;
+       int maxsurfaces = 0;
+       msurface_t *surfaces = NULL;
+       int linenumber = 0;
+       int hashindex;
+       float *v, *vt, *vn;
+       float *oldv, *oldvt, *oldvn;
+       int maxv = 65536, numv = 1;
+       int maxvt = 65536, numvt = 1;
+       int maxvn = 65536, numvn = 1;
+       int maxverthash = 65536, numverthash = 0;
+       int numhashindex = 65536;
+       struct objverthash_s
+       {
+               struct objverthash_s *next;
+               int s;
+               int v;
+               int vt;
+               int vn;
+       }
+       *hash, **verthash = Mem_Alloc(tempmempool, numhashindex * sizeof(*verthash)), *verthashdata = Mem_Alloc(tempmempool, maxverthash * sizeof(*verthashdata)), *oldverthashdata;
+       skinfile_t *skinfiles;
+
+       dpsnprintf(materialname, sizeof(materialname), "%s", loadmodel->name);
+
+       skinfiles = Mod_LoadSkinFiles();
+
+       loadmodel->modeldatatypestring = "OBJ";
+
+       loadmodel->type = mod_alias;
+       loadmodel->AnimateVertices = NULL;
+       loadmodel->DrawSky = NULL;
+       loadmodel->DrawAddWaterPlanes = NULL;
+       loadmodel->Draw = R_Q1BSP_Draw;
+       loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
+       loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
+       loadmodel->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
+       loadmodel->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
+       loadmodel->DrawLight = R_Q1BSP_DrawLight;
+       loadmodel->TraceBox = Mod_MDLMD2MD3_TraceBox;
+       loadmodel->PointSuperContents = NULL;
+
+       // parse the OBJ text now
+       for(;;)
+       {
+               if (!*text)
+                       break;
+               linenumber++;
+               linelen = 0;
+               for (linelen = 0;text[linelen] && text[linelen] != '\r' && text[linelen] != '\n';linelen++)
+                       line[linelen] = text[linelen];
+               line[linelen] = 0;
+               for (argc = 0;argc < (int)(sizeof(argv)/sizeof(argv[0]));argc++)
+                       argv[argc] = "";
+               argc = 0;
+               s = line;
+               while (*s == ' ' || *s == '\t')
+                       s++;
+               while (*s)
+               {
+                       argv[argc++] = s;
+                       while (*s > ' ')
+                               s++;
+                       if (!*s)
+                               break;
+                       *s++ = 0;
+                       while (*s == ' ' || *s == '\t')
+                               s++;
+               }
+               if (!argc)
+                       continue;
+               if (argv[0][0] == '#')
+                       continue;
+               if (!strcmp(argv[0], "v"))
+               {
+                       if (maxv <= numv)
+                       {
+                               maxv *= 2;
+                               oldv = v;
+                               v = Mem_Alloc(tempmempool, maxv * sizeof(float[3]));
+                               if (oldv)
+                               {
+                                       memcpy(v, oldv, numv * sizeof(float[3]));
+                                       Mem_Free(oldv);
+                               }
+                       }
+                       v[numv*3+0] = atof(argv[1]);
+                       v[numv*3+1] = atof(argv[2]);
+                       v[numv*3+2] = atof(argv[3]);
+                       numv++;
+               }
+               else if (!strcmp(argv[0], "vt"))
+               {
+                       if (maxvt <= numvt)
+                       {
+                               maxvt *= 2;
+                               oldvt = vt;
+                               vt = Mem_Alloc(tempmempool, maxvt * sizeof(float[2]));
+                               if (oldvt)
+                               {
+                                       memcpy(vt, oldvt, numvt * sizeof(float[2]));
+                                       Mem_Free(oldvt);
+                               }
+                       }
+                       vt[numvt*2+0] = atof(argv[1]);
+                       vt[numvt*2+1] = atof(argv[2]);
+                       numvt++;
+               }
+               else if (!strcmp(argv[0], "vn"))
+               {
+                       if (maxvn <= numvn)
+                       {
+                               maxvn *= 2;
+                               oldvn = vn;
+                               vn = Mem_Alloc(tempmempool, maxvn * sizeof(float[3]));
+                               if (oldvn)
+                               {
+                                       memcpy(vn, oldvn, numvn * sizeof(float[3]));
+                                       Mem_Free(oldvn);
+                               }
+                       }
+                       vn[numvn*3+0] = atof(argv[1]);
+                       vn[numvn*3+1] = atof(argv[2]);
+                       vn[numvn*3+2] = atof(argv[3]);
+                       numvn++;
+               }
+               else if (!strcmp(argv[0], "f"))
+               {
+                       if (!surface)
+                       {
+                               if (maxsurfaces <= numsurfaces)
+                               {
+                                       maxsurfaces++;
+                                       oldsurfaces = surfaces;
+                                       surfaces = Mem_Alloc(tempmempool, maxsurfaces * sizeof(*surfaces));
+                                       if (oldsurfaces)
+                                       {
+                                               memcpy(surfaces, oldsurfaces, numsurfaces * sizeof(*surfaces));
+                                               Mem_Free(oldsurfaces);
+                                       }
+                               }
+                               surface = surfaces + numsurfaces++;
+                               surface->
+                       }
+                       for (j = 1;j < argc;j++)
+                       {
+                               index1 = atoi(argv[j]);
+                               while(argv[j][0] && argv[j][0] != '/')
+                                       argv[j]++;
+                               if (argv[j][0])
+                                       argv[j]++;
+                               if (index1 < 0)
+                                       index1 = numv + 1 - index1;
+                               index2 = atoi(argv[j]);
+                               if (index2 < 0)
+                                       index2 = numvt + 1 - index2;
+                               while(argv[j][0] && argv[j][0] != '/')
+                                       argv[j]++;
+                               if (argv[j][0])
+                                       argv[j]++;
+                               index3 = atoi(argv[j]);
+                               if (index3 < 0)
+                                       index3 = numvn + 1 - index3;
+                               hashindex = (index1 + index2 * 3571 + index3 * 42589) & (numhashindex - 1);
+                               for (hash = verthash[hashindex];hash;hash = hash->next)
+                                       if (hash->surface == numsurfaces-1 && hash->v == index1 && hash->vt == index2 && hash->vn == index3)
+                                               break;
+                               if (!hash)
+                               {
+                                       if (maxverthash <= numverthash)
+                                       {
+                                               maxverthash *= 2;
+                                               oldverthashdata = verthashdata;
+                                               verthashdata = Mem_Alloc(tempmempool, maxverthash * sizeof(*verthashdata));
+                                               if (oldverthashdata)
+                                               {
+                                                       memcpy(verthashdata, oldverthashdata, numverthash * sizeof(*verthashdata));
+                                                       Mem_Free(oldverthashdata);
+                                               }
+                                       }
+                                       hash = verthashdata + numverthash++;
+                                       hash->next = verthash[hashindex];
+                                       hash->s = numsurfaces;
+                                       hash->v = index1;
+                                       hash->vt = index2;
+                                       hash->vn = index3;
+                                       verthash[hashindex] = hash;
+                               }
+                               index = (int)((size_t)(hash - verthashdata));
+                               if (j == 1)
+                                       first = index;
+                               else if (j >= 3)
+                               {
+                                       if (maxtriangles <= numtriangles)
+                                       {
+                                               maxtriangles *= 2;
+                                               oldelement3i = element3i;
+                                               element3i = Mem_Alloc(tempmempool, numtriangles * sizeof(int[3]));
+                                               if (oldelement3i)
+                                               {
+                                                       memcpy(element3i, oldelement3i, numtriangles * sizeof(int[3]));
+                                                       Mem_Free(oldelement3i);
+                                               }
+                                       }
+                                       element3i[numtriangles*3+0] = first;
+                                       element3i[numtriangles*3+1] = prev;
+                                       element3i[numtriangles*3+2] = index;
+                                       numtriangles++;
+                               }
+                               prev = index;
+                       }
+               }
+               else if (!strcmp(argv[0], "o") || !strcmp(argv[0], "g"))
+                       surface = NULL;
+               else if (!!strcmp(argv[0], "usemtl"))
+               {
+                       surface = NULL;
+                       strlcpy(materialname, argv[1], sizeof(materialname);
+               }
+               text += linelen;
+               if (*text == '\r')
+                       text++;
+               if (*text == '\n')
+                       text++;
+       }
+
+       if (skinfiles)
+               Mod_FreeSkinFiles(skinfiles);
+
+       // now that we have the OBJ data loaded as-is, we can convert it
+       loadmodel->numskins = LittleLong(pinmodel->num_skins);
+       numxyz = LittleLong(pinmodel->num_xyz);
+       numst = LittleLong(pinmodel->num_st);
+       loadmodel->surfmesh.num_triangles = LittleLong(pinmodel->num_tris);
+       loadmodel->numframes = LittleLong(pinmodel->num_frames);
+       loadmodel->surfmesh.num_morphframes = loadmodel->numframes;
+       loadmodel->num_poses = loadmodel->surfmesh.num_morphframes;
+       skinwidth = LittleLong(pinmodel->skinwidth);
+       skinheight = LittleLong(pinmodel->skinheight);
+       iskinwidth = 1.0f / skinwidth;
+       iskinheight = 1.0f / skinheight;
+
+       loadmodel->num_surfaces = 1;
+       loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
+       data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->numframes * sizeof(animscene_t) + loadmodel->numframes * sizeof(float[6]) + loadmodel->surfmesh.num_triangles * sizeof(int[3]) + loadmodel->surfmesh.num_triangles * sizeof(int[3]));
+       loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
+       loadmodel->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
+       loadmodel->sortedmodelsurfaces[0] = 0;
+       loadmodel->animscenes = (animscene_t *)data;data += loadmodel->numframes * sizeof(animscene_t);
+       loadmodel->surfmesh.data_morphmd2framesize6f = (float *)data;data += loadmodel->numframes * sizeof(float[6]);
+       loadmodel->surfmesh.data_element3i = (int *)data;data += loadmodel->surfmesh.num_triangles * sizeof(int[3]);
+       loadmodel->surfmesh.data_neighbor3i = (int *)data;data += loadmodel->surfmesh.num_triangles * sizeof(int[3]);
+
+       loadmodel->synctype = ST_RAND;
+
+       // load the skins
+       inskin = (char *)(base + LittleLong(pinmodel->ofs_skins));
+       skinfiles = Mod_LoadSkinFiles();
+       if (skinfiles)
+       {
+               loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
+               loadmodel->num_texturesperskin = loadmodel->num_surfaces;
+               loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
+               Mod_BuildAliasSkinsFromSkinFiles(loadmodel->data_textures, skinfiles, "default", "");
+               Mod_FreeSkinFiles(skinfiles);
+       }
+       else if (loadmodel->numskins)
+       {
+               // skins found (most likely not a player model)
+               loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
+               loadmodel->num_texturesperskin = loadmodel->num_surfaces;
+               loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
+               for (i = 0;i < loadmodel->numskins;i++, inskin += MD2_SKINNAME)
+                       Mod_LoadTextureFromQ3Shader(loadmodel->data_textures + i * loadmodel->num_surfaces, inskin, true, true, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP | TEXF_COMPRESS);
+       }
+       else
+       {
+               // no skins (most likely a player model)
+               loadmodel->numskins = 1;
+               loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
+               loadmodel->num_texturesperskin = loadmodel->num_surfaces;
+               loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
+               Mod_BuildAliasSkinFromSkinFrame(loadmodel->data_textures, NULL);
+       }
+
+       loadmodel->skinscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, sizeof(animscene_t) * loadmodel->numskins);
+       for (i = 0;i < loadmodel->numskins;i++)
+       {
+               loadmodel->skinscenes[i].firstframe = i;
+               loadmodel->skinscenes[i].framecount = 1;
+               loadmodel->skinscenes[i].loop = true;
+               loadmodel->skinscenes[i].framerate = 10;
+       }
+
+       // load the triangles and stvert data
+       inst = (unsigned short *)(base + LittleLong(pinmodel->ofs_st));
+       intri = (md2triangle_t *)(base + LittleLong(pinmodel->ofs_tris));
+       md2verthash = (struct md2verthash_s **)Mem_Alloc(tempmempool, 65536 * sizeof(hash));
+       md2verthashdata = (struct md2verthash_s *)Mem_Alloc(tempmempool, loadmodel->surfmesh.num_triangles * 3 * sizeof(*hash));
+       // swap the triangle list
+       loadmodel->surfmesh.num_vertices = 0;
+       for (i = 0;i < loadmodel->surfmesh.num_triangles;i++)
+       {
+               for (j = 0;j < 3;j++)
+               {
+                       xyz = (unsigned short) LittleShort (intri[i].index_xyz[j]);
+                       st = (unsigned short) LittleShort (intri[i].index_st[j]);
+                       if (xyz >= numxyz)
+                       {
+                               Con_Printf("%s has an invalid xyz index (%i) on triangle %i, resetting to 0\n", loadmodel->name, xyz, i);
+                               xyz = 0;
+                       }
+                       if (st >= numst)
+                       {
+                               Con_Printf("%s has an invalid st index (%i) on triangle %i, resetting to 0\n", loadmodel->name, st, i);
+                               st = 0;
+                       }
+                       hashindex = (xyz * 256 + st) & 65535;
+                       for (hash = md2verthash[hashindex];hash;hash = hash->next)
+                               if (hash->xyz == xyz && hash->st == st)
+                                       break;
+                       if (hash == NULL)
+                       {
+                               hash = md2verthashdata + loadmodel->surfmesh.num_vertices++;
+                               hash->xyz = xyz;
+                               hash->st = st;
+                               hash->next = md2verthash[hashindex];
+                               md2verthash[hashindex] = hash;
+                       }
+                       loadmodel->surfmesh.data_element3i[i*3+j] = (hash - md2verthashdata);
+               }
+       }
+
+       vertremap = (int *)Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_vertices * sizeof(int));
+       data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_vertices * sizeof(float[2]) + loadmodel->surfmesh.num_vertices * loadmodel->surfmesh.num_morphframes * sizeof(trivertx_t));
+       loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
+       loadmodel->surfmesh.data_morphmdlvertex = (trivertx_t *)data;data += loadmodel->surfmesh.num_vertices * loadmodel->surfmesh.num_morphframes * sizeof(trivertx_t);
+       for (i = 0;i < loadmodel->surfmesh.num_vertices;i++)
+       {
+               int sts, stt;
+               hash = md2verthashdata + i;
+               vertremap[i] = hash->xyz;
+               sts = LittleShort(inst[hash->st*2+0]);
+               stt = LittleShort(inst[hash->st*2+1]);
+               if (sts < 0 || sts >= skinwidth || stt < 0 || stt >= skinheight)
+               {
+                       Con_Printf("%s has an invalid skin coordinate (%i %i) on vert %i, changing to 0 0\n", loadmodel->name, sts, stt, i);
+                       sts = 0;
+                       stt = 0;
+               }
+               loadmodel->surfmesh.data_texcoordtexture2f[i*2+0] = sts * iskinwidth;
+               loadmodel->surfmesh.data_texcoordtexture2f[i*2+1] = stt * iskinheight;
+       }
+
+       Mem_Free(md2verthash);
+       Mem_Free(md2verthashdata);
+
+       // generate ushort elements array if possible
+       if (loadmodel->surfmesh.num_vertices <= 65536)
+       {
+               loadmodel->surfmesh.data_element3s = (unsigned short *)Mem_Alloc(loadmodel->mempool, sizeof(unsigned short[3]) * loadmodel->surfmesh.num_triangles);
+               for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
+                       loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
+       }
+
+       // load the frames
+       datapointer = (base + LittleLong(pinmodel->ofs_frames));
+       for (i = 0;i < loadmodel->surfmesh.num_morphframes;i++)
+       {
+               int k;
+               trivertx_t *v;
+               trivertx_t *out;
+               pinframe = (md2frame_t *)datapointer;
+               datapointer += sizeof(md2frame_t);
+               // store the frame scale/translate into the appropriate array
+               for (j = 0;j < 3;j++)
+               {
+                       loadmodel->surfmesh.data_morphmd2framesize6f[i*6+j] = LittleFloat(pinframe->scale[j]);
+                       loadmodel->surfmesh.data_morphmd2framesize6f[i*6+3+j] = LittleFloat(pinframe->translate[j]);
+               }
+               // convert the vertices
+               v = (trivertx_t *)datapointer;
+               out = loadmodel->surfmesh.data_morphmdlvertex + i * loadmodel->surfmesh.num_vertices;
+               for (k = 0;k < loadmodel->surfmesh.num_vertices;k++)
+                       out[k] = v[vertremap[k]];
+               datapointer += numxyz * sizeof(trivertx_t);
+
+               strlcpy(loadmodel->animscenes[i].name, pinframe->name, sizeof(loadmodel->animscenes[i].name));
+               loadmodel->animscenes[i].firstframe = i;
+               loadmodel->animscenes[i].framecount = 1;
+               loadmodel->animscenes[i].framerate = 10;
+               loadmodel->animscenes[i].loop = true;
+       }
+
+       Mem_Free(vertremap);
+
+       Mod_MakeSortedSurfaces(loadmodel);
+       Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
+       Mod_Alias_CalculateBoundingBox();
+       Mod_Alias_MorphMesh_CompileFrames();
+
+       surface = loadmodel->data_surfaces;
+       surface->texture = loadmodel->data_textures;
+       surface->num_firsttriangle = 0;
+       surface->num_triangles = loadmodel->surfmesh.num_triangles;
+       surface->num_firstvertex = 0;
+       surface->num_vertices = loadmodel->surfmesh.num_vertices;
+
+       loadmodel->surfmesh.isanimated = false;
+#endif
+}