]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - model_shared.c
physics: fix and refactor unsticking
[xonotic/darkplaces.git] / model_shared.c
index e70f47c2fb26359862c6b52a7c55242dd8e1712d..d6cea915396455ffce1b181e5ed6a0f99018af31 100644 (file)
@@ -217,7 +217,7 @@ void Mod_UnloadModel (model_t *mod)
        if (developer_loading.integer)
                Con_Printf("unloading model %s\n", mod->name);
 
-       strlcpy(name, mod->name, sizeof(name));
+       dp_strlcpy(name, mod->name, sizeof(name));
        parentmodel = mod->brush.parentmodel;
        used = mod->used;
        if (mod->mempool)
@@ -246,7 +246,7 @@ void Mod_UnloadModel (model_t *mod)
        // clear the struct to make it available
        memset(mod, 0, sizeof(model_t));
        // restore the fields we want to preserve
-       strlcpy(mod->name, name, sizeof(mod->name));
+       dp_strlcpy(mod->name, name, sizeof(mod->name));
        mod->brush.parentmodel = parentmodel;
        mod->used = used;
        mod->loaded = false;
@@ -315,7 +315,7 @@ static int Mod_FrameGroupify_ParseGroups(const char *buf, mod_framegroupify_pars
                name[0] = 0;
                if (bufptr && strcmp(com_token, "\n"))
                {
-                       strlcpy(name, com_token, sizeof(name));
+                       dp_strlcpy(name, com_token, sizeof(name));
                        COM_ParseToken_Simple(&bufptr, true, false, true);
                }
 
@@ -338,7 +338,7 @@ static void Mod_FrameGroupify_ParseGroups_Store (unsigned int i, int start, int
        model_t *mod = (model_t *) pass;
        animscene_t *anim = &mod->animscenes[i];
        if(name)
-               strlcpy(anim->name, name, sizeof(anim[i].name));
+               dp_strlcpy(anim->name, name, sizeof(anim[i].name));
        else
                dpsnprintf(anim->name, sizeof(anim[i].name), "groupified_%d_anim", i);
        anim->firstframe = bound(0, start, mod->num_poses - 1);
@@ -518,10 +518,10 @@ model_t *Mod_LoadModel(model_t *mod, qbool crash, qbool checkdisk)
                // all models use memory, so allocate a memory pool
                mod->mempool = Mem_AllocPool(mod->name, 0, NULL);
 
-               // call the apropriate loader
+               // We need to have a reference to the base model in case we're parsing submodels
                loadmodel = mod;
 
-               // Try matching magic bytes.
+               // Call the appropriate loader. Try matching magic bytes.
                for (i = 0; loader[i].Load; i++)
                {
                        // Headerless formats can just load based on extension. Otherwise match the magic string.
@@ -618,7 +618,7 @@ model_t *Mod_FindName(const char *name, const char *parentname)
 
        // no match found, create a new one
        mod = (model_t *) Mem_ExpandableArray_AllocRecord(&models);
-       strlcpy(mod->name, name, sizeof(mod->name));
+       dp_strlcpy(mod->name, name, sizeof(mod->name));
        if (parentname[0])
                mod->brush.parentmodel = Mod_FindName(parentname, NULL);
        else
@@ -1044,6 +1044,12 @@ void Mod_ShadowMesh_AddMesh(shadowmesh_t *mesh, const float *vertex3f, int numtr
 
        for (i = 0;i < numtris;i++)
        {
+               if ((mesh->numtriangles * 3 + 2) * sizeof(int) + 1 >= ((memheader_t *)((unsigned char *)mesh->element3i - sizeof(memheader_t)))->size)
+               {
+                       // FIXME: we didn't allocate enough space for all the tris, see R_Mod_CompileShadowMap
+                       Con_Print(CON_WARN "Mod_ShadowMesh_AddMesh: insufficient memory allocated!\n");
+                       return;
+               }
                mesh->element3i[mesh->numtriangles * 3 + 0] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 0]);
                mesh->element3i[mesh->numtriangles * 3 + 1] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 1]);
                mesh->element3i[mesh->numtriangles * 3 + 2] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 2]);
@@ -1512,7 +1518,7 @@ void Mod_LoadQ3Shaders(void)
                                        // name
                                        j = (int)strlen(com_token)+1;
                                        custsurfaceparmnames[numcustsurfaceflags] = (char *)Mem_Alloc(tempmempool, j);
-                                       strlcpy(custsurfaceparmnames[numcustsurfaceflags], com_token, j+1);
+                                       dp_strlcpy(custsurfaceparmnames[numcustsurfaceflags], com_token, j+1);
                                        // value
                                        if (COM_ParseToken_QuakeC(&text, false))
                                                custsurfaceflags[numcustsurfaceflags] = strtol(com_token, NULL, 0);
@@ -1574,7 +1580,7 @@ void Mod_LoadQ3Shaders(void)
                        // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
                        // JUST GREP FOR "specularscalemod = 1".
 
-                       strlcpy(shader.name, com_token, sizeof(shader.name));
+                       dp_strlcpy(shader.name, com_token, sizeof(shader.name));
                        if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
                        {
                                Con_DPrintf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[fileindex], com_token);
@@ -1617,7 +1623,7 @@ void Mod_LoadQ3Shaders(void)
                                                                if(j == 0 && !strncasecmp(com_token, "dp_", 3))
                                                                        dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
                                                                else
-                                                                       strlcpy(parameter[j], com_token, sizeof(parameter[j]));
+                                                                       dp_strlcpy(parameter[j], com_token, sizeof(parameter[j]));
                                                                numparameters = j + 1;
                                                        }
                                                        if (!COM_ParseToken_QuakeC(&text, true))
@@ -1872,7 +1878,7 @@ void Mod_LoadQ3Shaders(void)
                                                if(j == 0 && !strncasecmp(com_token, "dp_", 3))
                                                        dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
                                                else
-                                                       strlcpy(parameter[j], com_token, sizeof(parameter[j]));
+                                                       dp_strlcpy(parameter[j], com_token, sizeof(parameter[j]));
                                                numparameters = j + 1;
                                        }
                                        if (!COM_ParseToken_QuakeC(&text, true))
@@ -1982,7 +1988,7 @@ void Mod_LoadQ3Shaders(void)
                                else if (!strcasecmp(parameter[0], "dpnortlight"))
                                        shader.dpnortlight = true;
                                else if (!strcasecmp(parameter[0], "dpreflectcube"))
-                                       strlcpy(shader.dpreflectcube, parameter[1], sizeof(shader.dpreflectcube));
+                                       dp_strlcpy(shader.dpreflectcube, parameter[1], sizeof(shader.dpreflectcube));
                                else if (!strcasecmp(parameter[0], "dpmeshcollisions"))
                                        shader.dpmeshcollisions = true;
                                // this sets dpshaderkill to true if dpshaderkillifcvarzero was used, and to false if dpnoshaderkillifcvarzero was used
@@ -2041,14 +2047,14 @@ void Mod_LoadQ3Shaders(void)
                                {
                                        // some q3 skies don't have the sky parm set
                                        shader.surfaceparms |= Q3SURFACEPARM_SKY;
-                                       strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
+                                       dp_strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
                                }
                                else if (!strcasecmp(parameter[0], "skyparms") && numparameters >= 2)
                                {
                                        // some q3 skies don't have the sky parm set
                                        shader.surfaceparms |= Q3SURFACEPARM_SKY;
                                        if (!atoi(parameter[1]) && strcasecmp(parameter[1], "-"))
-                                               strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
+                                               dp_strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
                                }
                                else if (!strcasecmp(parameter[0], "cull") && numparameters >= 2)
                                {
@@ -2257,12 +2263,7 @@ texture_shaderpass_t *Mod_CreateShaderPassFromQ3ShaderLayer(mempool_t *mempool,
        for (j = 0; j < Q3MAXTCMODS && layer->tcmods[j].tcmod != Q3TCMOD_NONE; j++)
                shaderpass->tcmods[j] = layer->tcmods[j];
        for (j = 0; j < layer->numframes; j++)
-       {
-               for (int i = 0; layer->texturename[j][i]; i++)
-                       if(layer->texturename[j][i] == '\\')
-                               layer->texturename[j][i] = '/';
                shaderpass->skinframes[j] = R_SkinFrame_LoadExternal(layer->texturename[j], texflags, false, true);
-       }
        return shaderpass;
 }
 
@@ -2273,7 +2274,7 @@ qbool Mod_LoadTextureFromQ3Shader(mempool_t *mempool, const char *modelname, tex
        shader_t *shader;
        if (!name)
                name = "";
-       strlcpy(texture->name, name, sizeof(texture->name));
+       dp_strlcpy(texture->name, name, sizeof(texture->name));
        texture->basealpha = 1.0f;
        shader = name[0] ? Mod_LookupQ3Shader(name) : NULL;
 
@@ -2654,7 +2655,7 @@ void Mod_LoadCustomMaterial(mempool_t *mempool, texture_t *texture, const char *
        if (!(materialflags & (MATERIALFLAG_WALL | MATERIALFLAG_SKY)))
                Con_DPrintf("^1Custom texture ^3\"%s\" does not have MATERIALFLAG_WALL set\n", texture->name);
 
-       strlcpy(texture->name, name, sizeof(texture->name));
+       dp_strlcpy(texture->name, name, sizeof(texture->name));
        texture->basealpha = 1.0f;
        texture->basematerialflags = materialflags;
        texture->supercontents = supercontents;
@@ -2754,7 +2755,7 @@ tag_torso,
                        do
                        {
                                if (words < 10)
-                                       strlcpy(word[words++], com_token, sizeof (word[0]));
+                                       dp_strlcpy(word[words++], com_token, sizeof (word[0]));
                                else
                                        wordsoverflow = true;
                        }
@@ -2774,8 +2775,8 @@ tag_torso,
                                        skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
                                        skinfileitem->next = skinfile->items;
                                        skinfile->items = skinfileitem;
-                                       strlcpy (skinfileitem->name, word[1], sizeof (skinfileitem->name));
-                                       strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
+                                       dp_strlcpy (skinfileitem->name, word[1], sizeof (skinfileitem->name));
+                                       dp_strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
                                }
                                else
                                        Con_Printf("Mod_LoadSkinFiles: parsing error in file \"%s_%i.skin\" on line #%i: wrong number of parameters to command \"%s\", see documentation in DP_GFX_SKINFILES extension in dpextensions.qc\n", loadmodel->name, i, line, word[0]);
@@ -2793,8 +2794,8 @@ tag_torso,
                                skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
                                skinfileitem->next = skinfile->items;
                                skinfile->items = skinfileitem;
-                               strlcpy (skinfileitem->name, word[0], sizeof (skinfileitem->name));
-                               strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
+                               dp_strlcpy (skinfileitem->name, word[0], sizeof (skinfileitem->name));
+                               dp_strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
                        }
                        else
                                Con_Printf("Mod_LoadSkinFiles: parsing error in file \"%s_%i.skin\" on line #%i: does not look like tag or mesh specification, or replace command, see documentation in DP_GFX_SKINFILES extension in dpextensions.qc\n", loadmodel->name, i, line);
@@ -2885,7 +2886,7 @@ void Mod_VertexRangeFromElements(int numelements, const int *elements, int *firs
 
 void Mod_SetDrawSkyAndWater(model_t* mod)
 {
-       size_t j;
+       int j;
        uint64_t basematerialflags = 0;
        // by default assume there is no sky or water used in this model
        mod->DrawSky = NULL;
@@ -2936,7 +2937,12 @@ void Mod_MakeSortedSurfaces(model_t *mod)
 {
        // make an optimal set of texture-sorted batches to draw...
        int j, k;
-       Mod_MakeSortedSurfaces_qsortsurface_t *info = (Mod_MakeSortedSurfaces_qsortsurface_t*)R_FrameData_Alloc(mod->num_surfaces * sizeof(*info));
+       Mod_MakeSortedSurfaces_qsortsurface_t *info;
+
+       if(cls.state == ca_dedicated)
+               return;
+
+       info = (Mod_MakeSortedSurfaces_qsortsurface_t*)Mem_Alloc(loadmodel->mempool, mod->num_surfaces * sizeof(*info));
        if (!mod->modelsurfaces_sorted)
                mod->modelsurfaces_sorted = (int *) Mem_Alloc(loadmodel->mempool, mod->num_surfaces * sizeof(*mod->modelsurfaces_sorted));
        // the goal is to sort by submodel (can't change which submodel a surface belongs to), and then by effects and textures
@@ -2952,6 +2958,7 @@ void Mod_MakeSortedSurfaces(model_t *mod)
                        qsort(info + mod->brush.submodels[k]->submodelsurfaces_start, (size_t)mod->brush.submodels[k]->submodelsurfaces_end - mod->brush.submodels[k]->submodelsurfaces_start, sizeof(*info), Mod_MakeSortedSurfaces_qsortfunc);
        for (j = 0; j < mod->num_surfaces; j++)
                mod->modelsurfaces_sorted[j] = info[j].surfaceindex;
+       Mem_Free(info);
 }
 
 void Mod_BuildVBOs(void)
@@ -3057,7 +3064,7 @@ static void Mod_Decompile_OBJ(model_t *model, const char *filename, const char *
                if (textureindex >= maxtextures)
                        continue; // just a precaution
                textureindex = counttextures++;
-               strlcpy(texturenames + textureindex * MAX_QPATH, texname, MAX_QPATH);
+               dp_strlcpy(texturenames + textureindex * MAX_QPATH, texname, MAX_QPATH);
                if (outbufferpos >= outbuffermax >> 1)
                {
                        outbuffermax *= 2;
@@ -3202,7 +3209,7 @@ static void Mod_Decompile_SMD(model_t *model, const char *filename, int firstpos
                        // strangely the smd angles are for a transposed matrix, so we
                        // have to generate a transposed matrix, then convert that...
                        Matrix4x4_FromBonePose7s(&posematrix, model->num_posescale, model->data_poses7s + 7*(model->num_bones * poseindex + transformindex));
-                       Matrix4x4_ToArray12FloatGL(&posematrix, mtest[0]);
+                       Matrix4x4_ToArray12FloatGL(&posematrix, mtest);
                        AnglesFromVectors(angles, mtest[0], mtest[2], false);
                        if (angles[0] >= 180) angles[0] -= 360;
                        if (angles[1] >= 180) angles[1] -= 360;
@@ -3332,7 +3339,7 @@ static void Mod_Decompile_f(cmd_state_t *cmd)
                return;
        }
 
-       strlcpy(inname, Cmd_Argv(cmd, 1), sizeof(inname));
+       dp_strlcpy(inname, Cmd_Argv(cmd, 1), sizeof(inname));
        FS_StripExtension(inname, basename, sizeof(basename));
 
        mod = Mod_ForName(inname, false, true, inname[0] == '*' ? cl.model_name[1] : NULL);
@@ -3373,7 +3380,7 @@ static void Mod_Decompile_f(cmd_state_t *cmd)
                if (l > 0) dpmtextsize += l;
                for (i = 0;i < mod->numframes;i = j)
                {
-                       strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
+                       dp_strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
                        first = mod->animscenes[i].firstframe;
                        if (mod->animscenes[i].framecount > 1)
                        {
@@ -3394,7 +3401,7 @@ static void Mod_Decompile_f(cmd_state_t *cmd)
                                count = mod->num_poses - first;
                                for (j = i + 1;j < mod->numframes;j++)
                                {
-                                       strlcpy(animname2, mod->animscenes[j].name, sizeof(animname2));
+                                       dp_strlcpy(animname2, mod->animscenes[j].name, sizeof(animname2));
                                        for (l = 0, k = (int)strlen(animname2);animname2[l];l++)
                                                if(animname2[l] < '0' || animname2[l] > '9')
                                                        k = l + 1;
@@ -3409,7 +3416,7 @@ static void Mod_Decompile_f(cmd_state_t *cmd)
                                }
                                // if it's only one frame, use the original frame name
                                if (j == i + 1)
-                                       strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
+                                       dp_strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
                                
                        }
                        dpsnprintf(outname, sizeof(outname), "%s_decompiled/%s.smd", basename, animname);
@@ -4436,7 +4443,7 @@ static void Mod_GenerateLightmaps_f(cmd_state_t *cmd)
 void Mod_Mesh_Create(model_t *mod, const char *name)
 {
        memset(mod, 0, sizeof(*mod));
-       strlcpy(mod->name, name, sizeof(mod->name));
+       dp_strlcpy(mod->name, name, sizeof(mod->name));
        mod->mempool = Mem_AllocPool(name, 0, NULL);
        mod->texturepool = R_AllocTexturePool();
        mod->Draw = R_Mod_Draw;
@@ -4460,7 +4467,8 @@ void Mod_Mesh_Reset(model_t *mod)
        mod->num_surfaces = 0;
        mod->surfmesh.num_vertices = 0;
        mod->surfmesh.num_triangles = 0;
-       memset(mod->surfmesh.data_vertexhash, -1, mod->surfmesh.num_vertexhashsize * sizeof(*mod->surfmesh.data_vertexhash));
+       if (mod->surfmesh.data_vertexhash) // UBSan: memset arg 1 isn't allowed to be null, but sometimes this is NULL.
+               memset(mod->surfmesh.data_vertexhash, -1, mod->surfmesh.num_vertexhashsize * sizeof(*mod->surfmesh.data_vertexhash));
        mod->DrawSky = NULL; // will be set if a texture needs it
        mod->DrawAddWaterPlanes = NULL; // will be set if a texture needs it
 }
@@ -4543,31 +4551,10 @@ msurface_t *Mod_Mesh_AddSurface(model_t *mod, texture_t *tex, qbool batchwithpre
        return surf;
 }
 
-static void Mod_Mesh_RebuildHashTable(model_t *mod, msurface_t *surf)
+int Mod_Mesh_IndexForVertex(model_t *mod, msurface_t *surf, float x, float y, float z, float nx, float ny, float nz, float s, float t, float u, float v, float r, float g, float b, float a)
 {
        int hashindex, h, vnum, mask;
        surfmesh_t *mesh = &mod->surfmesh;
-
-       // rebuild the hash table
-       mesh->num_vertexhashsize = 4 * mesh->max_vertices;
-       mesh->num_vertexhashsize &= ~(mesh->num_vertexhashsize - 1); // round down to pow2
-       mesh->data_vertexhash = (int *)Mem_Realloc(mod->mempool, mesh->data_vertexhash, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
-       memset(mesh->data_vertexhash, -1, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
-       mask = mod->surfmesh.num_vertexhashsize - 1;
-       // no need to hash the vertices for the entire model, the latest surface will suffice.
-       for (vnum = surf ? surf->num_firstvertex : 0; vnum < mesh->num_vertices; vnum++)
-       {
-               // this uses prime numbers intentionally for computing the hash
-               hashindex = (unsigned int)(mesh->data_vertex3f[vnum * 3 + 0] * 2003 + mesh->data_vertex3f[vnum * 3 + 1] * 4001 + mesh->data_vertex3f[vnum * 3 + 2] * 7919 + mesh->data_normal3f[vnum * 3 + 0] * 4097 + mesh->data_normal3f[vnum * 3 + 1] * 257 + mesh->data_normal3f[vnum * 3 + 2] * 17) & mask;
-               for (h = hashindex; mesh->data_vertexhash[h] >= 0; h = (h + 1) & mask)
-                       ; // just iterate until we find the terminator
-               mesh->data_vertexhash[h] = vnum;
-       }
-}
-
-void Mod_Mesh_CheckResize_Vertex(model_t *mod, msurface_t *surf)
-{
-       surfmesh_t *mesh = &mod->surfmesh;
        if (mesh->max_vertices == mesh->num_vertices)
        {
                mesh->max_vertices = max(mesh->num_vertices * 2, 256);
@@ -4578,15 +4565,36 @@ void Mod_Mesh_CheckResize_Vertex(model_t *mod, msurface_t *surf)
                mesh->data_texcoordtexture2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordtexture2f, mesh->max_vertices * sizeof(float[2]));
                mesh->data_texcoordlightmap2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordlightmap2f, mesh->max_vertices * sizeof(float[2]));
                mesh->data_lightmapcolor4f = (float *)Mem_Realloc(mod->mempool, mesh->data_lightmapcolor4f, mesh->max_vertices * sizeof(float[4]));
-               Mod_Mesh_RebuildHashTable(mod, surf);
+               // rebuild the hash table
+               mesh->num_vertexhashsize = 4 * mesh->max_vertices;
+               mesh->num_vertexhashsize &= ~(mesh->num_vertexhashsize - 1); // round down to pow2
+               mesh->data_vertexhash = (int *)Mem_Realloc(mod->mempool, mesh->data_vertexhash, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
+               memset(mesh->data_vertexhash, -1, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
+               mask = mod->surfmesh.num_vertexhashsize - 1;
+               // no need to hash the vertices for the entire model, the latest surface will suffice.
+               for (vnum = surf ? surf->num_firstvertex : 0; vnum < mesh->num_vertices; vnum++)
+               {
+                       // this uses prime numbers intentionally for computing the hash
+                       hashindex = (unsigned int)(mesh->data_vertex3f[vnum * 3 + 0] * 2003 + mesh->data_vertex3f[vnum * 3 + 1] * 4001 + mesh->data_vertex3f[vnum * 3 + 2] * 7919 + mesh->data_normal3f[vnum * 3 + 0] * 4097 + mesh->data_normal3f[vnum * 3 + 1] * 257 + mesh->data_normal3f[vnum * 3 + 2] * 17) & mask;
+                       for (h = hashindex; mesh->data_vertexhash[h] >= 0; h = (h + 1) & mask)
+                               ; // just iterate until we find the terminator
+                       mesh->data_vertexhash[h] = vnum;
+               }
+       }
+       mask = mod->surfmesh.num_vertexhashsize - 1;
+       // this uses prime numbers intentionally for computing the hash
+       hashindex = (unsigned int)(x * 2003 + y * 4001 + z * 7919 + nx * 4097 + ny * 257 + nz * 17) & mask;
+       // when possible find an identical vertex within the same surface and return it
+       for(h = hashindex;(vnum = mesh->data_vertexhash[h]) >= 0;h = (h + 1) & mask)
+       {
+               if (vnum >= surf->num_firstvertex
+                && mesh->data_vertex3f[vnum * 3 + 0] == x && mesh->data_vertex3f[vnum * 3 + 1] == y && mesh->data_vertex3f[vnum * 3 + 2] == z
+                && mesh->data_normal3f[vnum * 3 + 0] == nx && mesh->data_normal3f[vnum * 3 + 1] == ny && mesh->data_normal3f[vnum * 3 + 2] == nz
+                && mesh->data_texcoordtexture2f[vnum * 2 + 0] == s && mesh->data_texcoordtexture2f[vnum * 2 + 1] == t
+                && mesh->data_texcoordlightmap2f[vnum * 2 + 0] == u && mesh->data_texcoordlightmap2f[vnum * 2 + 1] == v
+                && mesh->data_lightmapcolor4f[vnum * 4 + 0] == r && mesh->data_lightmapcolor4f[vnum * 4 + 1] == g && mesh->data_lightmapcolor4f[vnum * 4 + 2] == b && mesh->data_lightmapcolor4f[vnum * 4 + 3] == a)
+                       return vnum;
        }
-}
-
-int Mod_Mesh_AddVertex(model_t *mod, msurface_t *surf, float x, float y, float z, float nx, float ny, float nz, float s, float t, float u, float v, float r, float g, float b, float a)
-{
-       int vnum;
-       surfmesh_t *mesh = &mod->surfmesh;
-
        // add the new vertex
        vnum = mesh->num_vertices++;
        if (surf->num_vertices > 0)
@@ -4604,6 +4612,7 @@ int Mod_Mesh_AddVertex(model_t *mod, msurface_t *surf, float x, float y, float z
                VectorSet(surf->maxs, x, y, z);
        }
        surf->num_vertices = mesh->num_vertices - surf->num_firstvertex;
+       mesh->data_vertexhash[h] = vnum;
        mesh->data_vertex3f[vnum * 3 + 0] = x;
        mesh->data_vertex3f[vnum * 3 + 1] = y;
        mesh->data_vertex3f[vnum * 3 + 2] = z;
@@ -4621,32 +4630,6 @@ int Mod_Mesh_AddVertex(model_t *mod, msurface_t *surf, float x, float y, float z
        return vnum;
 }
 
-int Mod_Mesh_IndexForVertex(model_t *mod, msurface_t *surf, float x, float y, float z, float nx, float ny, float nz, float s, float t, float u, float v, float r, float g, float b, float a)
-{
-       int hashindex, h, vnum, mask;
-       surfmesh_t *mesh = &mod->surfmesh;
-
-       Mod_Mesh_CheckResize_Vertex(mod, surf);
-
-       mask = mod->surfmesh.num_vertexhashsize - 1;
-       // this uses prime numbers intentionally for computing the hash
-       hashindex = (unsigned int)(x * 2003 + y * 4001 + z * 7919 + nx * 4097 + ny * 257 + nz * 17) & mask;
-       // when possible find an identical vertex within the same surface and return it
-       for(h = hashindex;(vnum = mesh->data_vertexhash[h]) >= 0;h = (h + 1) & mask)
-       {
-               if (vnum >= surf->num_firstvertex
-                && mesh->data_vertex3f[vnum * 3 + 0] == x && mesh->data_vertex3f[vnum * 3 + 1] == y && mesh->data_vertex3f[vnum * 3 + 2] == z
-                && mesh->data_normal3f[vnum * 3 + 0] == nx && mesh->data_normal3f[vnum * 3 + 1] == ny && mesh->data_normal3f[vnum * 3 + 2] == nz
-                && mesh->data_texcoordtexture2f[vnum * 2 + 0] == s && mesh->data_texcoordtexture2f[vnum * 2 + 1] == t
-                && mesh->data_texcoordlightmap2f[vnum * 2 + 0] == u && mesh->data_texcoordlightmap2f[vnum * 2 + 1] == v
-                && mesh->data_lightmapcolor4f[vnum * 4 + 0] == r && mesh->data_lightmapcolor4f[vnum * 4 + 1] == g && mesh->data_lightmapcolor4f[vnum * 4 + 2] == b && mesh->data_lightmapcolor4f[vnum * 4 + 3] == a)
-                       return vnum;
-       }
-       vnum = Mod_Mesh_AddVertex(mod, surf, x, y, z, nx, ny, nz, s, t, u, v, r, g, b, a);
-       mesh->data_vertexhash[h] = vnum;
-       return vnum;
-}
-
 void Mod_Mesh_AddTriangle(model_t *mod, msurface_t *surf, int e0, int e1, int e2)
 {
        surfmesh_t *mesh = &mod->surfmesh;
@@ -4670,25 +4653,24 @@ static void Mod_Mesh_MakeSortedSurfaces(model_t *mod)
 {
        int i, j;
        texture_t *tex;
-       unsigned char* included = (unsigned char *)R_FrameData_Alloc(mod->num_surfaces * sizeof(unsigned char));
 
        // build the sorted surfaces list properly to reduce material setup
        // this is easy because we're just sorting on texture and don't care about the order of textures
        mod->submodelsurfaces_start = 0;
        mod->submodelsurfaces_end = 0;
        for (i = 0; i < mod->num_surfaces; i++)
-               included[i] = 0;
+               mod->data_surfaces[i].included = false;
        for (i = 0; i < mod->num_surfaces; i++)
        {
-               if (included[i])
+               if (mod->data_surfaces[i].included)
                        continue;
                tex = mod->data_surfaces[i].texture;
                // j = i is intentional
                for (j = i; j < mod->num_surfaces; j++)
                {
-                       if (!included[j] && mod->data_surfaces[j].texture == tex)
+                       if (!mod->data_surfaces[j].included && mod->data_surfaces[j].texture == tex)
                        {
-                               included[j] = 1;
+                               mod->data_surfaces[j].included = 1;
                                mod->modelsurfaces_sorted[mod->submodelsurfaces_end++] = j;
                        }
                }