]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - model_shared.c
removed detail texturing (it only worked in q1bsp and hlbsp maps, did not work proper...
[xonotic/darkplaces.git] / model_shared.c
index 366a03f4088e04b3311e5a49c6dc2054065a09f7..40967880f6238a691c0d54c1cc108bc19ed61809 100644 (file)
@@ -30,144 +30,10 @@ cvar_t r_mipskins = {CVAR_SAVE, "r_mipskins", "0"};
 
 model_t *loadmodel;
 
-// LordHavoc: increased from 512 to 2048
-#define        MAX_MOD_KNOWN   2048
+// LordHavoc: was 512
+#define MAX_MOD_KNOWN (MAX_MODELS + 256)
 static model_t mod_known[MAX_MOD_KNOWN];
 
-rtexturepool_t *mod_shared_texturepool;
-rtexture_t *r_notexture;
-rtexture_t *mod_shared_detailtextures[NUM_DETAILTEXTURES];
-rtexture_t *mod_shared_distorttexture[64];
-
-void Mod_BuildDetailTextures (void)
-{
-       int i, x, y, light;
-       float vc[3], vx[3], vy[3], vn[3], lightdir[3];
-#define DETAILRESOLUTION 256
-       qbyte data[DETAILRESOLUTION][DETAILRESOLUTION][4], noise[DETAILRESOLUTION][DETAILRESOLUTION];
-       lightdir[0] = 0.5;
-       lightdir[1] = 1;
-       lightdir[2] = -0.25;
-       VectorNormalize(lightdir);
-       for (i = 0;i < NUM_DETAILTEXTURES;i++)
-       {
-               fractalnoise(&noise[0][0], DETAILRESOLUTION, DETAILRESOLUTION >> 4);
-               for (y = 0;y < DETAILRESOLUTION;y++)
-               {
-                       for (x = 0;x < DETAILRESOLUTION;x++)
-                       {
-                               vc[0] = x;
-                               vc[1] = y;
-                               vc[2] = noise[y][x] * (1.0f / 32.0f);
-                               vx[0] = x + 1;
-                               vx[1] = y;
-                               vx[2] = noise[y][(x + 1) % DETAILRESOLUTION] * (1.0f / 32.0f);
-                               vy[0] = x;
-                               vy[1] = y + 1;
-                               vy[2] = noise[(y + 1) % DETAILRESOLUTION][x] * (1.0f / 32.0f);
-                               VectorSubtract(vx, vc, vx);
-                               VectorSubtract(vy, vc, vy);
-                               CrossProduct(vx, vy, vn);
-                               VectorNormalize(vn);
-                               light = 128 - DotProduct(vn, lightdir) * 128;
-                               light = bound(0, light, 255);
-                               data[y][x][0] = data[y][x][1] = data[y][x][2] = light;
-                               data[y][x][3] = 255;
-                       }
-               }
-               mod_shared_detailtextures[i] = R_LoadTexture2D(mod_shared_texturepool, va("detailtexture%i", i), DETAILRESOLUTION, DETAILRESOLUTION, &data[0][0][0], TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_PRECACHE, NULL);
-       }
-}
-
-qbyte Mod_MorphDistortTexture (double y0, double y1, double y2, double y3, double morph)
-{
-       int     value = (int)(((y1 + y3 - (y0 + y2)) * morph * morph * morph) +
-                               ((2 * (y0 - y1) + y2 - y3) * morph * morph) +
-                               ((y2 - y0) * morph) +
-                               (y1));
-
-       if (value > 255)
-               value = 255;
-       if (value < 0)
-               value = 0;
-
-       return (qbyte)value;
-}
-
-void Mod_BuildDistortTexture (void)
-{
-       int x, y, i, j;
-#define DISTORTRESOLUTION 32
-       qbyte data[5][DISTORTRESOLUTION][DISTORTRESOLUTION][2];
-
-       for (i=0; i<4; i++)
-       {
-               for (y=0; y<DISTORTRESOLUTION; y++)
-               {
-                       for (x=0; x<DISTORTRESOLUTION; x++)
-                       {
-                               data[i][y][x][0] = rand () & 255;
-                               data[i][y][x][1] = rand () & 255;
-                       }
-               }
-       }
-
-
-       for (i=0; i<4; i++)
-       {
-               for (j=0; j<16; j++)
-               {
-                       mod_shared_distorttexture[i*16+j] = NULL;
-                       if (gl_textureshader)
-                       {
-                               for (y=0; y<DISTORTRESOLUTION; y++)
-                               {
-                                       for (x=0; x<DISTORTRESOLUTION; x++)
-                                       {
-                                               data[4][y][x][0] = Mod_MorphDistortTexture (data[(i-1)&3][y][x][0], data[i][y][x][0], data[(i+1)&3][y][x][0], data[(i+2)&3][y][x][0], 0.0625*j);
-                                               data[4][y][x][1] = Mod_MorphDistortTexture (data[(i-1)&3][y][x][1], data[i][y][x][1], data[(i+1)&3][y][x][1], data[(i+2)&3][y][x][1], 0.0625*j);
-                                       }
-                               }
-                               mod_shared_distorttexture[i*16+j] = R_LoadTexture2D(mod_shared_texturepool, va("distorttexture%i", i*16+j), DISTORTRESOLUTION, DISTORTRESOLUTION, &data[4][0][0][0], TEXTYPE_DSDT, TEXF_PRECACHE, NULL);
-                       }
-               }
-       }
-
-       return;
-}
-
-texture_t r_surf_notexture;
-
-void Mod_SetupNoTexture(void)
-{
-       int x, y;
-       qbyte pix[16][16][4];
-
-       // this makes a light grey/dark grey checkerboard texture
-       for (y = 0;y < 16;y++)
-       {
-               for (x = 0;x < 16;x++)
-               {
-                       if ((y < 8) ^ (x < 8))
-                       {
-                               pix[y][x][0] = 128;
-                               pix[y][x][1] = 128;
-                               pix[y][x][2] = 128;
-                               pix[y][x][3] = 255;
-                       }
-                       else
-                       {
-                               pix[y][x][0] = 64;
-                               pix[y][x][1] = 64;
-                               pix[y][x][2] = 64;
-                               pix[y][x][3] = 255;
-                       }
-               }
-       }
-
-       r_notexture = R_LoadTexture2D(mod_shared_texturepool, "notexture", 16, 16, &pix[0][0][0], TEXTYPE_RGBA, TEXF_MIPMAP, NULL);
-}
-
 static void mod_start(void)
 {
        int i;
@@ -175,11 +41,6 @@ static void mod_start(void)
                if (mod_known[i].name[0])
                        Mod_UnloadModel(&mod_known[i]);
        Mod_LoadModels();
-
-       mod_shared_texturepool = R_AllocTexturePool();
-       Mod_SetupNoTexture();
-       Mod_BuildDetailTextures();
-       Mod_BuildDistortTexture();
 }
 
 static void mod_shutdown(void)
@@ -188,36 +49,28 @@ static void mod_shutdown(void)
        for (i = 0;i < MAX_MOD_KNOWN;i++)
                if (mod_known[i].name[0])
                        Mod_UnloadModel(&mod_known[i]);
-
-       R_FreeTexturePool(&mod_shared_texturepool);
 }
 
 static void mod_newmap(void)
 {
-       msurface_t *surf;
-       int i, surfnum, ssize, tsize;
+       msurface_t *surface;
+       int i, surfacenum, ssize, tsize;
 
-       if (!cl_stainmapsclearonload.integer)
+       if (!cl_stainmaps_clearonload.integer)
                return;
 
-       for (i=0; i<MAX_MOD_KNOWN; i++)
+       for (i = 0;i < MAX_MOD_KNOWN;i++)
        {
-               if (mod_known[i].name[0] && mod_known[i].type == mod_brushq1)
+               if (mod_known[i].name[0])
                {
-                       for (surfnum=0, surf=mod_known[i].brushq1.surfaces; surfnum<mod_known[i].brushq1.numsurfaces;surfnum++, surf++)
+                       for (surfacenum = 0, surface = mod_known[i].data_surfaces;surfacenum < mod_known[i].num_surfaces;surfacenum++, surface++)
                        {
-                               if (surf->texinfo->texture->flags & SURF_LIGHTMAP)
+                               if (surface->lightmapinfo && surface->lightmapinfo->stainsamples)
                                {
-                                       ssize = (surf->extents[0] >> 4) + 1;
-                                       tsize = (surf->extents[1] >> 4) + 1;
-                                       
-                                       if (ssize > 256 || tsize > 256)
-                                               Host_Error("Bad surface extents");
-
-                                       if (surf->stainsamples)
-                                               memset(surf->stainsamples, 255, ssize * tsize * 3);
-
-                                       surf->cached_dlight = true;
+                                       ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
+                                       tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
+                                       memset(surface->lightmapinfo->stainsamples, 255, ssize * tsize * 3);
+                                       surface->cached_dlight = true;
                                }
                        }
                }
@@ -260,12 +113,15 @@ void Mod_UnloadModel (model_t *mod)
 {
        char name[MAX_QPATH];
        qboolean isworldmodel;
+       qboolean used;
        strcpy(name, mod->name);
        isworldmodel = mod->isworldmodel;
+       used = mod->used;
        Mod_FreeModel(mod);
        strcpy(mod->name, name);
        mod->isworldmodel = isworldmodel;
-       mod->needload = true;
+       mod->used = used;
+       mod->loaded = false;
 }
 
 /*
@@ -275,7 +131,7 @@ Mod_LoadModel
 Loads a model
 ==================
 */
-static model_t *Mod_LoadModel(model_t *mod, qboolean crash, qboolean checkdisk, qboolean isworldmodel)
+model_t *Mod_LoadModel(model_t *mod, qboolean crash, qboolean checkdisk, qboolean isworldmodel)
 {
        int num;
        unsigned int crc;
@@ -289,31 +145,32 @@ static model_t *Mod_LoadModel(model_t *mod, qboolean crash, qboolean checkdisk,
        crc = 0;
        buf = NULL;
        if (mod->isworldmodel != isworldmodel)
-               mod->needload = true;
-       if (mod->needload || checkdisk)
+               mod->loaded = false;
+       if (!mod->loaded || checkdisk)
        {
-               if (checkdisk && !mod->needload)
+               if (checkdisk && mod->loaded)
                        Con_DPrintf("checking model %s\n", mod->name);
                buf = FS_LoadFile (mod->name, tempmempool, false);
                if (buf)
                {
                        crc = CRC_Block(buf, fs_filesize);
                        if (mod->crc != crc)
-                               mod->needload = true;
+                               mod->loaded = false;
                }
        }
-       if (!mod->needload)
+       if (mod->loaded)
                return mod; // already loaded
 
        Con_DPrintf("loading model %s\n", mod->name);
        // LordHavoc: unload the existing model in this slot (if there is one)
        Mod_UnloadModel(mod);
+
        // load the model
        mod->isworldmodel = isworldmodel;
        mod->used = true;
        mod->crc = crc;
-       // errors can prevent the corresponding mod->needload = false;
-       mod->needload = true;
+       // errors can prevent the corresponding mod->loaded = true;
+       mod->loaded = false;
 
        // default model radius and bounding box (mainly for missing models)
        mod->radius = 16;
@@ -332,18 +189,23 @@ static model_t *Mod_LoadModel(model_t *mod, qboolean crash, qboolean checkdisk,
 
        if (buf)
        {
+               char *bufend = (char *)buf + fs_filesize;
                num = LittleLong(*((int *)buf));
                // call the apropriate loader
                loadmodel = mod;
-                    if (!memcmp(buf, "IDPO", 4)) Mod_IDP0_Load(mod, buf);
-               else if (!memcmp(buf, "IDP2", 4)) Mod_IDP2_Load(mod, buf);
-               else if (!memcmp(buf, "IDP3", 4)) Mod_IDP3_Load(mod, buf);
-               else if (!memcmp(buf, "IDSP", 4)) Mod_IDSP_Load(mod, buf);
-               else if (!memcmp(buf, "IBSP", 4)) Mod_IBSP_Load(mod, buf);
-               else if (!memcmp(buf, "ZYMOTICMODEL", 12)) Mod_ZYMOTICMODEL_Load(mod, buf);
-               else if (strlen(mod->name) >= 4 && !strcmp(mod->name - 4, ".map")) Mod_MAP_Load(mod, buf);
-               else if (num == BSPVERSION || num == 30) Mod_Q1BSP_Load(mod, buf);
-               else Host_Error("Mod_LoadModel: model \"%s\" is of unknown/unsupported type\n", mod->name);
+                    if (!memcmp(buf, "IDPO", 4)) Mod_IDP0_Load(mod, buf, bufend);
+               else if (!memcmp(buf, "IDP2", 4)) Mod_IDP2_Load(mod, buf, bufend);
+               else if (!memcmp(buf, "IDP3", 4)) Mod_IDP3_Load(mod, buf, bufend);
+               else if (!memcmp(buf, "IDSP", 4)) Mod_IDSP_Load(mod, buf, bufend);
+               else if (!memcmp(buf, "IDS2", 4)) Mod_IDS2_Load(mod, buf, bufend);
+               else if (!memcmp(buf, "IBSP", 4)) Mod_IBSP_Load(mod, buf, bufend);
+               else if (!memcmp(buf, "ZYMOTICMODEL", 12)) Mod_ZYMOTICMODEL_Load(mod, buf, bufend);
+               else if (!memcmp(buf, "DARKPLACESMODEL", 16)) Mod_DARKPLACESMODEL_Load(mod, buf, bufend);
+               else if (!strcmp(buf, "ACTRHEAD")) Mod_PSKMODEL_Load(mod, buf, bufend);
+               else if (strlen(mod->name) >= 4 && !strcmp(mod->name - 4, ".map")) Mod_MAP_Load(mod, buf, bufend);
+               else if (!memcmp(buf, "MCBSP", 5)) Mod_Q1BSP_Load(mod, buf, bufend);
+               else if (num == BSPVERSION || num == 30) Mod_Q1BSP_Load(mod, buf, bufend);
+               else Con_Printf("Mod_LoadModel: model \"%s\" is of unknown/unsupported type\n", mod->name);
                Mem_Free(buf);
        }
        else if (crash)
@@ -353,26 +215,10 @@ static model_t *Mod_LoadModel(model_t *mod, qboolean crash, qboolean checkdisk,
        }
 
        // no errors occurred
-       mod->needload = false;
+       mod->loaded = true;
        return mod;
 }
 
-void Mod_CheckLoaded(model_t *mod)
-{
-       if (mod)
-       {
-               if (mod->needload)
-                       Mod_LoadModel(mod, true, true, mod->isworldmodel);
-               else
-               {
-                       //if (mod->type == mod_invalid)
-                       //      Host_Error("Mod_CheckLoaded: invalid model\n");
-                       mod->used = true;
-                       return;
-               }
-       }
-}
-
 /*
 ===================
 Mod_ClearAll
@@ -403,6 +249,15 @@ void Mod_PurgeUnused(void)
                                Mod_FreeModel(mod);
 }
 
+// only used during loading!
+void Mod_RemoveStaleWorldModels(model_t *skip)
+{
+       int i;
+       for (i = 0;i < MAX_MOD_KNOWN;i++)
+               if (mod_known[i].isworldmodel && skip != &mod_known[i])
+                       Mod_UnloadModel(mod_known + i);
+}
+
 void Mod_LoadModels(void)
 {
        int i;
@@ -448,7 +303,7 @@ model_t *Mod_FindName(const char *name)
        {
                mod = freemod;
                strcpy (mod->name, name);
-               mod->needload = true;
+               mod->loaded = false;
                mod->used = true;
                return mod;
        }
@@ -457,20 +312,6 @@ model_t *Mod_FindName(const char *name)
        return NULL;
 }
 
-/*
-==================
-Mod_TouchModel
-
-==================
-*/
-void Mod_TouchModel(const char *name)
-{
-       model_t *mod;
-
-       mod = Mod_FindName(name);
-       mod->used = true;
-}
-
 /*
 ==================
 Mod_ForName
@@ -533,7 +374,7 @@ int Mod_BuildVertexRemapTableFromElements(int numelements, const int *elements,
 
 #if 1
 // fast way, using an edge hash
-#define TRIANGLEEDGEHASH 16384
+#define TRIANGLEEDGEHASH 8192
 void Mod_BuildTriangleNeighbors(int *neighbors, const int *elements, int numtriangles)
 {
        int i, j, p, e1, e2, *n, hashindex, count, match;
@@ -640,51 +481,69 @@ void Mod_BuildTriangleNeighbors(int *neighbors, const int *elements, int numtria
 }
 #endif
 
-void Mod_ValidateElements(const int *elements, int numtriangles, int numverts, const char *filename, int fileline)
+void Mod_ValidateElements(int *elements, int numtriangles, int numverts, const char *filename, int fileline)
 {
-       int i;
+       int i, warned = false;
        for (i = 0;i < numtriangles * 3;i++)
+       {
                if ((unsigned int)elements[i] >= (unsigned int)numverts)
-                       Con_Printf("Mod_ValidateElements: out of bounds element detected at %s:%d\n", filename, fileline);
+               {
+                       if (!warned)
+                       {
+                               warned = true;
+                               Con_Printf("Mod_ValidateElements: out of bounds elements detected at %s:%d\n", filename, fileline);
+                       }
+                       elements[i] = 0;
+               }
+       }
 }
 
 // warning: this is an expensive function!
-void Mod_BuildNormals(int numverts, int numtriangles, const float *vertex3f, const int *elements, float *normal3f)
+void Mod_BuildNormals(int firstvertex, int numvertices, int numtriangles, const float *vertex3f, const int *elements, float *normal3f, qboolean areaweighting)
 {
-       int i, tnum;
-       float normal[3], *v;
-       const int *e;
+       int i, j;
+       const int *element;
+       float *vectorNormal;
+       float areaNormal[3];
        // clear the vectors
-       memset(normal3f, 0, numverts * sizeof(float[3]));
+       memset(normal3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
        // process each vertex of each triangle and accumulate the results
-       for (tnum = 0, e = elements;tnum < numtriangles;tnum++, e += 3)
+       // use area-averaging, to make triangles with a big area have a bigger
+       // weighting on the vertex normal than triangles with a small area
+       // to do so, just add the 'normals' together (the bigger the area
+       // the greater the length of the normal is
+       element = elements;
+       for (i = 0; i < numtriangles; i++, element += 3)
        {
-               TriangleNormal(vertex3f + e[0] * 3, vertex3f + e[1] * 3, vertex3f + e[2] * 3, normal);
-               VectorNormalize(normal);
-               v = normal3f + e[0] * 3;
-               v[0] += normal[0];
-               v[1] += normal[1];
-               v[2] += normal[2];
-               v = normal3f + e[1] * 3;
-               v[0] += normal[0];
-               v[1] += normal[1];
-               v[2] += normal[2];
-               v = normal3f + e[2] * 3;
-               v[0] += normal[0];
-               v[1] += normal[1];
-               v[2] += normal[2];
+               TriangleNormal(
+                       vertex3f + element[0] * 3,
+                       vertex3f + element[1] * 3,
+                       vertex3f + element[2] * 3,
+                       areaNormal
+                       );
+
+               if (!areaweighting)
+                       VectorNormalize(areaNormal);
+
+               for (j = 0;j < 3;j++)
+               {
+                       vectorNormal = normal3f + element[j] * 3;
+                       vectorNormal[0] += areaNormal[0];
+                       vectorNormal[1] += areaNormal[1];
+                       vectorNormal[2] += areaNormal[2];
+               }
        }
-       // now we could divide the vectors by the number of averaged values on
-       // each vertex...  but instead normalize them
-       for (i = 0, v = normal3f;i < numverts;i++, v += 3)
-               VectorNormalize(v);
+       // and just normalize the accumulated vertex normal in the end
+       vectorNormal = normal3f + 3 * firstvertex;
+       for (i = 0; i < numvertices; i++, vectorNormal += 3)
+               VectorNormalize(vectorNormal);
 }
 
 void Mod_BuildBumpVectors(const float *v0, const float *v1, const float *v2, const float *tc0, const float *tc1, const float *tc2, float *svector3f, float *tvector3f, float *normal3f)
 {
        float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
-       // 103 add/sub/negate/multiply (1 cycle), 3 divide (20 cycle), 3 sqrt (22 cycle), 4 compare (3 cycle?), total cycles not counting load/store/exchange roughly 241 cycles
-       // 12 add, 28 subtract, 57 multiply, 3 divide, 3 sqrt, 4 compare, 50% chance of 6 negates
+       // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
+       // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
 
        // 6 multiply, 9 subtract
        VectorSubtract(v1, v0, v10);
@@ -692,8 +551,6 @@ void Mod_BuildBumpVectors(const float *v0, const float *v1, const float *v2, con
        normal3f[0] = v10[1] * v20[2] - v10[2] * v20[1];
        normal3f[1] = v10[2] * v20[0] - v10[0] * v20[2];
        normal3f[2] = v10[0] * v20[1] - v10[1] * v20[0];
-       // 1 sqrt, 1 divide, 6 multiply, 2 add, 1 compare
-       VectorNormalize(normal3f);
        // 12 multiply, 10 subtract
        tc10[1] = tc1[1] - tc0[1];
        tc20[1] = tc2[1] - tc0[1];
@@ -714,9 +571,6 @@ void Mod_BuildBumpVectors(const float *v0, const float *v1, const float *v2, con
        tvector3f[0] -= f * normal3f[0];
        tvector3f[1] -= f * normal3f[1];
        tvector3f[2] -= f * normal3f[2];
-       // 2 sqrt, 2 divide, 12 multiply, 4 add, 2 compare
-       VectorNormalize(svector3f);
-       VectorNormalize(tvector3f);
        // if texture is mapped the wrong way (counterclockwise), the tangents
        // have to be flipped, this is detected by calculating a normal from the
        // two tangents, and seeing if it is opposite the surface normal
@@ -730,66 +584,85 @@ void Mod_BuildBumpVectors(const float *v0, const float *v1, const float *v2, con
 }
 
 // warning: this is a very expensive function!
-void Mod_BuildTextureVectorsAndNormals(int numverts, int numtriangles, const float *vertex3f, const float *texcoord2f, const int *elements, float *svector3f, float *tvector3f, float *normal3f)
+void Mod_BuildTextureVectorsAndNormals(int firstvertex, int numvertices, int numtriangles, const float *vertex3f, const float *texcoord2f, const int *elements, float *svector3f, float *tvector3f, float *normal3f, qboolean areaweighting)
 {
        int i, tnum;
        float sdir[3], tdir[3], normal[3], *v;
        const int *e;
        // clear the vectors
        if (svector3f)
-               memset(svector3f, 0, numverts * sizeof(float[3]));
+               memset(svector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
        if (tvector3f)
-               memset(tvector3f, 0, numverts * sizeof(float[3]));
+               memset(tvector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
        if (normal3f)
-               memset(normal3f, 0, numverts * sizeof(float[3]));
+               memset(normal3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
        // process each vertex of each triangle and accumulate the results
        for (tnum = 0, e = elements;tnum < numtriangles;tnum++, e += 3)
        {
                Mod_BuildBumpVectors(vertex3f + e[0] * 3, vertex3f + e[1] * 3, vertex3f + e[2] * 3, texcoord2f + e[0] * 2, texcoord2f + e[1] * 2, texcoord2f + e[2] * 2, sdir, tdir, normal);
-               if (svector3f)
+               if (!areaweighting)
                {
-                       for (i = 0;i < 3;i++)
-                       {
-                               svector3f[e[i]*3  ] += sdir[0];
-                               svector3f[e[i]*3+1] += sdir[1];
-                               svector3f[e[i]*3+2] += sdir[2];
-                       }
+                       VectorNormalize(sdir);
+                       VectorNormalize(tdir);
+                       VectorNormalize(normal);
                }
+               if (svector3f)
+                       for (i = 0;i < 3;i++)
+                               VectorAdd(svector3f + e[i]*3, sdir, svector3f + e[i]*3);
                if (tvector3f)
-               {
                        for (i = 0;i < 3;i++)
-                       {
-                               tvector3f[e[i]*3  ] += tdir[0];
-                               tvector3f[e[i]*3+1] += tdir[1];
-                               tvector3f[e[i]*3+2] += tdir[2];
-                       }
-               }
+                               VectorAdd(tvector3f + e[i]*3, tdir, tvector3f + e[i]*3);
                if (normal3f)
-               {
                        for (i = 0;i < 3;i++)
-                       {
-                               normal3f[e[i]*3  ] += normal[0];
-                               normal3f[e[i]*3+1] += normal[1];
-                               normal3f[e[i]*3+2] += normal[2];
-                       }
-               }
+                               VectorAdd(normal3f + e[i]*3, normal, normal3f + e[i]*3);
        }
        // now we could divide the vectors by the number of averaged values on
        // each vertex...  but instead normalize them
        // 4 assignments, 1 divide, 1 sqrt, 2 adds, 6 multiplies
        if (svector3f)
-               for (i = 0, v = svector3f;i < numverts;i++, v += 3)
+               for (i = 0, v = svector3f + 3 * firstvertex;i < numvertices;i++, v += 3)
                        VectorNormalize(v);
        // 4 assignments, 1 divide, 1 sqrt, 2 adds, 6 multiplies
        if (tvector3f)
-               for (i = 0, v = tvector3f;i < numverts;i++, v += 3)
+               for (i = 0, v = tvector3f + 3 * firstvertex;i < numvertices;i++, v += 3)
                        VectorNormalize(v);
        // 4 assignments, 1 divide, 1 sqrt, 2 adds, 6 multiplies
        if (normal3f)
-               for (i = 0, v = normal3f;i < numverts;i++, v += 3)
+               for (i = 0, v = normal3f + 3 * firstvertex;i < numvertices;i++, v += 3)
                        VectorNormalize(v);
 }
 
+surfmesh_t *Mod_AllocSurfMesh(mempool_t *mempool, int numvertices, int numtriangles, qboolean lightmapoffsets, qboolean vertexcolors, qboolean neighbors)
+{
+       surfmesh_t *mesh;
+       qbyte *data;
+       mesh = Mem_Alloc(mempool, sizeof(surfmesh_t) + numvertices * (3 + 3 + 3 + 3 + 2 + 2 + (vertexcolors ? 4 : 0)) * sizeof(float) + numvertices * (lightmapoffsets ? 1 : 0) * sizeof(int) + numtriangles * (3 + 3 + (neighbors ? 3 : 0)) * sizeof(int));
+       mesh->num_vertices = numvertices;
+       mesh->num_triangles = numtriangles;
+       data = (qbyte *)(mesh + 1);
+       if (mesh->num_vertices)
+       {
+               mesh->data_vertex3f = (float *)data, data += sizeof(float[3]) * mesh->num_vertices;
+               mesh->data_svector3f = (float *)data, data += sizeof(float[3]) * mesh->num_vertices;
+               mesh->data_tvector3f = (float *)data, data += sizeof(float[3]) * mesh->num_vertices;
+               mesh->data_normal3f = (float *)data, data += sizeof(float[3]) * mesh->num_vertices;
+               mesh->data_texcoordtexture2f = (float *)data, data += sizeof(float[2]) * mesh->num_vertices;
+               mesh->data_texcoordlightmap2f = (float *)data, data += sizeof(float[2]) * mesh->num_vertices;
+               if (vertexcolors)
+                       mesh->data_lightmapcolor4f = (float *)data, data += sizeof(float[4]) * mesh->num_vertices;
+               if (lightmapoffsets)
+                       mesh->data_lightmapoffsets = (int *)data, data += sizeof(int) * mesh->num_vertices;
+       }
+       if (mesh->num_triangles)
+       {
+               mesh->data_element3i = (int *)data, data += sizeof(int[3]) * mesh->num_triangles;
+               mesh->data_element3i = (int *)data, data += sizeof(int[3]) * mesh->num_triangles;
+               if (neighbors)
+                       mesh->data_neighbor3i = (int *)data, data += sizeof(int[3]) * mesh->num_triangles;
+       }
+       return mesh;
+}
+
 shadowmesh_t *Mod_ShadowMesh_Alloc(mempool_t *mempool, int maxverts, int maxtriangles, rtexture_t *map_diffuse, rtexture_t *map_specular, rtexture_t *map_normal, int light, int neighbors, int expandable)
 {
        shadowmesh_t *newmesh;
@@ -1039,15 +912,12 @@ static rtexture_t *GL_TextureForSkinLayer(const qbyte *in, int width, int height
        return NULL;
 }
 
-static int detailtexturecycle = 0;
-int Mod_LoadSkinFrame(skinframe_t *skinframe, char *basename, int textureflags, int loadpantsandshirt, int usedetailtexture, int loadglowtexture)
+int Mod_LoadSkinFrame(skinframe_t *skinframe, char *basename, int textureflags, int loadpantsandshirt, int loadglowtexture)
 {
        imageskin_t s;
        memset(skinframe, 0, sizeof(*skinframe));
        if (!image_loadskin(&s, basename))
                return false;
-       if (usedetailtexture)
-               skinframe->detail = mod_shared_detailtextures[(detailtexturecycle++) % NUM_DETAILTEXTURES];
        skinframe->base = R_LoadTexture2D (loadmodel->texturepool, basename, s.basepixels_width, s.basepixels_height, s.basepixels, TEXTYPE_RGBA, textureflags, NULL);
        if (s.nmappixels != NULL)
                skinframe->nmap = R_LoadTexture2D (loadmodel->texturepool, va("%s_nmap", basename), s.nmappixels_width, s.nmappixels_height, s.nmappixels, TEXTYPE_RGBA, textureflags, NULL);
@@ -1064,18 +934,20 @@ int Mod_LoadSkinFrame(skinframe_t *skinframe, char *basename, int textureflags,
                if (s.shirtpixels != NULL)
                        skinframe->shirt = R_LoadTexture2D (loadmodel->texturepool, va("%s_shirt", basename), s.shirtpixels_width, s.shirtpixels_height, s.shirtpixels, TEXTYPE_RGBA, textureflags, NULL);
        }
+       if (!skinframe->base)
+               skinframe->base = r_texture_notexture;
+       if (!skinframe->nmap)
+               skinframe->nmap = r_texture_blanknormalmap;
        image_freeskin(&s);
        return true;
 }
 
-int Mod_LoadSkinFrame_Internal(skinframe_t *skinframe, char *basename, int textureflags, int loadpantsandshirt, int usedetailtexture, int loadglowtexture, qbyte *skindata, int width, int height)
+int Mod_LoadSkinFrame_Internal(skinframe_t *skinframe, char *basename, int textureflags, int loadpantsandshirt, int loadglowtexture, qbyte *skindata, int width, int height)
 {
        qbyte *temp1, *temp2;
        memset(skinframe, 0, sizeof(*skinframe));
        if (!skindata)
                return false;
-       if (usedetailtexture)
-               skinframe->detail = mod_shared_detailtextures[(detailtexturecycle++) % NUM_DETAILTEXTURES];
        if (r_shadow_bumpscale_basetexture.value > 0)
        {
                temp1 = Mem_Alloc(loadmodel->mempool, width * height * 8);
@@ -1108,6 +980,8 @@ int Mod_LoadSkinFrame_Internal(skinframe_t *skinframe, char *basename, int textu
                                skinframe->base = GL_TextureForSkinLayer(skindata, width, height, va("%s_nospecial", basename), palette_nocolormap, textureflags); // no pants or shirt
                }
        }
+       if (!skinframe->nmap)
+               skinframe->nmap = r_texture_blanknormalmap;
        return true;
 }
 
@@ -1322,6 +1196,14 @@ int Mod_CountSkinFiles(skinfile_t *skinfile)
        return i;
 }
 
+void Mod_SnapVertices(int numcomponents, int numvertices, float *vertices, float snap)
+{
+       int i;
+       double isnap = 1.0 / snap;
+       for (i = 0;i < numvertices*numcomponents;i++)
+               vertices[i] = floor(vertices[i]*isnap)*snap;
+}
+
 int Mod_RemoveDegenerateTriangles(int numtriangles, const int *inelement3i, int *outelement3i, const float *vertex3f)
 {
        int i, outtriangles;