]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - model_brush.c
changed a lot of Con_DPrint/Con_DPrintf calls to Con_Print/Con_Printf (non-technical...
[xonotic/darkplaces.git] / model_brush.c
index 23d2a48d6499104157096fca280dc217fa5dee46..67de293585532329fedea68b5eb03e4a5d92b3fd 100644 (file)
@@ -23,6 +23,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "r_shadow.h"
 #include "polygon.h"
 #include "curves.h"
+#include "wad.h"
+
 
 // note: model_shared.c sets up r_notexture, and r_surf_notexture
 
@@ -35,12 +37,12 @@ cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"};
 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
 cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"};
 cvar_t r_subdivisions_tolerance = {0, "r_subdivisions_tolerance", "4"};
-cvar_t r_subdivisions_minlevel = {0, "r_subdivisions_minlevel", "0"};
-cvar_t r_subdivisions_maxlevel = {0, "r_subdivisions_maxlevel", "10"};
+cvar_t r_subdivisions_mintess = {0, "r_subdivisions_mintess", "1"};
+cvar_t r_subdivisions_maxtess = {0, "r_subdivisions_maxtess", "1024"};
 cvar_t r_subdivisions_maxvertices = {0, "r_subdivisions_maxvertices", "65536"};
 cvar_t r_subdivisions_collision_tolerance = {0, "r_subdivisions_collision_tolerance", "15"};
-cvar_t r_subdivisions_collision_minlevel = {0, "r_subdivisions_collision_minlevel", "0"};
-cvar_t r_subdivisions_collision_maxlevel = {0, "r_subdivisions_collision_maxlevel", "10"};
+cvar_t r_subdivisions_collision_mintess = {0, "r_subdivisions_collision_mintess", "1"};
+cvar_t r_subdivisions_collision_maxtess = {0, "r_subdivisions_collision_maxtess", "1024"};
 cvar_t r_subdivisions_collision_maxvertices = {0, "r_subdivisions_collision_maxvertices", "4225"};
 cvar_t mod_q3bsp_curves_collisions = {0, "mod_q3bsp_curves_collisions", "1"};
 cvar_t mod_q3bsp_optimizedtraceline = {0, "mod_q3bsp_optimizedtraceline", "1"};
@@ -56,12 +58,12 @@ void Mod_BrushInit(void)
        Cvar_RegisterVariable(&r_lightmaprgba);
        Cvar_RegisterVariable(&r_nosurftextures);
        Cvar_RegisterVariable(&r_subdivisions_tolerance);
-       Cvar_RegisterVariable(&r_subdivisions_minlevel);
-       Cvar_RegisterVariable(&r_subdivisions_maxlevel);
+       Cvar_RegisterVariable(&r_subdivisions_mintess);
+       Cvar_RegisterVariable(&r_subdivisions_maxtess);
        Cvar_RegisterVariable(&r_subdivisions_maxvertices);
        Cvar_RegisterVariable(&r_subdivisions_collision_tolerance);
-       Cvar_RegisterVariable(&r_subdivisions_collision_minlevel);
-       Cvar_RegisterVariable(&r_subdivisions_collision_maxlevel);
+       Cvar_RegisterVariable(&r_subdivisions_collision_mintess);
+       Cvar_RegisterVariable(&r_subdivisions_collision_maxtess);
        Cvar_RegisterVariable(&r_subdivisions_collision_maxvertices);
        Cvar_RegisterVariable(&mod_q3bsp_curves_collisions);
        Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline);
@@ -878,7 +880,7 @@ static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *
        {
                if (in == inend)
                {
-                       Con_DPrintf("Mod_Q1BSP_DecompressVis: input underrun on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart);
+                       Con_Printf("Mod_Q1BSP_DecompressVis: input underrun on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart);
                        return;
                }
                c = *in++;
@@ -888,14 +890,14 @@ static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *
                {
                        if (in == inend)
                        {
-                               Con_DPrintf("Mod_Q1BSP_DecompressVis: input underrun (during zero-run) on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart);
+                               Con_Printf("Mod_Q1BSP_DecompressVis: input underrun (during zero-run) on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart);
                                return;
                        }
                        for (c = *in++;c > 0;c--)
                        {
                                if (out == outend)
                                {
-                                       Con_DPrintf("Mod_Q1BSP_DecompressVis: output overrun on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart);
+                                       Con_Printf("Mod_Q1BSP_DecompressVis: output overrun on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart);
                                        return;
                                }
                                *out++ = 0;
@@ -904,6 +906,77 @@ static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *
        }
 }
 
+/*
+=============
+R_Q1BSP_LoadSplitSky
+
+A sky texture is 256*128, with the right side being a masked overlay
+==============
+*/
+void R_Q1BSP_LoadSplitSky (qbyte *src, int width, int height, int bytesperpixel)
+{
+       int i, j;
+       unsigned solidpixels[128*128], alphapixels[128*128];
+
+       // if sky isn't the right size, just use it as a solid layer
+       if (width != 256 || height != 128)
+       {
+               loadmodel->brush.solidskytexture = R_LoadTexture2D(loadmodel->texturepool, "sky_solidtexture", width, height, src, bytesperpixel == 4 ? TEXTYPE_RGBA : TEXTYPE_PALETTE, TEXF_PRECACHE, bytesperpixel == 1 ? palette_complete : NULL);
+               loadmodel->brush.alphaskytexture = NULL;;
+               return;
+       }
+
+       if (bytesperpixel == 4)
+       {
+               for (i = 0;i < 128;i++)
+               {
+                       for (j = 0;j < 128;j++)
+                       {
+                               solidpixels[(i*128) + j] = ((unsigned *)src)[i*256+j+128];
+                               alphapixels[(i*128) + j] = ((unsigned *)src)[i*256+j];
+                       }
+               }
+       }
+       else
+       {
+               // make an average value for the back to avoid
+               // a fringe on the top level
+               int p, r, g, b;
+               union
+               {
+                       unsigned int i;
+                       unsigned char b[4];
+               }
+               rgba;
+               r = g = b = 0;
+               for (i = 0;i < 128;i++)
+               {
+                       for (j = 0;j < 128;j++)
+                       {
+                               rgba.i = palette_complete[src[i*256 + j + 128]];
+                               r += rgba.b[0];
+                               g += rgba.b[1];
+                               b += rgba.b[2];
+                       }
+               }
+               rgba.b[0] = r/(128*128);
+               rgba.b[1] = g/(128*128);
+               rgba.b[2] = b/(128*128);
+               rgba.b[3] = 0;
+               for (i = 0;i < 128;i++)
+               {
+                       for (j = 0;j < 128;j++)
+                       {
+                               solidpixels[(i*128) + j] = palette_complete[src[i*256 + j + 128]];
+                               alphapixels[(i*128) + j] = (p = src[i*256 + j]) ? palette_complete[p] : rgba.i;
+                       }
+               }
+       }
+
+       loadmodel->brush.solidskytexture = R_LoadTexture2D(loadmodel->texturepool, "sky_solidtexture", 128, 128, (qbyte *) solidpixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
+       loadmodel->brush.alphaskytexture = R_LoadTexture2D(loadmodel->texturepool, "sky_alphatexture", 128, 128, (qbyte *) alphapixels, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
+}
+
 static void Mod_Q1BSP_LoadTextures(lump_t *l)
 {
        int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
@@ -1005,21 +1078,11 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l)
                                data = loadimagepixels(tx->name, false, 0, 0);
                                if (data)
                                {
-                                       if (image_width == 256 && image_height == 128)
-                                       {
-                                               R_InitSky(data, 4);
-                                               Mem_Free(data);
-                                       }
-                                       else
-                                       {
-                                               Mem_Free(data);
-                                               Con_Printf("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
-                                               if (mtdata != NULL)
-                                                       R_InitSky(mtdata, 1);
-                                       }
+                                       R_Q1BSP_LoadSplitSky(data, image_width, image_height, 4);
+                                       Mem_Free(data);
                                }
                                else if (mtdata != NULL)
-                                       R_InitSky(mtdata, 1);
+                                       R_Q1BSP_LoadSplitSky(mtdata, mtwidth, mtheight, 1);
                        }
                }
                else
@@ -2208,6 +2271,102 @@ static void Mod_Q1BSP_LoadPlanes(lump_t *l)
        }
 }
 
+static void Mod_Q1BSP_LoadMapBrushes(void)
+{
+#if 0
+// unfinished
+       int submodel, numbrushes;
+       qboolean firstbrush;
+       char *text, *maptext;
+       char mapfilename[MAX_QPATH];
+       FS_StripExtension (loadmodel->name, mapfilename, sizeof (mapfilename));
+       strlcat (mapfilename, ".map", sizeof (mapfilename));
+       maptext = (qbyte*) FS_LoadFile(mapfilename, tempmempool, false);
+       if (!maptext)
+               return;
+       text = maptext;
+       if (!COM_ParseToken(&data, false))
+               return; // error
+       submodel = 0;
+       for (;;)
+       {
+               if (!COM_ParseToken(&data, false))
+                       break;
+               if (com_token[0] != '{')
+                       return; // error
+               // entity
+               firstbrush = true;
+               numbrushes = 0;
+               maxbrushes = 256;
+               brushes = Mem_Alloc(loadmodel->mempool, maxbrushes * sizeof(mbrush_t));
+               for (;;)
+               {
+                       if (!COM_ParseToken(&data, false))
+                               return; // error
+                       if (com_token[0] == '}')
+                               break; // end of entity
+                       if (com_token[0] == '{')
+                       {
+                               // brush
+                               if (firstbrush)
+                               {
+                                       if (submodel)
+                                       {
+                                               if (submodel > loadmodel->brush.numsubmodels)
+                                               {
+                                                       Con_Printf("Mod_Q1BSP_LoadMapBrushes: .map has more submodels than .bsp!\n");
+                                                       model = NULL;
+                                               }
+                                               else
+                                                       model = loadmodel->brush.submodels[submodel];
+                                       }
+                                       else
+                                               model = loadmodel;
+                               }
+                               for (;;)
+                               {
+                                       if (!COM_ParseToken(&data, false))
+                                               return; // error
+                                       if (com_token[0] == '}')
+                                               break; // end of brush
+                                       // each brush face should be this format:
+                                       // ( x y z ) ( x y z ) ( x y z ) texture scroll_s scroll_t rotateangle scale_s scale_t
+                                       // FIXME: support hl .map format
+                                       for (pointnum = 0;pointnum < 3;pointnum++)
+                                       {
+                                               COM_ParseToken(&data, false);
+                                               for (componentnum = 0;componentnum < 3;componentnum++)
+                                               {
+                                                       COM_ParseToken(&data, false);
+                                                       point[pointnum][componentnum] = atof(com_token);
+                                               }
+                                               COM_ParseToken(&data, false);
+                                       }
+                                       COM_ParseToken(&data, false);
+                                       strlcpy(facetexture, com_token, sizeof(facetexture));
+                                       COM_ParseToken(&data, false);
+                                       //scroll_s = atof(com_token);
+                                       COM_ParseToken(&data, false);
+                                       //scroll_t = atof(com_token);
+                                       COM_ParseToken(&data, false);
+                                       //rotate = atof(com_token);
+                                       COM_ParseToken(&data, false);
+                                       //scale_s = atof(com_token);
+                                       COM_ParseToken(&data, false);
+                                       //scale_t = atof(com_token);
+                                       TriangleNormal(point[0], point[1], point[2], planenormal);
+                                       VectorNormalizeDouble(planenormal);
+                                       planedist = DotProduct(point[0], planenormal);
+                                       //ChooseTexturePlane(planenormal, texturevector[0], texturevector[1]);
+                               }
+                               continue;
+                       }
+               }
+       }
+#endif
+}
+
+
 #define MAX_PORTALPOINTS 64
 
 typedef struct portal_s
@@ -2878,7 +3037,7 @@ extern void R_Q1BSP_DrawSky(entity_render_t *ent);
 extern void R_Q1BSP_Draw(entity_render_t *ent);
 extern void R_Q1BSP_GetLightInfo(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, vec3_t outmins, vec3_t outmaxs, int *outclusterlist, qbyte *outclusterpvs, int *outnumclusterspointer, int *outsurfacelist, qbyte *outsurfacepvs, int *outnumsurfacespointer);
 extern void R_Q1BSP_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, int numsurfaces, const int *surfacelist);
-extern void R_Q1BSP_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *lightcubemap, int numsurfaces, const int *surfacelist);
+extern void R_Q1BSP_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *lightcubemap, vec_t ambientscale, vec_t diffusescale, vec_t specularscale, int numsurfaces, const int *surfacelist);
 void Mod_Q1BSP_Load(model_t *mod, void *buffer)
 {
        int i, j, k;
@@ -2913,17 +3072,17 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer)
        mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
 
        if (loadmodel->isworldmodel)
-       {
                Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
-               // until we get a texture for it...
-               R_ResetQuakeSky();
-       }
 
 // swap all the lumps
        mod_base = (qbyte *)header;
 
-       for (i = 0;i < (int) sizeof(dheader_t) / 4;i++)
-               ((int *)header)[i] = LittleLong(((int *)header)[i]);
+       header->version = LittleLong(header->version);
+       for (i = 0;i < HEADER_LUMPS;i++)
+       {
+               header->lumps[i].fileofs = LittleLong(header->lumps[i].fileofs);
+               header->lumps[i].filelen = LittleLong(header->lumps[i].filelen);
+       }
 
 // load into heap
 
@@ -2963,7 +3122,6 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer)
        mainmempool = mod->mempool;
 
        Mod_Q1BSP_LoadLightList();
-       loadmodel = loadmodel;
 
        // make a single combined shadow mesh to allow optimized shadow volume creation
        numshadowmeshtriangles = 0;
@@ -2977,7 +3135,10 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer)
                Mod_ShadowMesh_AddMesh(loadmodel->mempool, loadmodel->brush.shadowmesh, NULL, NULL, NULL, surf->mesh.data_vertex3f, NULL, NULL, NULL, NULL, surf->mesh.num_triangles, surf->mesh.data_element3i);
        loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true);
        Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
-       
+
+       if (loadmodel->brush.numsubmodels)
+               loadmodel->brush.submodels = Mem_Alloc(loadmodel->mempool, loadmodel->brush.numsubmodels * sizeof(model_t *));
+
        // LordHavoc: to clear the fog around the original quake submodel code, I
        // will explain:
        // first of all, some background info on the submodels:
@@ -2997,12 +3158,14 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer)
                // LordHavoc: this code was originally at the end of this loop, but
                // has been transformed to something more readable at the start here.
 
-               // LordHavoc: only register submodels if it is the world
-               // (prevents external bsp models from replacing world submodels with
-               //  their own)
-               if (loadmodel->isworldmodel && i)
+               if (i > 0)
                {
                        char name[10];
+                       // LordHavoc: only register submodels if it is the world
+                       // (prevents external bsp models from replacing world submodels with
+                       //  their own)
+                       if (!loadmodel->isworldmodel)
+                               continue;
                        // duplicate the basic information
                        sprintf(name, "*%i", i);
                        mod = Mod_FindName(name);
@@ -3015,6 +3178,9 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer)
                        mod->mempool = NULL;
                }
 
+               if (loadmodel->brush.submodels)
+                       loadmodel->brush.submodels[i] = mod;
+
                bm = &mod->brushq1.submodels[i];
 
                mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
@@ -3103,6 +3269,8 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer)
                mod->brushq1.num_visleafs = bm->visleafs;
        }
 
+       Mod_Q1BSP_LoadMapBrushes();
+
        //Mod_Q1BSP_ProcessLightList();
 
        if (developer.integer)
@@ -3503,11 +3671,7 @@ void static Mod_Q2BSP_Load(model_t *mod, void *buffer)
                Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
        mod->brush.ishlbsp = false;
        if (loadmodel->isworldmodel)
-       {
                Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
-               // until we get a texture for it...
-               R_ResetQuakeSky();
-       }
 
        mod_base = (qbyte *)header;
 
@@ -3591,9 +3755,11 @@ static void Mod_Q3BSP_LoadTextures(lump_t *l)
        fssearch_t *search;
        char *f;
        const char *text;
-       int flags;
+       int flags, flags2, numparameters, passnumber;
        char shadername[Q3PATHLENGTH];
        char sky[Q3PATHLENGTH];
+       char firstpasstexturename[Q3PATHLENGTH];
+       char parameter[4][Q3PATHLENGTH];
 
        in = (void *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
@@ -3611,7 +3777,6 @@ static void Mod_Q3BSP_LoadTextures(lump_t *l)
                out->surfaceflags = LittleLong(in->surfaceflags);
                out->nativecontents = LittleLong(in->contents);
                out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, out->nativecontents);
-               Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, false, true, true);
                out->surfaceparms = -1;
        }
 
@@ -3627,7 +3792,10 @@ static void Mod_Q3BSP_LoadTextures(lump_t *l)
                                {
                                        strlcpy (shadername, com_token, sizeof (shadername));
                                        flags = 0;
+                                       flags2 = 0;
                                        sky[0] = 0;
+                                       passnumber = 0;
+                                       firstpasstexturename[0] = 0;
                                        if (COM_ParseToken(&text, false) && !strcasecmp(com_token, "{"))
                                        {
                                                while (COM_ParseToken(&text, false))
@@ -3640,103 +3808,159 @@ static void Mod_Q3BSP_LoadTextures(lump_t *l)
                                                                {
                                                                        if (!strcasecmp(com_token, "}"))
                                                                                break;
+                                                                       if (!strcasecmp(com_token, "\n"))
+                                                                               continue;
+                                                                       numparameters = 0;
+                                                                       for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
+                                                                       {
+                                                                               if (j < 4)
+                                                                               {
+                                                                                       strlcpy(parameter[j], com_token, sizeof(parameter[j]));
+                                                                                       numparameters = j + 1;
+                                                                               }
+                                                                               if (!COM_ParseToken(&text, true))
+                                                                                       break;
+                                                                       }
+                                                                       if (developer.integer >= 2)
+                                                                       {
+                                                                               Con_Printf("%s %i: ", shadername, passnumber);
+                                                                               for (j = 0;j < numparameters;j++)
+                                                                                       Con_Printf(" %s", parameter[j]);
+                                                                               Con_Print("\n");
+                                                                       }
+                                                                       if (passnumber == 0 && numparameters >= 1)
+                                                                       {
+                                                                               if (!strcasecmp(parameter[0], "blendfunc"))
+                                                                               {
+                                                                                       if (numparameters == 2 && !strcasecmp(parameter[1], "add"))
+                                                                                               flags2 |= Q3TEXTUREFLAG_ADDITIVE;
+                                                                                       else if (numparameters == 3 && !strcasecmp(parameter[1], "gl_one") && !strcasecmp(parameter[2], "gl_one"))
+                                                                                               flags2 |= Q3TEXTUREFLAG_ADDITIVE;
+                                                                                       else if (numparameters == 3 && !strcasecmp(parameter[1], "gl_src_alpha") && !strcasecmp(parameter[2], "gl_one"))
+                                                                                               flags2 |= Q3TEXTUREFLAG_ADDITIVE;
+                                                                               }
+                                                                               else if (numparameters >= 2 && (!strcasecmp(parameter[0], "map") || !strcasecmp(parameter[0], "clampmap")))
+                                                                                       strlcpy(firstpasstexturename, parameter[1], sizeof(firstpasstexturename));
+                                                                               else if (numparameters >= 3 && !strcasecmp(parameter[0], "animmap"))
+                                                                                       strlcpy(firstpasstexturename, parameter[2], sizeof(firstpasstexturename));
+                                                                       }
+                                                                       if (!strcasecmp(parameter[0], "alphafunc"))
+                                                                               flags2 |= Q3TEXTUREFLAG_ALPHATEST;
+                                                                       // break out a level if it was }
+                                                                       if (!strcasecmp(com_token, "}"))
+                                                                               break;
                                                                }
+                                                               passnumber++;
+                                                               continue;
                                                        }
-                                                       else if (!strcasecmp(com_token, "surfaceparm"))
+                                                       numparameters = 0;
+                                                       for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
                                                        {
-                                                               if (COM_ParseToken(&text, true) && strcasecmp(com_token, "\n"))
+                                                               if (j < 4)
                                                                {
-                                                                       if (!strcasecmp(com_token, "alphashadow"))
-                                                                               flags |= Q3SURFACEPARM_ALPHASHADOW;
-                                                                       else if (!strcasecmp(com_token, "areaportal"))
-                                                                               flags |= Q3SURFACEPARM_AREAPORTAL;
-                                                                       else if (!strcasecmp(com_token, "clusterportal"))
-                                                                               flags |= Q3SURFACEPARM_CLUSTERPORTAL;
-                                                                       else if (!strcasecmp(com_token, "detail"))
-                                                                               flags |= Q3SURFACEPARM_DETAIL;
-                                                                       else if (!strcasecmp(com_token, "donotenter"))
-                                                                               flags |= Q3SURFACEPARM_DONOTENTER;
-                                                                       else if (!strcasecmp(com_token, "fog"))
-                                                                               flags |= Q3SURFACEPARM_FOG;
-                                                                       else if (!strcasecmp(com_token, "lava"))
-                                                                               flags |= Q3SURFACEPARM_LAVA;
-                                                                       else if (!strcasecmp(com_token, "lightfilter"))
-                                                                               flags |= Q3SURFACEPARM_LIGHTFILTER;
-                                                                       else if (!strcasecmp(com_token, "metalsteps"))
-                                                                               flags |= Q3SURFACEPARM_METALSTEPS;
-                                                                       else if (!strcasecmp(com_token, "nodamage"))
-                                                                               flags |= Q3SURFACEPARM_NODAMAGE;
-                                                                       else if (!strcasecmp(com_token, "nodlight"))
-                                                                               flags |= Q3SURFACEPARM_NODLIGHT;
-                                                                       else if (!strcasecmp(com_token, "nodraw"))
-                                                                               flags |= Q3SURFACEPARM_NODRAW;
-                                                                       else if (!strcasecmp(com_token, "nodrop"))
-                                                                               flags |= Q3SURFACEPARM_NODROP;
-                                                                       else if (!strcasecmp(com_token, "noimpact"))
-                                                                               flags |= Q3SURFACEPARM_NOIMPACT;
-                                                                       else if (!strcasecmp(com_token, "nolightmap"))
-                                                                               flags |= Q3SURFACEPARM_NOLIGHTMAP;
-                                                                       else if (!strcasecmp(com_token, "nomarks"))
-                                                                               flags |= Q3SURFACEPARM_NOMARKS;
-                                                                       else if (!strcasecmp(com_token, "nomipmaps"))
-                                                                               flags |= Q3SURFACEPARM_NOMIPMAPS;
-                                                                       else if (!strcasecmp(com_token, "nonsolid"))
-                                                                               flags |= Q3SURFACEPARM_NONSOLID;
-                                                                       else if (!strcasecmp(com_token, "origin"))
-                                                                               flags |= Q3SURFACEPARM_ORIGIN;
-                                                                       else if (!strcasecmp(com_token, "playerclip"))
-                                                                               flags |= Q3SURFACEPARM_PLAYERCLIP;
-                                                                       else if (!strcasecmp(com_token, "sky"))
-                                                                               flags |= Q3SURFACEPARM_SKY;
-                                                                       else if (!strcasecmp(com_token, "slick"))
-                                                                               flags |= Q3SURFACEPARM_SLICK;
-                                                                       else if (!strcasecmp(com_token, "slime"))
-                                                                               flags |= Q3SURFACEPARM_SLIME;
-                                                                       else if (!strcasecmp(com_token, "structural"))
-                                                                               flags |= Q3SURFACEPARM_STRUCTURAL;
-                                                                       else if (!strcasecmp(com_token, "trans"))
-                                                                               flags |= Q3SURFACEPARM_TRANS;
-                                                                       else if (!strcasecmp(com_token, "water"))
-                                                                               flags |= Q3SURFACEPARM_WATER;
-                                                                       else
-                                                                               Con_Printf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[i], com_token);
-                                                                       if (!COM_ParseToken(&text, true) || strcasecmp(com_token, "\n"))
-                                                                       {
-                                                                               Con_Printf("%s parsing error: surfaceparm only takes one parameter.\n", search->filenames[i]);
-                                                                               goto parseerror;
-                                                                       }
+                                                                       strlcpy(parameter[j], com_token, sizeof(parameter[j]));
+                                                                       numparameters = j + 1;
                                                                }
+                                                               if (!COM_ParseToken(&text, true))
+                                                                       break;
+                                                       }
+                                                       if (i == 0 && !strcasecmp(com_token, "}"))
+                                                               break;
+                                                       if (developer.integer >= 2)
+                                                       {
+                                                               Con_Printf("%s: ", shadername);
+                                                               for (j = 0;j < numparameters;j++)
+                                                                       Con_Printf(" %s", parameter[j]);
+                                                               Con_Print("\n");
+                                                       }
+                                                       if (numparameters < 1)
+                                                               continue;
+                                                       if (!strcasecmp(parameter[0], "surfaceparm") && numparameters >= 2)
+                                                       {
+                                                               if (!strcasecmp(parameter[1], "alphashadow"))
+                                                                       flags |= Q3SURFACEPARM_ALPHASHADOW;
+                                                               else if (!strcasecmp(parameter[1], "areaportal"))
+                                                                       flags |= Q3SURFACEPARM_AREAPORTAL;
+                                                               else if (!strcasecmp(parameter[1], "clusterportal"))
+                                                                       flags |= Q3SURFACEPARM_CLUSTERPORTAL;
+                                                               else if (!strcasecmp(parameter[1], "detail"))
+                                                                       flags |= Q3SURFACEPARM_DETAIL;
+                                                               else if (!strcasecmp(parameter[1], "donotenter"))
+                                                                       flags |= Q3SURFACEPARM_DONOTENTER;
+                                                               else if (!strcasecmp(parameter[1], "fog"))
+                                                                       flags |= Q3SURFACEPARM_FOG;
+                                                               else if (!strcasecmp(parameter[1], "lava"))
+                                                                       flags |= Q3SURFACEPARM_LAVA;
+                                                               else if (!strcasecmp(parameter[1], "lightfilter"))
+                                                                       flags |= Q3SURFACEPARM_LIGHTFILTER;
+                                                               else if (!strcasecmp(parameter[1], "metalsteps"))
+                                                                       flags |= Q3SURFACEPARM_METALSTEPS;
+                                                               else if (!strcasecmp(parameter[1], "nodamage"))
+                                                                       flags |= Q3SURFACEPARM_NODAMAGE;
+                                                               else if (!strcasecmp(parameter[1], "nodlight"))
+                                                                       flags |= Q3SURFACEPARM_NODLIGHT;
+                                                               else if (!strcasecmp(parameter[1], "nodraw"))
+                                                                       flags |= Q3SURFACEPARM_NODRAW;
+                                                               else if (!strcasecmp(parameter[1], "nodrop"))
+                                                                       flags |= Q3SURFACEPARM_NODROP;
+                                                               else if (!strcasecmp(parameter[1], "noimpact"))
+                                                                       flags |= Q3SURFACEPARM_NOIMPACT;
+                                                               else if (!strcasecmp(parameter[1], "nolightmap"))
+                                                                       flags |= Q3SURFACEPARM_NOLIGHTMAP;
+                                                               else if (!strcasecmp(parameter[1], "nomarks"))
+                                                                       flags |= Q3SURFACEPARM_NOMARKS;
+                                                               else if (!strcasecmp(parameter[1], "nomipmaps"))
+                                                                       flags |= Q3SURFACEPARM_NOMIPMAPS;
+                                                               else if (!strcasecmp(parameter[1], "nonsolid"))
+                                                                       flags |= Q3SURFACEPARM_NONSOLID;
+                                                               else if (!strcasecmp(parameter[1], "origin"))
+                                                                       flags |= Q3SURFACEPARM_ORIGIN;
+                                                               else if (!strcasecmp(parameter[1], "playerclip"))
+                                                                       flags |= Q3SURFACEPARM_PLAYERCLIP;
+                                                               else if (!strcasecmp(parameter[1], "sky"))
+                                                                       flags |= Q3SURFACEPARM_SKY;
+                                                               else if (!strcasecmp(parameter[1], "slick"))
+                                                                       flags |= Q3SURFACEPARM_SLICK;
+                                                               else if (!strcasecmp(parameter[1], "slime"))
+                                                                       flags |= Q3SURFACEPARM_SLIME;
+                                                               else if (!strcasecmp(parameter[1], "structural"))
+                                                                       flags |= Q3SURFACEPARM_STRUCTURAL;
+                                                               else if (!strcasecmp(parameter[1], "trans"))
+                                                                       flags |= Q3SURFACEPARM_TRANS;
+                                                               else if (!strcasecmp(parameter[1], "water"))
+                                                                       flags |= Q3SURFACEPARM_WATER;
                                                                else
-                                                               {
-                                                                       Con_Printf("%s parsing error: surfaceparm expects a parameter.\n", search->filenames[i]);
-                                                                       goto parseerror;
-                                                               }
+                                                                       Con_Printf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[i], parameter[1]);
                                                        }
-                                                       else if (!strcasecmp(com_token, "sky"))
+                                                       else if (!strcasecmp(parameter[0], "sky") && numparameters >= 2)
+                                                               strlcpy(sky, parameter[1], sizeof(sky));
+                                                       else if (!strcasecmp(parameter[0], "skyparms") && numparameters >= 2)
                                                        {
-                                                               if (COM_ParseToken(&text, true) && strcasecmp(com_token, "\n"))
-                                                                       if (strlen(com_token) < sizeof(sky))
-                                                                               strcpy(sky, com_token);
+                                                               if (!atoi(parameter[1]) && strcasecmp(parameter[1], "-"))
+                                                                       strlcpy(sky, parameter[1], sizeof(sky));
                                                        }
-                                                       else if (!strcasecmp(com_token, "skyparms"))
+                                                       else if (!strcasecmp(parameter[0], "cull") && numparameters >= 2)
                                                        {
-                                                               if (COM_ParseToken(&text, true) && strcasecmp(com_token, "\n"))
-                                                               {
-                                                                       if (strlen(com_token) < sizeof(sky) && !atoi(com_token) && strcasecmp(com_token, "-"))
-                                                                               strcpy(sky, com_token);
-                                                                       if (COM_ParseToken(&text, true) && strcasecmp(com_token, "\n"))
-                                                                               COM_ParseToken(&text, true);
-                                                               }
+                                                               if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "twosided"))
+                                                                       flags2 |= Q3TEXTUREFLAG_TWOSIDED;
                                                        }
-                                                       else
+                                                       else if (!strcasecmp(parameter[0], "nomipmaps"))
+                                                               flags2 |= Q3TEXTUREFLAG_NOMIPMAPS;
+                                                       else if (!strcasecmp(parameter[0], "nopicmip"))
+                                                               flags2 |= Q3TEXTUREFLAG_NOPICMIP;
+                                                       else if (!strcasecmp(parameter[0], "deformvertexes") && numparameters >= 2)
                                                        {
-                                                               // look for linebreak or }
-                                                               while(COM_ParseToken(&text, true) && strcasecmp(com_token, "\n") && strcasecmp(com_token, "}"));
-                                                               // break out to top level if it was }
-                                                               if (!strcasecmp(com_token, "}"))
-                                                                       break;
+                                                               if (!strcasecmp(parameter[1], "autosprite") && numparameters == 2)
+                                                                       flags2 |= Q3TEXTUREFLAG_AUTOSPRITE;
+                                                               if (!strcasecmp(parameter[1], "autosprite2") && numparameters == 2)
+                                                                       flags2 |= Q3TEXTUREFLAG_AUTOSPRITE2;
                                                        }
                                                }
+                                               // force transparent render path for a number of odd
+                                               // shader effects to avoid bogging down the normal
+                                               // render path unnecessarily
+                                               if (flags2 & (Q3TEXTUREFLAG_ADDITIVE | Q3TEXTUREFLAG_AUTOSPRITE | Q3TEXTUREFLAG_AUTOSPRITE2 | Q3TEXTUREFLAG_ALPHATEST))
+                                                       flags |= Q3SURFACEPARM_TRANS;
                                                // add shader to list (shadername and flags)
                                                // actually here we just poke into the texture settings
                                                for (j = 0, out = loadmodel->brushq3.data_textures;j < loadmodel->brushq3.num_textures;j++, out++)
@@ -3744,6 +3968,8 @@ static void Mod_Q3BSP_LoadTextures(lump_t *l)
                                                        if (!strcasecmp(out->name, shadername))
                                                        {
                                                                out->surfaceparms = flags;
+                                                               out->textureflags = flags2;
+                                                               strlcpy(out->firstpasstexturename, firstpasstexturename, sizeof(out->firstpasstexturename));
                                                                if ((flags & Q3SURFACEPARM_SKY) && sky[0])
                                                                {
                                                                        // quake3 seems to append a _ to the skybox name, so this must do so as well
@@ -3773,16 +3999,20 @@ parseerror:
                        Con_DPrintf("%s: No shader found for texture \"%s\"\n", loadmodel->name, out->name);
                        out->surfaceparms = 0;
                        // these are defaults
-                       if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk")
-                        || !strcmp(out->name, "nodraw") || !strcmp(out->name, "common/nodraw") || !strcmp(out->name, "textures/common/nodraw"))
-                               out->surfaceparms |= Q3SURFACEPARM_NODRAW;
-                       if (!strncmp(out->name, "textures/skies/", 15))
-                               out->surfaceparms |= Q3SURFACEPARM_SKY;
-                       if (R_TextureHasAlpha(out->skin.base))
-                               out->surfaceparms |= Q3SURFACEPARM_TRANS;
+                       //if (!strncmp(out->name, "textures/skies/", 15))
+                       //      out->surfaceparms |= Q3SURFACEPARM_SKY;
+                       //if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk")
+                       // || !strcmp(out->name, "nodraw") || !strcmp(out->name, "common/nodraw") || !strcmp(out->name, "textures/common/nodraw"))
+                       //      out->surfaceparms |= Q3SURFACEPARM_NODRAW;
+                       //if (R_TextureHasAlpha(out->skin.base))
+                       //      out->surfaceparms |= Q3SURFACEPARM_TRANS;
                }
+               if (!Mod_LoadSkinFrame(&out->skin, out->name, (((out->textureflags & Q3TEXTUREFLAG_NOMIPMAPS) || (out->surfaceparms & Q3SURFACEPARM_NOMIPMAPS)) ? 0 : TEXF_MIPMAP) | TEXF_ALPHA | TEXF_PRECACHE | (out->textureflags & Q3TEXTUREFLAG_NOPICMIP ? 0 : TEXF_PICMIP), false, true, true))
+                       if (!Mod_LoadSkinFrame(&out->skin, out->firstpasstexturename, (((out->textureflags & Q3TEXTUREFLAG_NOMIPMAPS) || (out->surfaceparms & Q3SURFACEPARM_NOMIPMAPS)) ? 0 : TEXF_MIPMAP) | TEXF_ALPHA | TEXF_PRECACHE | (out->textureflags & Q3TEXTUREFLAG_NOPICMIP ? 0 : TEXF_PICMIP), false, true, true))
+                               Con_Printf("%s: texture loading for shader \"%s\" failed (first layer \"%s\" not found either)\n", loadmodel->name, out->name, out->firstpasstexturename);
        }
-       Con_DPrintf("%s: %i textures missing shaders\n", loadmodel->name, c);
+       if (c)
+               Con_DPrintf("%s: %i textures missing shaders\n", loadmodel->name, c);
 }
 
 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
@@ -4011,7 +4241,7 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
 {
        q3dface_t *in;
        q3msurface_t *out;
-       int i, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xlevel, ylevel, row0, row1, x, y, *e, finalvertices, finaltriangles;
+       int i, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xtess, ytess, finalvertices, finaltriangles, firstvertex, firstelement, type, oldnumtriangles, oldnumtriangles2;
        //int *originalelement3i;
        //int *originalneighbor3i;
        float *originalvertex3f;
@@ -4035,16 +4265,16 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
        for (i = 0;i < count;i++, in++, out++)
        {
                // check face type first
-               out->type = LittleLong(in->type);
-               if (out->type != Q3FACETYPE_POLYGON
-                && out->type != Q3FACETYPE_PATCH
-                && out->type != Q3FACETYPE_MESH
-                && out->type != Q3FACETYPE_FLARE)
+               type = LittleLong(in->type);
+               if (type != Q3FACETYPE_POLYGON
+                && type != Q3FACETYPE_PATCH
+                && type != Q3FACETYPE_MESH
+                && type != Q3FACETYPE_FLARE)
                {
-                       Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
+                       Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, type);
                        out->num_vertices = 0;
                        out->num_triangles = 0;
-                       out->type = 0; // error
+                       type = 0; // error
                        continue;
                }
 
@@ -4054,7 +4284,7 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                        Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
                        out->num_vertices = 0;
                        out->num_triangles = 0;
-                       out->type = 0; // error
+                       type = 0; // error
                        continue;
                        n = 0;
                }
@@ -4062,7 +4292,8 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                n = LittleLong(in->effectindex);
                if (n < -1 || n >= loadmodel->brushq3.num_effects)
                {
-                       Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
+                       if (developer.integer >= 2)
+                               Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
                        n = -1;
                }
                if (n == -1)
@@ -4070,79 +4301,81 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                else
                        out->effect = loadmodel->brushq3.data_effects + n;
                n = LittleLong(in->lightmapindex);
-               if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
+               if (n >= loadmodel->brushq3.num_lightmaps)
                {
-                       Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
+                       Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
                        n = -1;
                }
+               else if (n < 0)
+                       n = -1;
                if (n == -1)
                        out->lightmaptexture = NULL;
                else
                        out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
 
-               out->firstvertex = LittleLong(in->firstvertex);
+               firstvertex = LittleLong(in->firstvertex);
                out->num_vertices = LittleLong(in->numvertices);
-               out->firstelement = LittleLong(in->firstelement);
+               firstelement = LittleLong(in->firstelement);
                out->num_triangles = LittleLong(in->numelements) / 3;
                if (out->num_triangles * 3 != LittleLong(in->numelements))
                {
                        Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): numelements %i is not a multiple of 3\n", i, out->texture->name, LittleLong(in->numelements));
                        out->num_vertices = 0;
                        out->num_triangles = 0;
-                       out->type = 0; // error
+                       type = 0; // error
                        continue;
                }
-               if (out->firstvertex < 0 || out->firstvertex + out->num_vertices > loadmodel->brushq3.num_vertices)
+               if (firstvertex < 0 || firstvertex + out->num_vertices > loadmodel->brushq3.num_vertices)
                {
-                       Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid vertex range %i : %i (%i vertices)\n", i, out->texture->name, out->firstvertex, out->firstvertex + out->num_vertices, loadmodel->brushq3.num_vertices);
+                       Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid vertex range %i : %i (%i vertices)\n", i, out->texture->name, firstvertex, firstvertex + out->num_vertices, loadmodel->brushq3.num_vertices);
                        out->num_vertices = 0;
                        out->num_triangles = 0;
-                       out->type = 0; // error
+                       type = 0; // error
                        continue;
                }
-               if (out->firstelement < 0 || out->firstelement + out->num_triangles * 3 > loadmodel->brushq3.num_triangles * 3)
+               if (firstelement < 0 || firstelement + out->num_triangles * 3 > loadmodel->brushq3.num_triangles * 3)
                {
-                       Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid element range %i : %i (%i elements)\n", i, out->texture->name, out->firstelement, out->firstelement + out->num_triangles * 3, loadmodel->brushq3.num_triangles * 3);
+                       Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid element range %i : %i (%i elements)\n", i, out->texture->name, firstelement, firstelement + out->num_triangles * 3, loadmodel->brushq3.num_triangles * 3);
                        out->num_vertices = 0;
                        out->num_triangles = 0;
-                       out->type = 0; // error
+                       type = 0; // error
                        continue;
                }
-               switch(out->type)
+               switch(type)
                {
                case Q3FACETYPE_POLYGON:
                case Q3FACETYPE_MESH:
                        // no processing necessary
-                       out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
-                       out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
-                       out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
-                       out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
-                       out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
-                       out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
-                       out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
-                       out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
-                       out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
+                       out->data_vertex3f = loadmodel->brushq3.data_vertex3f + firstvertex * 3;
+                       out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + firstvertex * 2;
+                       out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + firstvertex * 2;
+                       out->data_svector3f = loadmodel->brushq3.data_svector3f + firstvertex * 3;
+                       out->data_tvector3f = loadmodel->brushq3.data_tvector3f + firstvertex * 3;
+                       out->data_normal3f = loadmodel->brushq3.data_normal3f + firstvertex * 3;
+                       out->data_color4f = loadmodel->brushq3.data_color4f + firstvertex * 4;
+                       out->data_element3i = loadmodel->brushq3.data_element3i + firstelement;
+                       out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + firstelement;
                        break;
                case Q3FACETYPE_PATCH:
                        patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
                        patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
-                       if (patchsize[0] < 1 || patchsize[1] < 1)
+                       if (patchsize[0] < 3 || patchsize[1] < 3 || !(patchsize[0] & 1) || !(patchsize[1] & 1) || patchsize[0] * patchsize[1] >= min(r_subdivisions_maxvertices.integer, r_subdivisions_collision_maxvertices.integer))
                        {
                                Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
                                out->num_vertices = 0;
                                out->num_triangles = 0;
-                               out->type = 0; // error
+                               type = 0; // error
                                continue;
                        }
-                       originalvertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
-                       //originalsvector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
-                       //originaltvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
-                       //originalnormal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
-                       originaltexcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
-                       originaltexcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
-                       originalcolor4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
-                       //originalelement3i = loadmodel->brushq3.data_element3i + out->firstelement;
-                       //originalneighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
+                       originalvertex3f = loadmodel->brushq3.data_vertex3f + firstvertex * 3;
+                       //originalsvector3f = loadmodel->brushq3.data_svector3f + firstvertex * 3;
+                       //originaltvector3f = loadmodel->brushq3.data_tvector3f + firstvertex * 3;
+                       //originalnormal3f = loadmodel->brushq3.data_normal3f + firstvertex * 3;
+                       originaltexcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + firstvertex * 2;
+                       originaltexcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + firstvertex * 2;
+                       originalcolor4f = loadmodel->brushq3.data_color4f + firstvertex * 4;
+                       //originalelement3i = loadmodel->brushq3.data_element3i + firstelement;
+                       //originalneighbor3i = loadmodel->brushq3.data_neighbor3i + firstelement;
                        /*
                        originalvertex3f = out->data_vertex3f;
                        //originalsvector3f = out->data_svector3f;
@@ -4155,24 +4388,24 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                        //originalneighbor3i = out->data_neighbor3i;
                        */
                        // convert patch to Q3FACETYPE_MESH
-                       xlevel = QuadraticBSplinePatchSubdivisionLevelOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value, 10);
-                       ylevel = QuadraticBSplinePatchSubdivisionLevelOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value, 10);
+                       xtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value);
+                       ytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value);
                        // bound to user settings
-                       xlevel = bound(r_subdivisions_minlevel.integer, xlevel, r_subdivisions_maxlevel.integer);
-                       ylevel = bound(r_subdivisions_minlevel.integer, ylevel, r_subdivisions_maxlevel.integer);
+                       xtess = bound(r_subdivisions_mintess.integer, xtess, r_subdivisions_maxtess.integer);
+                       ytess = bound(r_subdivisions_mintess.integer, ytess, r_subdivisions_maxtess.integer);
                        // bound to sanity settings
-                       xlevel = bound(0, xlevel, 10);
-                       ylevel = bound(0, ylevel, 10);
+                       xtess = bound(1, xtess, 1024);
+                       ytess = bound(1, ytess, 1024);
                        // bound to user limit on vertices
-                       while ((xlevel > 0 || ylevel > 0) && (((patchsize[0] - 1) << xlevel) + 1) * (((patchsize[1] - 1) << ylevel) + 1) > min(r_subdivisions_maxvertices.integer, 262144))
+                       while ((xtess > 1 || ytess > 1) && (((patchsize[0] - 1) * xtess) + 1) * (((patchsize[1] - 1) * ytess) + 1) > min(r_subdivisions_maxvertices.integer, 262144))
                        {
-                               if (xlevel > ylevel)
-                                       xlevel--;
+                               if (xtess > ytess)
+                                       xtess--;
                                else
-                                       ylevel--;
+                                       ytess--;
                        }
-                       finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
-                       finalheight = ((patchsize[1] - 1) << ylevel) + 1;
+                       finalwidth = ((patchsize[0] - 1) * xtess) + 1;
+                       finalheight = ((patchsize[1] - 1) * ytess) + 1;
                        finalvertices = finalwidth * finalheight;
                        finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
                        out->data_vertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[20]) * finalvertices);
@@ -4184,37 +4417,19 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                        out->data_texcoordlightmap2f = out->data_texcoordtexture2f + finalvertices * 2;
                        out->data_element3i = Mem_Alloc(loadmodel->mempool, sizeof(int[6]) * finaltriangles);
                        out->data_neighbor3i = out->data_element3i + finaltriangles * 3;
-                       out->type = Q3FACETYPE_MESH;
-                       out->firstvertex = -1;
+                       type = Q3FACETYPE_MESH;
+                       firstvertex = -1;
                        out->num_vertices = finalvertices;
-                       out->firstelement = -1;
+                       firstelement = -1;
                        out->num_triangles = finaltriangles;
                        // generate geometry
                        // (note: normals are skipped because they get recalculated)
-                       QuadraticBSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_vertex3f);
-                       QuadraticBSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordtexture2f, out->data_texcoordtexture2f);
-                       QuadraticBSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordlightmap2f, out->data_texcoordlightmap2f);
-                       QuadraticBSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 4, originalcolor4f, out->data_color4f);
-                       // generate elements
-                       e = out->data_element3i;
-                       for (y = 0;y < finalheight - 1;y++)
-                       {
-                               row0 = (y + 0) * finalwidth;
-                               row1 = (y + 1) * finalwidth;
-                               for (x = 0;x < finalwidth - 1;x++)
-                               {
-                                       *e++ = row0;
-                                       *e++ = row1;
-                                       *e++ = row0 + 1;
-                                       *e++ = row1;
-                                       *e++ = row1 + 1;
-                                       *e++ = row0 + 1;
-                                       row0++;
-                                       row1++;
-                               }
-                       }
-                       out->num_triangles = Mod_RemoveDegenerateTriangles(out->num_triangles, out->data_element3i, out->data_element3i, out->data_vertex3f);
-                       if (developer.integer)
+                       Q3PatchTesselateFloat(3, sizeof(float[3]), out->data_vertex3f, patchsize[0], patchsize[1], sizeof(float[3]), originalvertex3f, xtess, ytess);
+                       Q3PatchTesselateFloat(2, sizeof(float[2]), out->data_texcoordtexture2f, patchsize[0], patchsize[1], sizeof(float[2]), originaltexcoordtexture2f, xtess, ytess);
+                       Q3PatchTesselateFloat(2, sizeof(float[2]), out->data_texcoordlightmap2f, patchsize[0], patchsize[1], sizeof(float[2]), originaltexcoordlightmap2f, xtess, ytess);
+                       Q3PatchTesselateFloat(4, sizeof(float[4]), out->data_color4f, patchsize[0], patchsize[1], sizeof(float[4]), originalcolor4f, xtess, ytess);
+                       Q3PatchTriangleElements(out->data_element3i, finalwidth, finalheight);
+                       if (developer.integer >= 2)
                        {
                                if (out->num_triangles < finaltriangles)
                                        Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles, %i degenerate triangles removed (leaving %i)\n", patchsize[0], patchsize[1], out->num_vertices, finaltriangles, finaltriangles - out->num_triangles, out->num_triangles);
@@ -4223,65 +4438,51 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                        }
                        // q3map does not put in collision brushes for curves... ugh
                        // build the lower quality collision geometry
-                       out->collisions = true;
-                       xlevel = QuadraticBSplinePatchSubdivisionLevelOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value, 10);
-                       ylevel = QuadraticBSplinePatchSubdivisionLevelOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value, 10);
+                       xtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value);
+                       ytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value);
                        // bound to user settings
-                       xlevel = bound(r_subdivisions_collision_minlevel.integer, xlevel, r_subdivisions_collision_maxlevel.integer);
-                       ylevel = bound(r_subdivisions_collision_minlevel.integer, ylevel, r_subdivisions_collision_maxlevel.integer);
+                       xtess = bound(r_subdivisions_collision_mintess.integer, xtess, r_subdivisions_collision_maxtess.integer);
+                       ytess = bound(r_subdivisions_collision_mintess.integer, ytess, r_subdivisions_collision_maxtess.integer);
                        // bound to sanity settings
-                       xlevel = bound(0, xlevel, 10);
-                       ylevel = bound(0, ylevel, 10);
+                       xtess = bound(1, xtess, 1024);
+                       ytess = bound(1, ytess, 1024);
                        // bound to user limit on vertices
-                       while ((xlevel > 0 || ylevel > 0) && (((patchsize[0] - 1) << xlevel) + 1) * (((patchsize[1] - 1) << ylevel) + 1) > min(r_subdivisions_collision_maxvertices.integer, 262144))
+                       while ((xtess > 1 || ytess > 1) && (((patchsize[0] - 1) * xtess) + 1) * (((patchsize[1] - 1) * ytess) + 1) > min(r_subdivisions_collision_maxvertices.integer, 262144))
                        {
-                               if (xlevel > ylevel)
-                                       xlevel--;
+                               if (xtess > ytess)
+                                       xtess--;
                                else
-                                       ylevel--;
+                                       ytess--;
                        }
-                       finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
-                       finalheight = ((patchsize[1] - 1) << ylevel) + 1;
+                       finalwidth = ((patchsize[0] - 1) * xtess) + 1;
+                       finalheight = ((patchsize[1] - 1) * ytess) + 1;
                        finalvertices = finalwidth * finalheight;
                        finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
+
                        out->data_collisionvertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * finalvertices);
                        out->data_collisionelement3i = Mem_Alloc(loadmodel->mempool, sizeof(int[3]) * finaltriangles);
                        out->num_collisionvertices = finalvertices;
                        out->num_collisiontriangles = finaltriangles;
-                       QuadraticBSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_collisionvertex3f);
-                       // generate elements
-                       e = out->data_collisionelement3i;
-                       for (y = 0;y < finalheight - 1;y++)
-                       {
-                               row0 = (y + 0) * finalwidth;
-                               row1 = (y + 1) * finalwidth;
-                               for (x = 0;x < finalwidth - 1;x++)
-                               {
-                                       *e++ = row0;
-                                       *e++ = row1;
-                                       *e++ = row0 + 1;
-                                       *e++ = row1;
-                                       *e++ = row1 + 1;
-                                       *e++ = row0 + 1;
-                                       row0++;
-                                       row1++;
-                               }
-                       }
+                       Q3PatchTesselateFloat(3, sizeof(float[3]), out->data_collisionvertex3f, patchsize[0], patchsize[1], sizeof(float[3]), originalvertex3f, xtess, ytess);
+                       Q3PatchTriangleElements(out->data_collisionelement3i, finalwidth, finalheight);
+
+                       Mod_SnapVertices(3, out->num_vertices, out->data_vertex3f, 0.25);
+                       Mod_SnapVertices(3, out->num_collisionvertices, out->data_collisionvertex3f, 1);
+
+                       oldnumtriangles = out->num_triangles;
+                       oldnumtriangles2 = out->num_collisiontriangles;
+                       out->num_triangles = Mod_RemoveDegenerateTriangles(out->num_triangles, out->data_element3i, out->data_element3i, out->data_vertex3f);
                        out->num_collisiontriangles = Mod_RemoveDegenerateTriangles(out->num_collisiontriangles, out->data_collisionelement3i, out->data_collisionelement3i, out->data_collisionvertex3f);
                        if (developer.integer)
-                       {
-                               if (out->num_collisiontriangles < finaltriangles)
-                                       Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided for collisions to %i vertices / %i triangles, %i degenerate triangles removed (leaving %i)\n", patchsize[0], patchsize[1], out->num_collisionvertices, finaltriangles, finaltriangles - out->num_collisiontriangles, out->num_collisiontriangles);
-                               else
-                                       Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided for collisions to %i vertices / %i triangles\n", patchsize[0], patchsize[1], out->num_collisionvertices, out->num_collisiontriangles);
-                       }
+                               Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve became %i:%i vertices / %i:%i triangles (%i:%i degenerate)\n", patchsize[0], patchsize[1], out->num_vertices, out->num_collisionvertices, oldnumtriangles, oldnumtriangles2, oldnumtriangles - out->num_triangles, oldnumtriangles2 - out->num_collisiontriangles);
                        break;
                case Q3FACETYPE_FLARE:
-                       Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
+                       if (developer.integer >= 2)
+                               Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
                        // don't render it
                        out->num_vertices = 0;
                        out->num_triangles = 0;
-                       out->type = 0;
+                       type = 0;
                        break;
                }
                for (j = 0, invalidelements = 0;j < out->num_triangles * 3;j++)
@@ -4289,7 +4490,7 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                                invalidelements++;
                if (invalidelements)
                {
-                       Con_Printf("Mod_Q3BSP_LoadFaces: Warning: face #%i has %i invalid elements, type = %i, texture->name = \"%s\", texture->surfaceflags = %i, texture->nativecontents = %i, firstvertex = %i, numvertices = %i, firstelement = %i, numelements = %i, elements list:\n", i, invalidelements, out->type, out->texture->name, out->texture->surfaceflags, out->texture->nativecontents, out->firstvertex, out->num_vertices, out->firstelement, out->num_triangles * 3);
+                       Con_Printf("Mod_Q3BSP_LoadFaces: Warning: face #%i has %i invalid elements, type = %i, texture->name = \"%s\", texture->surfaceflags = %i, texture->nativecontents = %i, firstvertex = %i, numvertices = %i, firstelement = %i, numelements = %i, elements list:\n", i, invalidelements, type, out->texture->name, out->texture->surfaceflags, out->texture->nativecontents, firstvertex, out->num_vertices, firstelement, out->num_triangles * 3);
                        for (j = 0;j < out->num_triangles * 3;j++)
                        {
                                Con_Printf(" %i", out->data_element3i[j]);
@@ -4493,12 +4694,15 @@ static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
        loadmodel->brushq3.data_leaffaces = out;
        loadmodel->brushq3.num_leaffaces = count;
 
+       loadmodel->brushq3.data_leaffacenums = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
+
        for (i = 0;i < count;i++, in++, out++)
        {
                n = LittleLong(*in);
                if (n < 0 || n >= loadmodel->brushq3.num_faces)
                        Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
                *out = loadmodel->brushq3.data_faces + n;
+               loadmodel->brushq3.data_leaffacenums[i] = n;
        }
 }
 
@@ -4534,6 +4738,7 @@ static void Mod_Q3BSP_LoadLeafs(lump_t *l)
                if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
                        Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
                out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
+               out->firstleaffacenum = loadmodel->brushq3.data_leaffacenums + n;
                out->numleaffaces = c;
                n = LittleLong(in->firstleafbrush);
                c = LittleLong(in->numleafbrushes);
@@ -4677,7 +4882,7 @@ static void Mod_Q3BSP_LoadPVS(lump_t *l)
                // leafs to find real number of clusters
                loadmodel->brush.num_pvsclusters = 1;
                for (i = 0;i < loadmodel->brushq3.num_leafs;i++)
-                       loadmodel->brush.num_pvsclusters = min(loadmodel->brush.num_pvsclusters, loadmodel->brushq3.data_leafs[i].clusterindex + 1);
+                       loadmodel->brush.num_pvsclusters = max(loadmodel->brush.num_pvsclusters, loadmodel->brushq3.data_leafs[i].clusterindex + 1);
 
                // create clusters
                loadmodel->brush.num_pvsclusterbytes = (loadmodel->brush.num_pvsclusters + 7) / 8;
@@ -4860,7 +5065,7 @@ static void Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace_t *trace, q3mnode_t *node
                for (i = 0;i < leaf->numleaffaces;i++)
                {
                        face = leaf->firstleafface[i];
-                       if (face->collisions && face->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
+                       if (face->num_collisiontriangles && face->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
                        {
                                face->collisionmarkframe = markframe;
                                Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
@@ -5240,9 +5445,9 @@ static void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, q3mnode_t *nod
                for (i = 0;i < leaf->numleaffaces;i++)
                {
                        face = leaf->firstleafface[i];
-                       if (face->collisions && face->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
+                       if (face->num_collisiontriangles && face->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
                        {
-                               face->markframe = markframe;
+                               face->collisionmarkframe = markframe;
                                Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
                        }
                }
@@ -5296,7 +5501,7 @@ static void Mod_Q3BSP_TraceBox(model_t *model, int frame, trace_t *trace, const
                                        for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
                                        {
                                                face = model->brushq3.data_thismodel->firstface + i;
-                                               if (face->collisions)
+                                               if (face->num_collisiontriangles)
                                                        Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
                                        }
                                }
@@ -5320,7 +5525,7 @@ static void Mod_Q3BSP_TraceBox(model_t *model, int frame, trace_t *trace, const
                                for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
                                {
                                        face = model->brushq3.data_thismodel->firstface + i;
-                                       if (face->collisions)
+                                       if (face->num_collisiontriangles)
                                                Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
                                }
                        }
@@ -5534,11 +5739,48 @@ void Mod_Q3BSP_GetVisible(model_t *model, const vec3_t point, const vec3_t mins,
 }
 */
 
+void Mod_Q3BSP_BuildTextureFaceLists(void)
+{
+       int i, j;
+       loadmodel->brushq3.data_texturefaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummodelsurfaces * sizeof(q3msurface_t *));
+       loadmodel->brushq3.data_texturefacenums = Mem_Alloc(loadmodel->mempool, loadmodel->nummodelsurfaces * sizeof(int));
+       for (i = 0;i < loadmodel->brushq3.num_textures;i++)
+               loadmodel->brushq3.data_textures[i].numfaces = 0;
+       for (i = 0;i < loadmodel->nummodelsurfaces;i++)
+               loadmodel->brushq3.data_faces[loadmodel->surfacelist[i]].texture->numfaces++;
+       j = 0;
+       for (i = 0;i < loadmodel->brushq3.num_textures;i++)
+       {
+               loadmodel->brushq3.data_textures[i].facelist = loadmodel->brushq3.data_texturefaces + j;
+               loadmodel->brushq3.data_textures[i].facenumlist = loadmodel->brushq3.data_texturefacenums + j;
+               j += loadmodel->brushq3.data_textures[i].numfaces;
+               loadmodel->brushq3.data_textures[i].numfaces = 0;
+       }
+       for (i = 0;i < loadmodel->nummodelsurfaces;i++)
+       {
+               loadmodel->brushq3.data_faces[loadmodel->surfacelist[i]].texture->facenumlist[loadmodel->brushq3.data_faces[loadmodel->surfacelist[i]].texture->numfaces] = loadmodel->surfacelist[i];
+               loadmodel->brushq3.data_faces[loadmodel->surfacelist[i]].texture->facelist[loadmodel->brushq3.data_faces[loadmodel->surfacelist[i]].texture->numfaces++] = loadmodel->brushq3.data_faces + loadmodel->surfacelist[i];
+       }
+}
+
+void Mod_Q3BSP_RecursiveFindNumLeafs(q3mnode_t *node)
+{
+       int numleafs;
+       while (node->plane)
+       {
+               Mod_Q3BSP_RecursiveFindNumLeafs(node->children[0]);
+               node = node->children[1];
+       }
+       numleafs = ((q3mleaf_t *)node - loadmodel->brushq3.data_leafs) + 1;
+       if (loadmodel->brushq3.num_leafs < numleafs)
+               loadmodel->brushq3.num_leafs = numleafs;
+}
+
 extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
 extern void R_Q3BSP_Draw(struct entity_render_s *ent);
 extern void R_Q3BSP_GetLightInfo(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, vec3_t outmins, vec3_t outmaxs, int *outclusterlist, qbyte *outclusterpvs, int *outnumclusterspointer, int *outsurfacelist, qbyte *outsurfacepvs, int *outnumsurfacespointer);
 extern void R_Q3BSP_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, int numsurfaces, const int *surfacelist);
-extern void R_Q3BSP_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *lightcubemap, int numsurfaces, const int *surfacelist);
+extern void R_Q3BSP_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *lightcubemap, vec_t ambientscale, vec_t diffusescale, vec_t specularscale, int numsurfaces, const int *surfacelist);
 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
 {
        int i, j, numshadowmeshtriangles;
@@ -5556,11 +5798,7 @@ void Mod_Q3BSP_Load(model_t *mod, void *buffer)
        if (i != Q3BSPVERSION)
                Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
        if (mod->isworldmodel)
-       {
                Cvar_SetValue("halflifebsp", false);
-               // until we get a texture for it...
-               R_ResetQuakeSky();
-       }
 
        mod->soundfromcenter = true;
        mod->TraceBox = Mod_Q3BSP_TraceBox;
@@ -5580,8 +5818,13 @@ void Mod_Q3BSP_Load(model_t *mod, void *buffer)
        mod_base = (qbyte *)header;
 
        // swap all the lumps
-       for (i = 0;i < (int) sizeof(*header) / 4;i++)
-               ((int *)header)[i] = LittleLong(((int *)header)[i]);
+       header->ident = LittleLong(header->ident);
+       header->version = LittleLong(header->version);
+       for (i = 0;i < Q3HEADER_LUMPS;i++)
+       {
+               header->lumps[i].fileofs = LittleLong(header->lumps[i].fileofs);
+               header->lumps[i].filelen = LittleLong(header->lumps[i].filelen);
+       }
 
        Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
        Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
@@ -5614,16 +5857,19 @@ void Mod_Q3BSP_Load(model_t *mod, void *buffer)
                Mod_ShadowMesh_AddMesh(loadmodel->mempool, loadmodel->brush.shadowmesh, NULL, NULL, NULL, face->data_vertex3f, NULL, NULL, NULL, NULL, face->num_triangles, face->data_element3i);
        loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true);
        Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
-       
+
+       loadmodel->brushq3.num_leafs = 0;
+       Mod_Q3BSP_RecursiveFindNumLeafs(loadmodel->brushq3.data_nodes);
+
+       mod = loadmodel;
        for (i = 0;i < loadmodel->brushq3.num_models;i++)
        {
-               if (i == 0)
-                       mod = loadmodel;
-               else
+               if (i > 0)
                {
                        char name[10];
                        // LordHavoc: only register submodels if it is the world
-                       // (prevents bsp models from replacing world submodels)
+                       // (prevents external bsp models from replacing world submodels with
+                       //  their own)
                        if (!loadmodel->isworldmodel)
                                continue;
                        // duplicate the basic information
@@ -5671,6 +5917,8 @@ void Mod_Q3BSP_Load(model_t *mod, void *buffer)
                if (j < mod->brushq3.data_thismodel->numfaces)
                        mod->DrawSky = R_Q3BSP_DrawSky;
        }
+
+       Mod_Q3BSP_BuildTextureFaceLists();
 }
 
 void Mod_IBSP_Load(model_t *mod, void *buffer)