]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - model_brush.c
made darkplaces able to compile as C++ again, and fixed all conversion warnings when...
[xonotic/darkplaces.git] / model_brush.c
index 2c577105f03366f44b7394e414e6ac32b46f27a6..440c412922382a0b44b476e271b8fc6cd0a36db2 100644 (file)
@@ -21,35 +21,79 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "quakedef.h"
 #include "image.h"
 #include "r_shadow.h"
-#include "winding.h"
+#include "polygon.h"
 #include "curves.h"
-
-// note: model_shared.c sets up r_notexture, and r_surf_notexture
-
-qbyte mod_q1bsp_novis[(MAX_MAP_LEAFS + 7)/ 8];
-
-//cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"};
-cvar_t halflifebsp = {0, "halflifebsp", "0"};
-cvar_t r_novis = {0, "r_novis", "0"};
-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 mod_q3bsp_curves_subdivide_level = {0, "mod_q3bsp_curves_subdivide_level", "2"};
-cvar_t mod_q3bsp_curves_collisions = {0, "mod_q3bsp_curves_collisions", "1"};
-cvar_t mod_q3bsp_optimizedtraceline = {0, "mod_q3bsp_optimizedtraceline", "1"};
+#include "wad.h"
+
+
+//cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128", "how large water polygons should be (smaller values produce more polygons which give better warping effects)"};
+cvar_t halflifebsp = {0, "halflifebsp", "0", "indicates the current map is hlbsp format (useful to know because of different bounding box sizes)"};
+cvar_t mcbsp = {0, "mcbsp", "0", "indicates the current map is mcbsp format (useful to know because of different bounding box sizes)"};
+cvar_t r_novis = {0, "r_novis", "0", "draws whole level, see also sv_cullentities_pvs 0"};
+cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1", "whether to use RGBA (32bit) or RGB (24bit) lightmaps"};
+cvar_t r_nosurftextures = {0, "r_nosurftextures", "0", "pretends there was no texture lump found in the q1bsp/hlbsp loading (useful for debugging this rare case)"};
+cvar_t r_subdivisions_tolerance = {0, "r_subdivisions_tolerance", "4", "maximum error tolerance on curve subdivision for rendering purposes (in other words, the curves will be given as many polygons as necessary to represent curves at this quality)"};
+cvar_t r_subdivisions_mintess = {0, "r_subdivisions_mintess", "1", "minimum number of subdivisions (values above 1 will smooth curves that don't need it)"};
+cvar_t r_subdivisions_maxtess = {0, "r_subdivisions_maxtess", "1024", "maximum number of subdivisions (prevents curves beyond a certain detail level, limits smoothing)"};
+cvar_t r_subdivisions_maxvertices = {0, "r_subdivisions_maxvertices", "65536", "maximum vertices allowed per subdivided curve"};
+cvar_t r_subdivisions_collision_tolerance = {0, "r_subdivisions_collision_tolerance", "15", "maximum error tolerance on curve subdivision for collision purposes (usually a larger error tolerance than for rendering)"};
+cvar_t r_subdivisions_collision_mintess = {0, "r_subdivisions_collision_mintess", "1", "minimum number of subdivisions (values above 1 will smooth curves that don't need it)"};
+cvar_t r_subdivisions_collision_maxtess = {0, "r_subdivisions_collision_maxtess", "1024", "maximum number of subdivisions (prevents curves beyond a certain detail level, limits smoothing)"};
+cvar_t r_subdivisions_collision_maxvertices = {0, "r_subdivisions_collision_maxvertices", "4225", "maximum vertices allowed per subdivided curve"};
+cvar_t mod_q3bsp_curves_collisions = {0, "mod_q3bsp_curves_collisions", "1", "enables collisions with curves (SLOW)"};
+cvar_t mod_q3bsp_optimizedtraceline = {0, "mod_q3bsp_optimizedtraceline", "1", "whether to use optimized traceline code for line traces (as opposed to tracebox code)"};
+cvar_t mod_q3bsp_debugtracebrush = {0, "mod_q3bsp_debugtracebrush", "0", "selects different tracebrush bsp recursion algorithms (for debugging purposes only)"};
+
+static texture_t mod_q1bsp_texture_solid;
+static texture_t mod_q1bsp_texture_sky;
+static texture_t mod_q1bsp_texture_lava;
+static texture_t mod_q1bsp_texture_slime;
+static texture_t mod_q1bsp_texture_water;
 
 void Mod_BrushInit(void)
 {
 //     Cvar_RegisterVariable(&r_subdivide_size);
        Cvar_RegisterVariable(&halflifebsp);
+       Cvar_RegisterVariable(&mcbsp);
        Cvar_RegisterVariable(&r_novis);
-       Cvar_RegisterVariable(&r_miplightmaps);
        Cvar_RegisterVariable(&r_lightmaprgba);
        Cvar_RegisterVariable(&r_nosurftextures);
-       Cvar_RegisterVariable(&mod_q3bsp_curves_subdivide_level);
+       Cvar_RegisterVariable(&r_subdivisions_tolerance);
+       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_mintess);
+       Cvar_RegisterVariable(&r_subdivisions_collision_maxtess);
+       Cvar_RegisterVariable(&r_subdivisions_collision_maxvertices);
        Cvar_RegisterVariable(&mod_q3bsp_curves_collisions);
        Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline);
-       memset(mod_q1bsp_novis, 0xff, sizeof(mod_q1bsp_novis));
+       Cvar_RegisterVariable(&mod_q3bsp_debugtracebrush);
+
+       memset(&mod_q1bsp_texture_solid, 0, sizeof(mod_q1bsp_texture_solid));
+       strlcpy(mod_q1bsp_texture_solid.name, "solid" , sizeof(mod_q1bsp_texture_solid.name));
+       mod_q1bsp_texture_solid.surfaceflags = 0;
+       mod_q1bsp_texture_solid.supercontents = SUPERCONTENTS_SOLID;
+
+       mod_q1bsp_texture_sky = mod_q1bsp_texture_solid;
+       strlcpy(mod_q1bsp_texture_sky.name, "sky", sizeof(mod_q1bsp_texture_sky.name));
+       mod_q1bsp_texture_sky.surfaceflags = Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT | Q3SURFACEFLAG_NOMARKS | Q3SURFACEFLAG_NODLIGHT | Q3SURFACEFLAG_NOLIGHTMAP;
+       mod_q1bsp_texture_sky.supercontents = SUPERCONTENTS_SKY | SUPERCONTENTS_NODROP;
+
+       mod_q1bsp_texture_lava = mod_q1bsp_texture_solid;
+       strlcpy(mod_q1bsp_texture_lava.name, "*lava", sizeof(mod_q1bsp_texture_lava.name));
+       mod_q1bsp_texture_lava.surfaceflags = Q3SURFACEFLAG_NOMARKS;
+       mod_q1bsp_texture_lava.supercontents = SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP;
+
+       mod_q1bsp_texture_slime = mod_q1bsp_texture_solid;
+       strlcpy(mod_q1bsp_texture_slime.name, "*slime", sizeof(mod_q1bsp_texture_slime.name));
+       mod_q1bsp_texture_slime.surfaceflags = Q3SURFACEFLAG_NOMARKS;
+       mod_q1bsp_texture_slime.supercontents = SUPERCONTENTS_SLIME;
+
+       mod_q1bsp_texture_water = mod_q1bsp_texture_solid;
+       strlcpy(mod_q1bsp_texture_water.name, "*water", sizeof(mod_q1bsp_texture_water.name));
+       mod_q1bsp_texture_water.surfaceflags = Q3SURFACEFLAG_NOMARKS;
+       mod_q1bsp_texture_water.supercontents = SUPERCONTENTS_WATER;
 }
 
 static mleaf_t *Mod_Q1BSP_PointInLeaf(model_t *model, const vec3_t p)
@@ -59,25 +103,23 @@ static mleaf_t *Mod_Q1BSP_PointInLeaf(model_t *model, const vec3_t p)
        if (model == NULL)
                return NULL;
 
-       Mod_CheckLoaded(model);
-
        // LordHavoc: modified to start at first clip node,
        // in other words: first node of the (sub)model
-       node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
-       while (node->contents == 0)
+       node = model->brush.data_nodes + model->brushq1.hulls[0].firstclipnode;
+       while (node->plane)
                node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
 
        return (mleaf_t *)node;
 }
 
-static void Mod_Q1BSP_AmbientSoundLevelsForPoint(model_t *model, const vec3_t p, qbyte *out, int outsize)
+static void Mod_Q1BSP_AmbientSoundLevelsForPoint(model_t *model, const vec3_t p, unsigned char *out, int outsize)
 {
        int i;
        mleaf_t *leaf;
        leaf = Mod_Q1BSP_PointInLeaf(model, p);
        if (leaf)
        {
-               i = min(outsize, (int)sizeof(leaf->ambient_sound_level));;
+               i = min(outsize, (int)sizeof(leaf->ambient_sound_level));
                if (i)
                {
                        memcpy(out, leaf->ambient_sound_level, i);
@@ -89,64 +131,282 @@ static void Mod_Q1BSP_AmbientSoundLevelsForPoint(model_t *model, const vec3_t p,
                memset(out, 0, outsize);
 }
 
-
-static int Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(const model_t *model, const mnode_t *node, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
+static int Mod_Q1BSP_FindBoxClusters(model_t *model, const vec3_t mins, const vec3_t maxs, int maxclusters, int *clusterlist)
 {
-       int leafnum;
-loc0:
-       if (node->contents < 0)
+       int numclusters = 0;
+       int nodestackindex = 0;
+       mnode_t *node, *nodestack[1024];
+       if (!model->brush.num_pvsclusters)
+               return -1;
+       node = model->brush.data_nodes;
+       for (;;)
        {
-               // leaf
-               if (node->contents == CONTENTS_SOLID)
-                       return false;
-               leafnum = (mleaf_t *)node - model->brushq1.leafs - 1;
-               return pvs[leafnum >> 3] & (1 << (leafnum & 7));
+#if 1
+               if (node->plane)
+               {
+                       // node - recurse down the BSP tree
+                       int sides = BoxOnPlaneSide(mins, maxs, node->plane);
+                       if (sides < 3)
+                       {
+                               if (sides == 0)
+                                       return -1; // ERROR: NAN bounding box!
+                               // box is on one side of plane, take that path
+                               node = node->children[sides-1];
+                       }
+                       else
+                       {
+                               // box crosses plane, take one path and remember the other
+                               if (nodestackindex < 1024)
+                                       nodestack[nodestackindex++] = node->children[0];
+                               node = node->children[1];
+                       }
+                       continue;
+               }
+               else
+               {
+                       // leaf - add clusterindex to list
+                       if (numclusters < maxclusters)
+                               clusterlist[numclusters] = ((mleaf_t *)node)->clusterindex;
+                       numclusters++;
+               }
+#else
+               if (BoxesOverlap(mins, maxs, node->mins, node->maxs))
+               {
+                       if (node->plane)
+                       {
+                               if (nodestackindex < 1024)
+                                       nodestack[nodestackindex++] = node->children[0];
+                               node = node->children[1];
+                               continue;
+                       }
+                       else
+                       {
+                               // leaf - add clusterindex to list
+                               if (numclusters < maxclusters)
+                                       clusterlist[numclusters] = ((mleaf_t *)node)->clusterindex;
+                               numclusters++;
+                       }
+               }
+#endif
+               // try another path we didn't take earlier
+               if (nodestackindex == 0)
+                       break;
+               node = nodestack[--nodestackindex];
        }
+       // return number of clusters found (even if more than the maxclusters)
+       return numclusters;
+}
 
-       // node - recurse down the BSP tree
-       switch (BoxOnPlaneSide(mins, maxs, node->plane))
+static int Mod_Q1BSP_BoxTouchingPVS(model_t *model, const unsigned char *pvs, const vec3_t mins, const vec3_t maxs)
+{
+       int nodestackindex = 0;
+       mnode_t *node, *nodestack[1024];
+       if (!model->brush.num_pvsclusters)
+               return true;
+       node = model->brush.data_nodes;
+       for (;;)
        {
-       case 1: // front
-               node = node->children[0];
-               goto loc0;
-       case 2: // back
-               node = node->children[1];
-               goto loc0;
-       default: // crossing
-               if (node->children[0]->contents != CONTENTS_SOLID)
-                       if (Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
+#if 1
+               if (node->plane)
+               {
+                       // node - recurse down the BSP tree
+                       int sides = BoxOnPlaneSide(mins, maxs, node->plane);
+                       if (sides < 3)
+                       {
+                               if (sides == 0)
+                                       return -1; // ERROR: NAN bounding box!
+                               // box is on one side of plane, take that path
+                               node = node->children[sides-1];
+                       }
+                       else
+                       {
+                               // box crosses plane, take one path and remember the other
+                               if (nodestackindex < 1024)
+                                       nodestack[nodestackindex++] = node->children[0];
+                               node = node->children[1];
+                       }
+                       continue;
+               }
+               else
+               {
+                       // leaf - check cluster bit
+                       int clusterindex = ((mleaf_t *)node)->clusterindex;
+                       if (CHECKPVSBIT(pvs, clusterindex))
+                       {
+                               // it is visible, return immediately with the news
                                return true;
-               node = node->children[1];
-               goto loc0;
+                       }
+               }
+#else
+               if (BoxesOverlap(mins, maxs, node->mins, node->maxs))
+               {
+                       if (node->plane)
+                       {
+                               if (nodestackindex < 1024)
+                                       nodestack[nodestackindex++] = node->children[0];
+                               node = node->children[1];
+                               continue;
+                       }
+                       else
+                       {
+                               // leaf - check cluster bit
+                               int clusterindex = ((mleaf_t *)node)->clusterindex;
+                               if (CHECKPVSBIT(pvs, clusterindex))
+                               {
+                                       // it is visible, return immediately with the news
+                                       return true;
+                               }
+                       }
+               }
+#endif
+               // nothing to see here, try another path we didn't take earlier
+               if (nodestackindex == 0)
+                       break;
+               node = nodestack[--nodestackindex];
        }
-       // never reached
+       // it is not visible
        return false;
 }
 
-int Mod_Q1BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
+static int Mod_Q1BSP_BoxTouchingLeafPVS(model_t *model, const unsigned char *pvs, const vec3_t mins, const vec3_t maxs)
 {
-       return Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode, pvs, mins, maxs);
+       int nodestackindex = 0;
+       mnode_t *node, *nodestack[1024];
+       if (!model->brush.num_leafs)
+               return true;
+       node = model->brush.data_nodes;
+       for (;;)
+       {
+#if 1
+               if (node->plane)
+               {
+                       // node - recurse down the BSP tree
+                       int sides = BoxOnPlaneSide(mins, maxs, node->plane);
+                       if (sides < 3)
+                       {
+                               if (sides == 0)
+                                       return -1; // ERROR: NAN bounding box!
+                               // box is on one side of plane, take that path
+                               node = node->children[sides-1];
+                       }
+                       else
+                       {
+                               // box crosses plane, take one path and remember the other
+                               if (nodestackindex < 1024)
+                                       nodestack[nodestackindex++] = node->children[0];
+                               node = node->children[1];
+                       }
+                       continue;
+               }
+               else
+               {
+                       // leaf - check cluster bit
+                       int clusterindex = ((mleaf_t *)node) - model->brush.data_leafs;
+                       if (CHECKPVSBIT(pvs, clusterindex))
+                       {
+                               // it is visible, return immediately with the news
+                               return true;
+                       }
+               }
+#else
+               if (BoxesOverlap(mins, maxs, node->mins, node->maxs))
+               {
+                       if (node->plane)
+                       {
+                               if (nodestackindex < 1024)
+                                       nodestack[nodestackindex++] = node->children[0];
+                               node = node->children[1];
+                               continue;
+                       }
+                       else
+                       {
+                               // leaf - check cluster bit
+                               int clusterindex = ((mleaf_t *)node) - model->brush.data_leafs;
+                               if (CHECKPVSBIT(pvs, clusterindex))
+                               {
+                                       // it is visible, return immediately with the news
+                                       return true;
+                               }
+                       }
+               }
+#endif
+               // nothing to see here, try another path we didn't take earlier
+               if (nodestackindex == 0)
+                       break;
+               node = nodestack[--nodestackindex];
+       }
+       // it is not visible
+       return false;
 }
 
-/*
-static int Mod_Q1BSP_PointContents(model_t *model, const vec3_t p)
+static int Mod_Q1BSP_BoxTouchingVisibleLeafs(model_t *model, const unsigned char *visibleleafs, const vec3_t mins, const vec3_t maxs)
 {
-       mnode_t *node;
-
-       if (model == NULL)
-               return CONTENTS_EMPTY;
-
-       Mod_CheckLoaded(model);
-
-       // LordHavoc: modified to start at first clip node,
-       // in other words: first node of the (sub)model
-       node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
-       while (node->contents == 0)
-               node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
-
-       return ((mleaf_t *)node)->contents;
+       int nodestackindex = 0;
+       mnode_t *node, *nodestack[1024];
+       if (!model->brush.num_leafs)
+               return true;
+       node = model->brush.data_nodes;
+       for (;;)
+       {
+#if 1
+               if (node->plane)
+               {
+                       // node - recurse down the BSP tree
+                       int sides = BoxOnPlaneSide(mins, maxs, node->plane);
+                       if (sides < 3)
+                       {
+                               if (sides == 0)
+                                       return -1; // ERROR: NAN bounding box!
+                               // box is on one side of plane, take that path
+                               node = node->children[sides-1];
+                       }
+                       else
+                       {
+                               // box crosses plane, take one path and remember the other
+                               if (nodestackindex < 1024)
+                                       nodestack[nodestackindex++] = node->children[0];
+                               node = node->children[1];
+                       }
+                       continue;
+               }
+               else
+               {
+                       // leaf - check if it is visible
+                       if (visibleleafs[(mleaf_t *)node - model->brush.data_leafs])
+                       {
+                               // it is visible, return immediately with the news
+                               return true;
+                       }
+               }
+#else
+               if (BoxesOverlap(mins, maxs, node->mins, node->maxs))
+               {
+                       if (node->plane)
+                       {
+                               if (nodestackindex < 1024)
+                                       nodestack[nodestackindex++] = node->children[0];
+                               node = node->children[1];
+                               continue;
+                       }
+                       else
+                       {
+                               // leaf - check if it is visible
+                               if (visibleleafs[(mleaf_t *)node - model->brush.data_leafs])
+                               {
+                                       // it is visible, return immediately with the news
+                                       return true;
+                               }
+                       }
+               }
+#endif
+               // nothing to see here, try another path we didn't take earlier
+               if (nodestackindex == 0)
+                       break;
+               node = nodestack[--nodestackindex];
+       }
+       // it is not visible
+       return false;
 }
-*/
 
 typedef struct findnonsolidlocationinfo_s
 {
@@ -158,125 +418,93 @@ typedef struct findnonsolidlocationinfo_s
 }
 findnonsolidlocationinfo_t;
 
-#if 0
-extern cvar_t samelevel;
-#endif
 static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
 {
-       int i, surfnum, k, *tri, *mark;
+       int i, surfacenum, k, *tri, *mark;
        float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3];
-#if 0
-       float surfnormal[3];
-#endif
-       msurface_t *surf;
-       surfmesh_t *mesh;
-       for (surfnum = 0, mark = leaf->firstmarksurface;surfnum < leaf->nummarksurfaces;surfnum++, mark++)
+       msurface_t *surface;
+       for (surfacenum = 0, mark = leaf->firstleafsurface;surfacenum < leaf->numleafsurfaces;surfacenum++, mark++)
        {
-               surf = info->model->brushq1.surfaces + *mark;
-               if (surf->flags & SURF_SOLIDCLIP)
+               surface = info->model->data_surfaces + *mark;
+               if (surface->texture->supercontents & SUPERCONTENTS_SOLID)
                {
-#if 0
-                       VectorCopy(surf->plane->normal, surfnormal);
-                       if (surf->flags & SURF_PLANEBACK)
-                               VectorNegate(surfnormal, surfnormal);
-#endif
-                       for (mesh = surf->mesh;mesh;mesh = mesh->chain)
+                       for (k = 0;k < surface->num_triangles;k++)
                        {
-                               for (k = 0;k < mesh->numtriangles;k++)
+                               tri = (info->model->surfmesh.data_element3i + 3 * surface->num_firsttriangle) + k * 3;
+                               VectorCopy((info->model->surfmesh.data_vertex3f + tri[0] * 3), vert[0]);
+                               VectorCopy((info->model->surfmesh.data_vertex3f + tri[1] * 3), vert[1]);
+                               VectorCopy((info->model->surfmesh.data_vertex3f + tri[2] * 3), vert[2]);
+                               VectorSubtract(vert[1], vert[0], edge[0]);
+                               VectorSubtract(vert[2], vert[1], edge[1]);
+                               CrossProduct(edge[1], edge[0], facenormal);
+                               if (facenormal[0] || facenormal[1] || facenormal[2])
                                {
-                                       tri = mesh->element3i + k * 3;
-                                       VectorCopy((mesh->vertex3f + tri[0] * 3), vert[0]);
-                                       VectorCopy((mesh->vertex3f + tri[1] * 3), vert[1]);
-                                       VectorCopy((mesh->vertex3f + tri[2] * 3), vert[2]);
-                                       VectorSubtract(vert[1], vert[0], edge[0]);
-                                       VectorSubtract(vert[2], vert[1], edge[1]);
-                                       CrossProduct(edge[1], edge[0], facenormal);
-                                       if (facenormal[0] || facenormal[1] || facenormal[2])
+                                       VectorNormalize(facenormal);
+                                       f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
+                                       if (f <= info->bestdist && f >= -info->bestdist)
                                        {
-                                               VectorNormalize(facenormal);
-#if 0
-                                               if (VectorDistance(facenormal, surfnormal) > 0.01f)
-                                                       Con_Printf("a2! %f %f %f != %f %f %f\n", facenormal[0], facenormal[1], facenormal[2], surfnormal[0], surfnormal[1], surfnormal[2]);
-#endif
-                                               f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
-                                               if (f <= info->bestdist && f >= -info->bestdist)
+                                               VectorSubtract(vert[0], vert[2], edge[2]);
+                                               VectorNormalize(edge[0]);
+                                               VectorNormalize(edge[1]);
+                                               VectorNormalize(edge[2]);
+                                               CrossProduct(facenormal, edge[0], edgenormal[0]);
+                                               CrossProduct(facenormal, edge[1], edgenormal[1]);
+                                               CrossProduct(facenormal, edge[2], edgenormal[2]);
+                                               // face distance
+                                               if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0])
+                                                && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1])
+                                                && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2]))
                                                {
-                                                       VectorSubtract(vert[0], vert[2], edge[2]);
-                                                       VectorNormalize(edge[0]);
-                                                       VectorNormalize(edge[1]);
-                                                       VectorNormalize(edge[2]);
-                                                       CrossProduct(facenormal, edge[0], edgenormal[0]);
-                                                       CrossProduct(facenormal, edge[1], edgenormal[1]);
-                                                       CrossProduct(facenormal, edge[2], edgenormal[2]);
-#if 0
-                                                       if (samelevel.integer & 1)
-                                                               VectorNegate(edgenormal[0], edgenormal[0]);
-                                                       if (samelevel.integer & 2)
-                                                               VectorNegate(edgenormal[1], edgenormal[1]);
-                                                       if (samelevel.integer & 4)
-                                                               VectorNegate(edgenormal[2], edgenormal[2]);
+                                                       // we got lucky, the center is within the face
+                                                       dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
+                                                       if (dist < 0)
+                                                       {
+                                                               dist = -dist;
+                                                               if (info->bestdist > dist)
+                                                               {
+                                                                       info->bestdist = dist;
+                                                                       VectorScale(facenormal, (info->radius - -dist), info->nudge);
+                                                               }
+                                                       }
+                                                       else
+                                                       {
+                                                               if (info->bestdist > dist)
+                                                               {
+                                                                       info->bestdist = dist;
+                                                                       VectorScale(facenormal, (info->radius - dist), info->nudge);
+                                                               }
+                                                       }
+                                               }
+                                               else
+                                               {
+                                                       // check which edge or vertex the center is nearest
                                                        for (i = 0;i < 3;i++)
-                                                               if (DotProduct(vert[0], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
-                                                                || DotProduct(vert[1], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
-                                                                || DotProduct(vert[2], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f)
-                                                                       Con_Printf("a! %i : %f %f %f (%f %f %f)\n", i, edgenormal[i][0], edgenormal[i][1], edgenormal[i][2], facenormal[0], facenormal[1], facenormal[2]);
-#endif
-                                                       // face distance
-                                                       if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0])
-                                                        && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1])
-                                                        && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2]))
                                                        {
-                                                               // we got lucky, the center is within the face
-                                                               dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
-                                                               if (dist < 0)
+                                                               f = DotProduct(info->center, edge[i]);
+                                                               if (f >= DotProduct(vert[0], edge[i])
+                                                                && f <= DotProduct(vert[1], edge[i]))
                                                                {
-                                                                       dist = -dist;
+                                                                       // on edge
+                                                                       VectorMA(info->center, -f, edge[i], point);
+                                                                       dist = sqrt(DotProduct(point, point));
                                                                        if (info->bestdist > dist)
                                                                        {
                                                                                info->bestdist = dist;
-                                                                               VectorScale(facenormal, (info->radius - -dist), info->nudge);
+                                                                               VectorScale(point, (info->radius / dist), info->nudge);
                                                                        }
+                                                                       // skip both vertex checks
+                                                                       // (both are further away than this edge)
+                                                                       i++;
                                                                }
                                                                else
                                                                {
+                                                                       // not on edge, check first vertex of edge
+                                                                       VectorSubtract(info->center, vert[i], point);
+                                                                       dist = sqrt(DotProduct(point, point));
                                                                        if (info->bestdist > dist)
                                                                        {
                                                                                info->bestdist = dist;
-                                                                               VectorScale(facenormal, (info->radius - dist), info->nudge);
-                                                                       }
-                                                               }
-                                                       }
-                                                       else
-                                                       {
-                                                               // check which edge or vertex the center is nearest
-                                                               for (i = 0;i < 3;i++)
-                                                               {
-                                                                       f = DotProduct(info->center, edge[i]);
-                                                                       if (f >= DotProduct(vert[0], edge[i])
-                                                                        && f <= DotProduct(vert[1], edge[i]))
-                                                                       {
-                                                                               // on edge
-                                                                               VectorMA(info->center, -f, edge[i], point);
-                                                                               dist = sqrt(DotProduct(point, point));
-                                                                               if (info->bestdist > dist)
-                                                                               {
-                                                                                       info->bestdist = dist;
-                                                                                       VectorScale(point, (info->radius / dist), info->nudge);
-                                                                               }
-                                                                               // skip both vertex checks
-                                                                               // (both are further away than this edge)
-                                                                               i++;
-                                                                       }
-                                                                       else
-                                                                       {
-                                                                               // not on edge, check first vertex of edge
-                                                                               VectorSubtract(info->center, vert[i], point);
-                                                                               dist = sqrt(DotProduct(point, point));
-                                                                               if (info->bestdist > dist)
-                                                                               {
-                                                                                       info->bestdist = dist;
-                                                                                       VectorScale(point, (info->radius / dist), info->nudge);
-                                                                               }
+                                                                               VectorScale(point, (info->radius / dist), info->nudge);
                                                                        }
                                                                }
                                                        }
@@ -290,12 +518,7 @@ static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *in
 
 static void Mod_Q1BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node)
 {
-       if (node->contents)
-       {
-               if (((mleaf_t *)node)->nummarksurfaces)
-                       Mod_Q1BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node);
-       }
-       else
+       if (node->plane)
        {
                float f = PlaneDiff(info->center, node->plane);
                if (f >= -info->bestdist)
@@ -303,6 +526,11 @@ static void Mod_Q1BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, m
                if (f <= info->bestdist)
                        Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[1]);
        }
+       else
+       {
+               if (((mleaf_t *)node)->numleafsurfaces)
+                       Mod_Q1BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node);
+       }
 }
 
 static void Mod_Q1BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, float radius)
@@ -322,7 +550,7 @@ static void Mod_Q1BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3
        {
                VectorClear(info.nudge);
                info.bestdist = radius;
-               Mod_Q1BSP_FindNonSolidLocation_r(&info, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode);
+               Mod_Q1BSP_FindNonSolidLocation_r(&info, model->brush.data_nodes + model->brushq1.hulls[0].firstclipnode);
                VectorAdd(info.center, info.nudge, info.center);
        }
        while (info.bestdist < radius && ++i < 10);
@@ -342,9 +570,9 @@ int Mod_Q1BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents
                case CONTENTS_SLIME:
                        return SUPERCONTENTS_SLIME;
                case CONTENTS_LAVA:
-                       return SUPERCONTENTS_LAVA;
+                       return SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP;
                case CONTENTS_SKY:
-                       return SUPERCONTENTS_SKY;
+                       return SUPERCONTENTS_SKY | SUPERCONTENTS_NODROP;
        }
        return 0;
 }
@@ -364,7 +592,7 @@ int Mod_Q1BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
        return CONTENTS_EMPTY;
 }
 
-typedef struct
+typedef struct RecursiveHullCheckTraceInfo_s
 {
        // the hull we're tracing through
        const hull_t *hull;
@@ -415,16 +643,34 @@ loc0:
                        t->trace->inwater = true;
                if (num == 0)
                        t->trace->inopen = true;
+               if (num & SUPERCONTENTS_SOLID)
+                       t->trace->hittexture = &mod_q1bsp_texture_solid;
+               else if (num & SUPERCONTENTS_SKY)
+                       t->trace->hittexture = &mod_q1bsp_texture_sky;
+               else if (num & SUPERCONTENTS_LAVA)
+                       t->trace->hittexture = &mod_q1bsp_texture_lava;
+               else if (num & SUPERCONTENTS_SLIME)
+                       t->trace->hittexture = &mod_q1bsp_texture_slime;
+               else
+                       t->trace->hittexture = &mod_q1bsp_texture_water;
+               t->trace->hitq3surfaceflags = t->trace->hittexture->surfaceflags;
+               t->trace->hitsupercontents = num;
                if (num & t->trace->hitsupercontentsmask)
                {
                        // if the first leaf is solid, set startsolid
                        if (t->trace->allsolid)
                                t->trace->startsolid = true;
+#if COLLISIONPARANOID >= 3
+                       Con_Print("S");
+#endif
                        return HULLCHECKSTATE_SOLID;
                }
                else
                {
                        t->trace->allsolid = false;
+#if COLLISIONPARANOID >= 3
+                       Con_Print("E");
+#endif
                        return HULLCHECKSTATE_EMPTY;
                }
        }
@@ -448,6 +694,9 @@ loc0:
        {
                if (t2 < 0)
                {
+#if COLLISIONPARANOID >= 3
+                       Con_Print("<");
+#endif
                        num = node->children[1];
                        goto loc0;
                }
@@ -457,6 +706,9 @@ loc0:
        {
                if (t2 >= 0)
                {
+#if COLLISIONPARANOID >= 3
+                       Con_Print(">");
+#endif
                        num = node->children[0];
                        goto loc0;
                }
@@ -465,6 +717,9 @@ loc0:
 
        // the line intersects, find intersection point
        // LordHavoc: this uses the original trace for maximum accuracy
+#if COLLISIONPARANOID >= 3
+       Con_Print("M");
+#endif
        if (plane->type < 3)
        {
                t1 = t->start[plane->type] - plane->dist;
@@ -503,17 +758,47 @@ loc0:
                VectorCopy (plane->normal, t->trace->plane.normal);
        }
 
-       // bias away from surface a bit
-       t1 = DotProduct(t->trace->plane.normal, t->start) - (t->trace->plane.dist + DIST_EPSILON);
-       t2 = DotProduct(t->trace->plane.normal, t->end) - (t->trace->plane.dist + DIST_EPSILON);
-
+       // calculate the true fraction
+       t1 = DotProduct(t->trace->plane.normal, t->start) - t->trace->plane.dist;
+       t2 = DotProduct(t->trace->plane.normal, t->end) - t->trace->plane.dist;
        midf = t1 / (t1 - t2);
-       t->trace->fraction = bound(0.0f, midf, 1.0);
+       t->trace->realfraction = bound(0, midf, 1);
+
+       // calculate the return fraction which is nudged off the surface a bit
+       midf = (t1 - DIST_EPSILON) / (t1 - t2);
+       t->trace->fraction = bound(0, midf, 1);
 
+#if COLLISIONPARANOID >= 3
+       Con_Print("D");
+#endif
        return HULLCHECKSTATE_DONE;
 }
 
-static void Mod_Q1BSP_TraceBox(struct model_s *model, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask)
+#if COLLISIONPARANOID < 2
+static int Mod_Q1BSP_RecursiveHullCheckPoint(RecursiveHullCheckTraceInfo_t *t, int num)
+{
+       while (num >= 0)
+               num = t->hull->clipnodes[num].children[(t->hull->planes[t->hull->clipnodes[num].planenum].type < 3 ? t->start[t->hull->planes[t->hull->clipnodes[num].planenum].type] : DotProduct(t->hull->planes[t->hull->clipnodes[num].planenum].normal, t->start)) < t->hull->planes[t->hull->clipnodes[num].planenum].dist];
+       num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num);
+       t->trace->startsupercontents |= num;
+       if (num & SUPERCONTENTS_LIQUIDSMASK)
+               t->trace->inwater = true;
+       if (num == 0)
+               t->trace->inopen = true;
+       if (num & t->trace->hitsupercontentsmask)
+       {
+               t->trace->allsolid = t->trace->startsolid = true;
+               return HULLCHECKSTATE_SOLID;
+       }
+       else
+       {
+               t->trace->allsolid = t->trace->startsolid = false;
+               return HULLCHECKSTATE_EMPTY;
+       }
+}
+#endif
+
+static void Mod_Q1BSP_TraceBox(struct model_s *model, int frame, trace_t *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask)
 {
        // this function currently only supports same size start and end
        double boxsize[3];
@@ -524,13 +809,23 @@ static void Mod_Q1BSP_TraceBox(struct model_s *model, trace_t *trace, const vec3
        rhc.trace = trace;
        rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
        rhc.trace->fraction = 1;
+       rhc.trace->realfraction = 1;
        rhc.trace->allsolid = true;
-       VectorSubtract(boxstartmaxs, boxstartmins, boxsize);
+       VectorSubtract(boxmaxs, boxmins, boxsize);
        if (boxsize[0] < 3)
                rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
+       else if (model->brush.ismcbsp)
+       {
+               if (boxsize[2] < 48) // pick the nearest of 40 or 56
+                       rhc.hull = &model->brushq1.hulls[2]; // 16x16x40
+               else
+                       rhc.hull = &model->brushq1.hulls[1]; // 16x16x56
+       }
        else if (model->brush.ishlbsp)
        {
-               if (boxsize[0] <= 32)
+               // LordHavoc: this has to have a minor tolerance (the .1) because of
+               // minor float precision errors from the box being transformed around
+               if (boxsize[0] < 32.1)
                {
                        if (boxsize[2] < 54) // pick the nearest of 36 or 72
                                rhc.hull = &model->brushq1.hulls[3]; // 32x32x36
@@ -542,25 +837,146 @@ static void Mod_Q1BSP_TraceBox(struct model_s *model, trace_t *trace, const vec3
        }
        else
        {
-               if (boxsize[0] <= 32)
+               // LordHavoc: this has to have a minor tolerance (the .1) because of
+               // minor float precision errors from the box being transformed around
+               if (boxsize[0] < 32.1)
                        rhc.hull = &model->brushq1.hulls[1]; // 32x32x56
                else
                        rhc.hull = &model->brushq1.hulls[2]; // 64x64x88
        }
-       VectorSubtract(boxstartmins, rhc.hull->clip_mins, rhc.start);
-       VectorSubtract(boxendmins, rhc.hull->clip_mins, rhc.end);
+       VectorMAMAM(1, start, 1, boxmins, -1, rhc.hull->clip_mins, rhc.start);
+       VectorMAMAM(1, end, 1, boxmins, -1, rhc.hull->clip_mins, rhc.end);
+       VectorSubtract(rhc.end, rhc.start, rhc.dist);
+#if COLLISIONPARANOID >= 2
+       Con_Printf("t(%f %f %f,%f %f %f,%i %f %f %f)", rhc.start[0], rhc.start[1], rhc.start[2], rhc.end[0], rhc.end[1], rhc.end[2], rhc.hull - model->brushq1.hulls, rhc.hull->clip_mins[0], rhc.hull->clip_mins[1], rhc.hull->clip_mins[2]);
+       Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
+       Con_Print("\n");
+#else
+       if (VectorLength2(rhc.dist))
+               Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
+       else
+               Mod_Q1BSP_RecursiveHullCheckPoint(&rhc, rhc.hull->firstclipnode);
+#endif
+       if (trace->fraction == 1)
+       {
+               trace->hitsupercontents = 0;
+               trace->hitq3surfaceflags = 0;
+               trace->hittexture = NULL;
+       }
+}
+
+void Collision_ClipTrace_Box(trace_t *trace, const vec3_t cmins, const vec3_t cmaxs, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int hitsupercontentsmask, int boxsupercontents, int boxq3surfaceflags, texture_t *boxtexture)
+{
+#if 1
+       colbrushf_t cbox;
+       colplanef_t cbox_planes[6];
+       cbox.supercontents = boxsupercontents;
+       cbox.numplanes = 6;
+       cbox.numpoints = 0;
+       cbox.numtriangles = 0;
+       cbox.planes = cbox_planes;
+       cbox.points = NULL;
+       cbox.elements = NULL;
+       cbox.markframe = 0;
+       cbox.mins[0] = 0;
+       cbox.mins[1] = 0;
+       cbox.mins[2] = 0;
+       cbox.maxs[0] = 0;
+       cbox.maxs[1] = 0;
+       cbox.maxs[2] = 0;
+       cbox_planes[0].normal[0] =  1;cbox_planes[0].normal[1] =  0;cbox_planes[0].normal[2] =  0;cbox_planes[0].dist = cmaxs[0] - mins[0];
+       cbox_planes[1].normal[0] = -1;cbox_planes[1].normal[1] =  0;cbox_planes[1].normal[2] =  0;cbox_planes[1].dist = maxs[0] - cmins[0];
+       cbox_planes[2].normal[0] =  0;cbox_planes[2].normal[1] =  1;cbox_planes[2].normal[2] =  0;cbox_planes[2].dist = cmaxs[1] - mins[1];
+       cbox_planes[3].normal[0] =  0;cbox_planes[3].normal[1] = -1;cbox_planes[3].normal[2] =  0;cbox_planes[3].dist = maxs[1] - cmins[1];
+       cbox_planes[4].normal[0] =  0;cbox_planes[4].normal[1] =  0;cbox_planes[4].normal[2] =  1;cbox_planes[4].dist = cmaxs[2] - mins[2];
+       cbox_planes[5].normal[0] =  0;cbox_planes[5].normal[1] =  0;cbox_planes[5].normal[2] = -1;cbox_planes[5].dist = maxs[2] - cmins[2];
+       cbox_planes[0].supercontents = boxsupercontents;cbox_planes[0].q3surfaceflags = boxq3surfaceflags;cbox_planes[0].texture = boxtexture;
+       cbox_planes[1].supercontents = boxsupercontents;cbox_planes[1].q3surfaceflags = boxq3surfaceflags;cbox_planes[1].texture = boxtexture;
+       cbox_planes[2].supercontents = boxsupercontents;cbox_planes[2].q3surfaceflags = boxq3surfaceflags;cbox_planes[2].texture = boxtexture;
+       cbox_planes[3].supercontents = boxsupercontents;cbox_planes[3].q3surfaceflags = boxq3surfaceflags;cbox_planes[3].texture = boxtexture;
+       cbox_planes[4].supercontents = boxsupercontents;cbox_planes[4].q3surfaceflags = boxq3surfaceflags;cbox_planes[4].texture = boxtexture;
+       cbox_planes[5].supercontents = boxsupercontents;cbox_planes[5].q3surfaceflags = boxq3surfaceflags;cbox_planes[5].texture = boxtexture;
+       memset(trace, 0, sizeof(trace_t));
+       trace->hitsupercontentsmask = hitsupercontentsmask;
+       trace->fraction = 1;
+       trace->realfraction = 1;
+       Collision_TraceLineBrushFloat(trace, start, end, &cbox, &cbox);
+#else
+       RecursiveHullCheckTraceInfo_t rhc;
+       static hull_t box_hull;
+       static dclipnode_t box_clipnodes[6];
+       static mplane_t box_planes[6];
+       // fill in a default trace
+       memset(&rhc, 0, sizeof(rhc));
+       memset(trace, 0, sizeof(trace_t));
+       //To keep everything totally uniform, bounding boxes are turned into small
+       //BSP trees instead of being compared directly.
+       // create a temp hull from bounding box sizes
+       box_planes[0].dist = cmaxs[0] - mins[0];
+       box_planes[1].dist = cmins[0] - maxs[0];
+       box_planes[2].dist = cmaxs[1] - mins[1];
+       box_planes[3].dist = cmins[1] - maxs[1];
+       box_planes[4].dist = cmaxs[2] - mins[2];
+       box_planes[5].dist = cmins[2] - maxs[2];
+#if COLLISIONPARANOID >= 3
+       Con_Printf("box_planes %f:%f %f:%f %f:%f\ncbox %f %f %f:%f %f %f\nbox %f %f %f:%f %f %f\n", box_planes[0].dist, box_planes[1].dist, box_planes[2].dist, box_planes[3].dist, box_planes[4].dist, box_planes[5].dist, cmins[0], cmins[1], cmins[2], cmaxs[0], cmaxs[1], cmaxs[2], mins[0], mins[1], mins[2], maxs[0], maxs[1], maxs[2]);
+#endif
+
+       if (box_hull.clipnodes == NULL)
+       {
+               int i, side;
+
+               //Set up the planes and clipnodes so that the six floats of a bounding box
+               //can just be stored out and get a proper hull_t structure.
+
+               box_hull.clipnodes = box_clipnodes;
+               box_hull.planes = box_planes;
+               box_hull.firstclipnode = 0;
+               box_hull.lastclipnode = 5;
+
+               for (i = 0;i < 6;i++)
+               {
+                       box_clipnodes[i].planenum = i;
+
+                       side = i&1;
+
+                       box_clipnodes[i].children[side] = CONTENTS_EMPTY;
+                       if (i != 5)
+                               box_clipnodes[i].children[side^1] = i + 1;
+                       else
+                               box_clipnodes[i].children[side^1] = CONTENTS_SOLID;
+
+                       box_planes[i].type = i>>1;
+                       box_planes[i].normal[i>>1] = 1;
+               }
+       }
+
+       // trace a line through the generated clipping hull
+       //rhc.boxsupercontents = boxsupercontents;
+       rhc.hull = &box_hull;
+       rhc.trace = trace;
+       rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
+       rhc.trace->fraction = 1;
+       rhc.trace->realfraction = 1;
+       rhc.trace->allsolid = true;
+       VectorCopy(start, rhc.start);
+       VectorCopy(end, rhc.end);
        VectorSubtract(rhc.end, rhc.start, rhc.dist);
        Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
+       //VectorMA(rhc.start, rhc.trace->fraction, rhc.dist, rhc.trace->endpos);
+       if (rhc.trace->startsupercontents)
+               rhc.trace->startsupercontents = boxsupercontents;
+#endif
 }
 
-static int Mod_Q1BSP_LightPoint_RecursiveBSPNode(vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal, const mnode_t *node, float x, float y, float startz, float endz)
+static int Mod_Q1BSP_LightPoint_RecursiveBSPNode(model_t *model, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal, const mnode_t *node, float x, float y, float startz, float endz)
 {
-       int side, distz = endz - startz;
+       int side;
        float front, back;
-       float mid;
+       float mid, distz = endz - startz;
 
 loc0:
-       if (node->contents < 0)
+       if (!node->plane)
                return false;           // didn't hit anything
 
        switch (node->plane->type)
@@ -597,7 +1013,7 @@ loc0:
        }
 
        // go down front side
-       if (node->children[side]->contents >= 0 && Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, node->children[side], x, y, startz, mid))
+       if (node->children[side]->plane && Mod_Q1BSP_LightPoint_RecursiveBSPNode(model, ambientcolor, diffusecolor, diffusenormal, node->children[side], x, y, startz, mid))
                return true;    // hit something
        else
        {
@@ -605,38 +1021,31 @@ loc0:
                if (node->numsurfaces)
                {
                        int i, ds, dt;
-                       msurface_t *surf;
+                       msurface_t *surface;
 
-                       surf = cl.worldmodel->brushq1.surfaces + node->firstsurface;
-                       for (i = 0;i < node->numsurfaces;i++, surf++)
+                       surface = model->data_surfaces + node->firstsurface;
+                       for (i = 0;i < node->numsurfaces;i++, surface++)
                        {
-                               if (!(surf->flags & SURF_LIGHTMAP))
+                               if (!(surface->texture->basematerialflags & MATERIALFLAG_WALL) || !surface->lightmapinfo->samples)
                                        continue;       // no lightmaps
 
-                               ds = (int) (x * surf->texinfo->vecs[0][0] + y * surf->texinfo->vecs[0][1] + mid * surf->texinfo->vecs[0][2] + surf->texinfo->vecs[0][3]);
-                               dt = (int) (x * surf->texinfo->vecs[1][0] + y * surf->texinfo->vecs[1][1] + mid * surf->texinfo->vecs[1][2] + surf->texinfo->vecs[1][3]);
-
-                               if (ds < surf->texturemins[0] || dt < surf->texturemins[1])
-                                       continue;
-
-                               ds -= surf->texturemins[0];
-                               dt -= surf->texturemins[1];
-
-                               if (ds > surf->extents[0] || dt > surf->extents[1])
-                                       continue;
+                               ds = (int) (x * surface->lightmapinfo->texinfo->vecs[0][0] + y * surface->lightmapinfo->texinfo->vecs[0][1] + mid * surface->lightmapinfo->texinfo->vecs[0][2] + surface->lightmapinfo->texinfo->vecs[0][3]) - surface->lightmapinfo->texturemins[0];
+                               dt = (int) (x * surface->lightmapinfo->texinfo->vecs[1][0] + y * surface->lightmapinfo->texinfo->vecs[1][1] + mid * surface->lightmapinfo->texinfo->vecs[1][2] + surface->lightmapinfo->texinfo->vecs[1][3]) - surface->lightmapinfo->texturemins[1];
 
-                               if (surf->samples)
+                               if (ds >= 0 && ds < surface->lightmapinfo->extents[0] && dt >= 0 && dt < surface->lightmapinfo->extents[1])
                                {
-                                       qbyte *lightmap;
-                                       int maps, line3, size3, dsfrac = ds & 15, dtfrac = dt & 15, scale = 0, r00 = 0, g00 = 0, b00 = 0, r01 = 0, g01 = 0, b01 = 0, r10 = 0, g10 = 0, b10 = 0, r11 = 0, g11 = 0, b11 = 0;
-                                       line3 = ((surf->extents[0]>>4)+1)*3;
-                                       size3 = ((surf->extents[0]>>4)+1) * ((surf->extents[1]>>4)+1)*3; // LordHavoc: *3 for colored lighting
+                                       unsigned char *lightmap;
+                                       int lmwidth, lmheight, maps, line3, size3, dsfrac = ds & 15, dtfrac = dt & 15, scale = 0, r00 = 0, g00 = 0, b00 = 0, r01 = 0, g01 = 0, b01 = 0, r10 = 0, g10 = 0, b10 = 0, r11 = 0, g11 = 0, b11 = 0;
+                                       lmwidth = ((surface->lightmapinfo->extents[0]>>4)+1);
+                                       lmheight = ((surface->lightmapinfo->extents[1]>>4)+1);
+                                       line3 = lmwidth * 3; // LordHavoc: *3 for colored lighting
+                                       size3 = lmwidth * lmheight * 3; // LordHavoc: *3 for colored lighting
 
-                                       lightmap = surf->samples + ((dt>>4) * ((surf->extents[0]>>4)+1) + (ds>>4))*3; // LordHavoc: *3 for color
+                                       lightmap = surface->lightmapinfo->samples + ((dt>>4) * lmwidth + (ds>>4))*3; // LordHavoc: *3 for colored lighting
 
-                                       for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++)
+                                       for (maps = 0;maps < MAXLIGHTMAPS && surface->lightmapinfo->styles[maps] != 255;maps++)
                                        {
-                                               scale = d_lightstylevalue[surf->styles[maps]];
+                                               scale = r_refdef.lightstylevalue[surface->lightmapinfo->styles[maps]];
                                                r00 += lightmap[      0] * scale;g00 += lightmap[      1] * scale;b00 += lightmap[      2] * scale;
                                                r01 += lightmap[      3] * scale;g01 += lightmap[      4] * scale;b01 += lightmap[      5] * scale;
                                                r10 += lightmap[line3+0] * scale;g10 += lightmap[line3+1] * scale;b10 += lightmap[line3+2] * scale;
@@ -680,8 +1089,8 @@ middle sample (the one which was requested)
                                        ambientcolor[0] += (float) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00)) * (1.0f / 32768.0f);
                                        ambientcolor[1] += (float) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00)) * (1.0f / 32768.0f);
                                        ambientcolor[2] += (float) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00)) * (1.0f / 32768.0f);
+                                       return true; // success
                                }
-                               return true; // success
                        }
                }
 
@@ -695,17 +1104,19 @@ middle sample (the one which was requested)
 
 void Mod_Q1BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
 {
-       Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, cl.worldmodel->brushq1.nodes + cl.worldmodel->brushq1.hulls[0].firstclipnode, p[0], p[1], p[2], p[2] - 65536);
+       Mod_Q1BSP_LightPoint_RecursiveBSPNode(model, ambientcolor, diffusecolor, diffusenormal, model->brush.data_nodes + model->brushq1.hulls[0].firstclipnode, p[0], p[1], p[2], p[2] - 65536);
+       VectorSet(diffusenormal, 0, 0, -1);
 }
 
-static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *out, qbyte *outend)
+static void Mod_Q1BSP_DecompressVis(const unsigned char *in, const unsigned char *inend, unsigned char *out, unsigned char *outend)
 {
        int c;
+       unsigned char *outstart = out;
        while (out < outend)
        {
                if (in == inend)
                {
-                       Con_Printf("Mod_Q1BSP_DecompressVis: input underrun\n");
+                       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++;
@@ -713,11 +1124,16 @@ static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *
                        *out++ = c;
                else
                {
+                       if (in == inend)
+                       {
+                               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_Printf("Mod_Q1BSP_DecompressVis: output overrun\n");
+                                       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;
@@ -726,46 +1142,122 @@ static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *
        }
 }
 
-static void Mod_Q1BSP_LoadTextures(lump_t *l)
-{
-       int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
-       miptex_t *dmiptex;
-       texture_t *tx, *tx2, *anims[10], *altanims[10];
-       dmiptexlump_t *m;
-       qbyte *data, *mtdata;
-       char name[256];
+/*
+=============
+R_Q1BSP_LoadSplitSky
 
-       loadmodel->brushq1.textures = NULL;
+A sky texture is 256*128, with the right side being a masked overlay
+==============
+*/
+void R_Q1BSP_LoadSplitSky (unsigned char *src, int width, int height, int bytesperpixel)
+{
+       int i, j;
+       unsigned solidpixels[128*128], alphapixels[128*128];
 
-       // add two slots for notexture walls and notexture liquids
-       if (l->filelen)
-       {
-               m = (dmiptexlump_t *)(mod_base + l->fileofs);
-               m->nummiptex = LittleLong (m->nummiptex);
-               loadmodel->brushq1.numtextures = m->nummiptex + 2;
-       }
-       else
+       // if sky isn't the right size, just use it as a solid layer
+       if (width != 256 || height != 128)
        {
-               m = NULL;
-               loadmodel->brushq1.numtextures = 2;
+               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;
        }
 
-       loadmodel->brushq1.textures = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numtextures * sizeof(texture_t));
+       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, (unsigned char *) solidpixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
+       loadmodel->brush.alphaskytexture = R_LoadTexture2D(loadmodel->texturepool, "sky_alphatexture", 128, 128, (unsigned char *) 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;
+       miptex_t *dmiptex;
+       texture_t *tx, *tx2, *anims[10], *altanims[10];
+       dmiptexlump_t *m;
+       unsigned char *data, *mtdata;
+       char name[MAX_QPATH];
+
+       loadmodel->data_textures = NULL;
+
+       // add two slots for notexture walls and notexture liquids
+       if (l->filelen)
+       {
+               m = (dmiptexlump_t *)(mod_base + l->fileofs);
+               m->nummiptex = LittleLong (m->nummiptex);
+               loadmodel->num_textures = m->nummiptex + 2;
+       }
+       else
+       {
+               m = NULL;
+               loadmodel->num_textures = 2;
+       }
+
+       loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_textures * sizeof(texture_t));
 
        // fill out all slots with notexture
-       for (i = 0, tx = loadmodel->brushq1.textures;i < loadmodel->brushq1.numtextures;i++, tx++)
+       for (i = 0, tx = loadmodel->data_textures;i < loadmodel->num_textures;i++, tx++)
        {
-               tx->number = i;
                strcpy(tx->name, "NO TEXTURE FOUND");
                tx->width = 16;
                tx->height = 16;
-               tx->skin.base = r_notexture;
-               tx->shader = &Cshader_wall_lightmap;
-               tx->flags = SURF_SOLIDCLIP;
-               if (i == loadmodel->brushq1.numtextures - 1)
+               tx->skin.base = r_texture_notexture;
+               tx->basematerialflags = 0;
+               if (i == loadmodel->num_textures - 1)
+               {
+                       tx->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_LIGHTBOTHSIDES;
+                       tx->supercontents = mod_q1bsp_texture_water.supercontents;
+                       tx->surfaceflags = mod_q1bsp_texture_water.surfaceflags;
+               }
+               else
                {
-                       tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
-                       tx->shader = &Cshader_water;
+                       tx->basematerialflags |= MATERIALFLAG_WALL;
+                       tx->supercontents = mod_q1bsp_texture_solid.supercontents;
+                       tx->surfaceflags = mod_q1bsp_texture_solid.surfaceflags;
                }
                tx->currentframe = tx;
        }
@@ -781,7 +1273,7 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l)
                dofs[i] = LittleLong(dofs[i]);
                if (dofs[i] == -1 || r_nosurftextures.integer)
                        continue;
-               dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
+               dmiptex = (miptex_t *)((unsigned char *)m + dofs[i]);
 
                // make sure name is no more than 15 characters
                for (j = 0;dmiptex->name[j] && j < 15;j++)
@@ -800,18 +1292,18 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l)
                                Con_Printf("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
                                continue;
                        }
-                       mtdata = (qbyte *)dmiptex + j;
+                       mtdata = (unsigned char *)dmiptex + j;
                }
 
                if ((mtwidth & 15) || (mtheight & 15))
-                       Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
+                       Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned\n", dmiptex->name, loadmodel->name);
 
                // LordHavoc: force all names to lowercase
                for (j = 0;name[j];j++)
                        if (name[j] >= 'A' && name[j] <= 'Z')
                                name[j] += 'a' - 'A';
 
-               tx = loadmodel->brushq1.textures + i;
+               tx = loadmodel->data_textures + i;
                strcpy(tx->name, name);
                tx->width = mtwidth;
                tx->height = mtheight;
@@ -822,105 +1314,98 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l)
                        Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
                }
 
-               // LordHavoc: HL sky textures are entirely different than quake
-               if (!loadmodel->brush.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
+               if (cls.state != ca_dedicated)
                {
-                       if (loadmodel->isworldmodel)
+                       // LordHavoc: HL sky textures are entirely different than quake
+                       if (!loadmodel->brush.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
                        {
-                               data = loadimagepixels(tx->name, false, 0, 0);
-                               if (data)
+                               if (loadmodel->isworldmodel)
                                {
-                                       if (image_width == 256 && image_height == 128)
-                                       {
-                                               R_InitSky(data, 4);
-                                               Mem_Free(data);
-                                       }
-                                       else
+                                       data = loadimagepixels(tx->name, false, 0, 0);
+                                       if (data)
                                        {
+                                               R_Q1BSP_LoadSplitSky(data, image_width, image_height, 4);
                                                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);
                                        }
+                                       else if (mtdata != NULL)
+                                               R_Q1BSP_LoadSplitSky(mtdata, mtwidth, mtheight, 1);
                                }
-                               else if (mtdata != NULL)
-                                       R_InitSky(mtdata, 1);
                        }
-               }
-               else
-               {
-                       if (!Mod_LoadSkinFrame(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true))
+                       else
                        {
-                               // did not find external texture, load it from the bsp or wad3
-                               if (loadmodel->brush.ishlbsp)
+                               if (!Mod_LoadSkinFrame(&tx->skin, gamemode == GAME_TENEBRAE ? tx->name : va("textures/%s", tx->name), TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, false, true))
                                {
-                                       // internal texture overrides wad
-                                       qbyte *pixels, *freepixels, *fogpixels;
-                                       pixels = freepixels = NULL;
-                                       if (mtdata)
-                                               pixels = W_ConvertWAD3Texture(dmiptex);
-                                       if (pixels == NULL)
-                                               pixels = freepixels = W_GetTexture(tx->name);
-                                       if (pixels != NULL)
+                                       // did not find external texture, load it from the bsp or wad3
+                                       if (loadmodel->brush.ishlbsp)
                                        {
-                                               tx->width = image_width;
-                                               tx->height = image_height;
-                                               tx->skin.base = tx->skin.merged = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
-                                               if (Image_CheckAlpha(pixels, image_width * image_height, true))
+                                               // internal texture overrides wad
+                                               unsigned char *pixels, *freepixels;
+                                               pixels = freepixels = NULL;
+                                               if (mtdata)
+                                                       pixels = W_ConvertWAD3Texture(dmiptex);
+                                               if (pixels == NULL)
+                                                       pixels = freepixels = W_GetTexture(tx->name);
+                                               if (pixels != NULL)
                                                {
-                                                       fogpixels = Mem_Alloc(tempmempool, image_width * image_height * 4);
-                                                       for (j = 0;j < image_width * image_height * 4;j += 4)
-                                                       {
-                                                               fogpixels[j + 0] = 255;
-                                                               fogpixels[j + 1] = 255;
-                                                               fogpixels[j + 2] = 255;
-                                                               fogpixels[j + 3] = pixels[j + 3];
-                                                       }
-                                                       tx->skin.fog = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
-                                                       Mem_Free(fogpixels);
+                                                       tx->width = image_width;
+                                                       tx->height = image_height;
+                                                       Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, false, false, pixels, image_width, image_height, 32, NULL, NULL);
                                                }
+                                               if (freepixels)
+                                                       Mem_Free(freepixels);
                                        }
-                                       if (freepixels)
-                                               Mem_Free(freepixels);
+                                       else if (mtdata) // texture included
+                                               Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_PRECACHE | TEXF_PICMIP, false, r_fullbrights.integer, mtdata, tx->width, tx->height, 8, NULL, NULL);
                                }
-                               else if (mtdata) // texture included
-                                       Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_PRECACHE, false, true, tx->name[0] != '*' && r_fullbrights.integer, mtdata, tx->width, tx->height);
                        }
-               }
-               if (tx->skin.base == NULL)
-               {
-                       // no texture found
-                       tx->width = 16;
-                       tx->height = 16;
-                       tx->skin.base = r_notexture;
+                       if (tx->skin.base == NULL)
+                       {
+                               // no texture found
+                               tx->width = 16;
+                               tx->height = 16;
+                               tx->skin.base = r_texture_notexture;
+                       }
                }
 
+               tx->basematerialflags = 0;
                if (tx->name[0] == '*')
                {
-                       // turb does not block movement
-                       tx->flags &= ~SURF_SOLIDCLIP;
-                       tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
-                       // LordHavoc: some turbulent textures should be fullbright and solid
-                       if (!strncmp(tx->name,"*lava",5)
-                        || !strncmp(tx->name,"*teleport",9)
-                        || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
-                               tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA;
+                       // LordHavoc: some turbulent textures should not be affected by wateralpha
+                       if (strncmp(tx->name,"*lava",5)
+                        && strncmp(tx->name,"*teleport",9)
+                        && strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
+                               tx->basematerialflags |= MATERIALFLAG_WATERALPHA;
+                       if (!strncmp(tx->name, "*lava", 5))
+                       {
+                               tx->supercontents = mod_q1bsp_texture_lava.supercontents;
+                               tx->surfaceflags = mod_q1bsp_texture_lava.surfaceflags;
+                       }
+                       else if (!strncmp(tx->name, "*slime", 6))
+                       {
+                               tx->supercontents = mod_q1bsp_texture_slime.supercontents;
+                               tx->surfaceflags = mod_q1bsp_texture_slime.surfaceflags;
+                       }
                        else
-                               tx->flags |= SURF_WATERALPHA;
-                       tx->shader = &Cshader_water;
+                       {
+                               tx->supercontents = mod_q1bsp_texture_water.supercontents;
+                               tx->surfaceflags = mod_q1bsp_texture_water.surfaceflags;
+                       }
+                       tx->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_LIGHTBOTHSIDES;
                }
                else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
                {
-                       tx->flags |= SURF_DRAWSKY;
-                       tx->shader = &Cshader_sky;
+                       tx->supercontents = mod_q1bsp_texture_sky.supercontents;
+                       tx->surfaceflags = mod_q1bsp_texture_sky.surfaceflags;
+                       tx->basematerialflags |= MATERIALFLAG_SKY;
                }
                else
                {
-                       tx->flags |= SURF_LIGHTMAP;
-                       if (!tx->skin.fog)
-                               tx->flags |= SURF_SHADOWCAST | SURF_SHADOWLIGHT;
-                       tx->shader = &Cshader_wall_lightmap;
+                       tx->supercontents = mod_q1bsp_texture_solid.supercontents;
+                       tx->surfaceflags = mod_q1bsp_texture_solid.surfaceflags;
+                       tx->basematerialflags |= MATERIALFLAG_WALL;
                }
+               if (tx->skin.fog)
+                       tx->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_TRANSPARENT;
 
                // start out with no animation
                tx->currentframe = tx;
@@ -929,7 +1414,7 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l)
        // sequence the animations
        for (i = 0;i < m->nummiptex;i++)
        {
-               tx = loadmodel->brushq1.textures + i;
+               tx = loadmodel->data_textures + i;
                if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
                        continue;
                if (tx->anim_total[0] || tx->anim_total[1])
@@ -941,7 +1426,7 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l)
 
                for (j = i;j < m->nummiptex;j++)
                {
-                       tx2 = loadmodel->brushq1.textures + j;
+                       tx2 = loadmodel->data_textures + j;
                        if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
                                continue;
 
@@ -1031,56 +1516,80 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l)
 static void Mod_Q1BSP_LoadLighting(lump_t *l)
 {
        int i;
-       qbyte *in, *out, *data, d;
-       char litfilename[1024];
-       loadmodel->brushq1.lightdata = NULL;
+       unsigned char *in, *out, *data, d;
+       char litfilename[MAX_QPATH];
+       char dlitfilename[MAX_QPATH];
+       fs_offset_t filesize;
        if (loadmodel->brush.ishlbsp) // LordHavoc: load the colored lighting data straight
        {
-               loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
+               loadmodel->brushq1.lightdata = (unsigned char *)Mem_Alloc(loadmodel->mempool, l->filelen);
+               for (i=0; i<l->filelen; i++)
+                       loadmodel->brushq1.lightdata[i] = mod_base[l->fileofs+i] >>= 1;
+       }
+       else if (loadmodel->brush.ismcbsp)
+       {
+               loadmodel->brushq1.lightdata = (unsigned char *)Mem_Alloc(loadmodel->mempool, l->filelen);
                memcpy(loadmodel->brushq1.lightdata, mod_base + l->fileofs, l->filelen);
        }
        else // LordHavoc: bsp version 29 (normal white lighting)
        {
                // LordHavoc: hope is not lost yet, check for a .lit file to load
-               strcpy(litfilename, loadmodel->name);
-               FS_StripExtension(litfilename, litfilename);
-               strcat(litfilename, ".lit");
-               data = (qbyte*) FS_LoadFile(litfilename, false);
+               strlcpy (litfilename, loadmodel->name, sizeof (litfilename));
+               FS_StripExtension (litfilename, litfilename, sizeof (litfilename));
+               strlcpy (dlitfilename, litfilename, sizeof (dlitfilename));
+               strlcat (litfilename, ".lit", sizeof (litfilename));
+               strlcat (dlitfilename, ".dlit", sizeof (dlitfilename));
+               data = (unsigned char*) FS_LoadFile(litfilename, tempmempool, false, &filesize);
                if (data)
                {
-                       if (fs_filesize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
+                       if (filesize == (fs_offset_t)(8 + l->filelen * 3) && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
                        {
                                i = LittleLong(((int *)data)[1]);
                                if (i == 1)
                                {
                                        Con_DPrintf("loaded %s\n", litfilename);
-                                       loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, fs_filesize - 8);
-                                       memcpy(loadmodel->brushq1.lightdata, data + 8, fs_filesize - 8);
+                                       loadmodel->brushq1.lightdata = (unsigned char *)Mem_Alloc(loadmodel->mempool, filesize - 8);
+                                       memcpy(loadmodel->brushq1.lightdata, data + 8, filesize - 8);
                                        Mem_Free(data);
+                                       data = (unsigned char*) FS_LoadFile(dlitfilename, tempmempool, false, &filesize);
+                                       if (data)
+                                       {
+                                               if (filesize == (fs_offset_t)(8 + l->filelen * 3) && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
+                                               {
+                                                       i = LittleLong(((int *)data)[1]);
+                                                       if (i == 1)
+                                                       {
+                                                               Con_DPrintf("loaded %s\n", dlitfilename);
+                                                               loadmodel->brushq1.nmaplightdata = (unsigned char *)Mem_Alloc(loadmodel->mempool, filesize - 8);
+                                                               memcpy(loadmodel->brushq1.nmaplightdata, data + 8, filesize - 8);
+                                                               loadmodel->brushq3.deluxemapping_modelspace = false;
+                                                               loadmodel->brushq3.deluxemapping = true;
+                                                       }
+                                               }
+                                               Mem_Free(data);
+                                               data = NULL;
+                                       }
                                        return;
                                }
                                else
-                               {
                                        Con_Printf("Unknown .lit file version (%d)\n", i);
-                                       Mem_Free(data);
-                               }
                        }
+                       else if (filesize == 8)
+                               Con_Print("Empty .lit file, ignoring\n");
                        else
+                               Con_Printf("Corrupt .lit file (file size %i bytes, should be %i bytes), ignoring\n", filesize, 8 + l->filelen * 3);
+                       if (data)
                        {
-                               if (fs_filesize == 8)
-                                       Con_Printf("Empty .lit file, ignoring\n");
-                               else
-                                       Con_Printf("Corrupt .lit file (old version?), ignoring\n");
                                Mem_Free(data);
+                               data = NULL;
                        }
                }
                // LordHavoc: oh well, expand the white lighting data
                if (!l->filelen)
                        return;
-               loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
-               in = loadmodel->brushq1.lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
+               loadmodel->brushq1.lightdata = (unsigned char *)Mem_Alloc(loadmodel->mempool, l->filelen*3);
+               in = mod_base + l->fileofs;
                out = loadmodel->brushq1.lightdata;
-               memcpy(in, mod_base + l->fileofs, l->filelen);
                for (i = 0;i < l->filelen;i++)
                {
                        d = *in++;
@@ -1091,66 +1600,6 @@ static void Mod_Q1BSP_LoadLighting(lump_t *l)
        }
 }
 
-static void Mod_Q1BSP_LoadLightList(void)
-{
-       int a, n, numlights;
-       char lightsfilename[1024], *s, *t, *lightsstring;
-       mlight_t *e;
-
-       strcpy(lightsfilename, loadmodel->name);
-       FS_StripExtension(lightsfilename, lightsfilename);
-       strcat(lightsfilename, ".lights");
-       s = lightsstring = (char *) FS_LoadFile(lightsfilename, false);
-       if (s)
-       {
-               numlights = 0;
-               while (*s)
-               {
-                       while (*s && *s != '\n')
-                               s++;
-                       if (!*s)
-                       {
-                               Mem_Free(lightsstring);
-                               Host_Error("lights file must end with a newline\n");
-                       }
-                       s++;
-                       numlights++;
-               }
-               loadmodel->brushq1.lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
-               s = lightsstring;
-               n = 0;
-               while (*s && n < numlights)
-               {
-                       t = s;
-                       while (*s && *s != '\n')
-                               s++;
-                       if (!*s)
-                       {
-                               Mem_Free(lightsstring);
-                               Host_Error("misparsed lights file!\n");
-                       }
-                       e = loadmodel->brushq1.lights + n;
-                       *s = 0;
-                       a = sscanf(t, "%f %f %f %f %f %f %f %f %f %f %f %f %f %d", &e->origin[0], &e->origin[1], &e->origin[2], &e->falloff, &e->light[0], &e->light[1], &e->light[2], &e->subtract, &e->spotdir[0], &e->spotdir[1], &e->spotdir[2], &e->spotcone, &e->distbias, &e->style);
-                       *s = '\n';
-                       if (a != 14)
-                       {
-                               Mem_Free(lightsstring);
-                               Host_Error("invalid lights file, found %d parameters on line %i, should be 14 parameters (origin[0] origin[1] origin[2] falloff light[0] light[1] light[2] subtract spotdir[0] spotdir[1] spotdir[2] spotcone distancebias style)\n", a, n + 1);
-                       }
-                       s++;
-                       n++;
-               }
-               if (*s)
-               {
-                       Mem_Free(lightsstring);
-                       Host_Error("misparsed lights file!\n");
-               }
-               loadmodel->brushq1.numlights = numlights;
-               Mem_Free(lightsstring);
-       }
-}
-
 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
 {
        loadmodel->brushq1.num_compressedpvs = 0;
@@ -1158,7 +1607,7 @@ static void Mod_Q1BSP_LoadVisibility(lump_t *l)
        if (!l->filelen)
                return;
        loadmodel->brushq1.num_compressedpvs = l->filelen;
-       loadmodel->brushq1.data_compressedpvs = Mem_Alloc(loadmodel->mempool, l->filelen);
+       loadmodel->brushq1.data_compressedpvs = (unsigned char *)Mem_Alloc(loadmodel->mempool, l->filelen);
        memcpy(loadmodel->brushq1.data_compressedpvs, mod_base + l->fileofs, l->filelen);
 }
 
@@ -1188,18 +1637,18 @@ static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
                        key[strlen(key)-1] = 0;
                if (!COM_ParseToken(&data, false))
                        return; // error
-               strcpy(value, com_token);
+               dpsnprintf(value, sizeof(value), "%s", com_token);
                if (!strcmp("wad", key)) // for HalfLife maps
                {
                        if (loadmodel->brush.ishlbsp)
                        {
                                j = 0;
-                               for (i = 0;i < 4096;i++)
+                               for (i = 0;i < (int)sizeof(value);i++)
                                        if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
                                                break;
                                if (value[i])
                                {
-                                       for (;i < 4096;i++)
+                                       for (;i < (int)sizeof(value);i++)
                                        {
                                                // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
                                                if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
@@ -1227,7 +1676,7 @@ static void Mod_Q1BSP_LoadEntities(lump_t *l)
        loadmodel->brush.entities = NULL;
        if (!l->filelen)
                return;
-       loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
+       loadmodel->brush.entities = (char *)Mem_Alloc(loadmodel->mempool, l->filelen);
        memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
        if (loadmodel->brush.ishlbsp)
                Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities);
@@ -1240,11 +1689,11 @@ static void Mod_Q1BSP_LoadVertexes(lump_t *l)
        mvertex_t       *out;
        int                     i, count;
 
-       in = (void *)(mod_base + l->fileofs);
+       in = (dvertex_t *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
                Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
        count = l->filelen / sizeof(*in);
-       out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
+       out = (mvertex_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
 
        loadmodel->brushq1.vertexes = out;
        loadmodel->brushq1.numvertexes = count;
@@ -1257,35 +1706,63 @@ static void Mod_Q1BSP_LoadVertexes(lump_t *l)
        }
 }
 
-static void Mod_Q1BSP_LoadSubmodels(lump_t *l)
+// The following two functions should be removed and MSG_* or SZ_* function sets adjusted so they
+// can be used for this
+// REMOVEME
+int SB_ReadInt (unsigned char **buffer)
+{
+       int     i;
+       i = ((*buffer)[0]) + 256*((*buffer)[1]) + 65536*((*buffer)[2]) + 16777216*((*buffer)[3]);
+       (*buffer) += 4;
+       return i;
+}
+
+// REMOVEME
+float SB_ReadFloat (unsigned char **buffer)
+{
+       union
+       {
+               int             i;
+               float   f;
+       } u;
+
+       u.i = SB_ReadInt (buffer);
+       return u.f;
+}
+
+static void Mod_Q1BSP_LoadSubmodels(lump_t *l, hullinfo_t *hullinfo)
 {
-       dmodel_t        *in;
+       unsigned char           *index;
        dmodel_t        *out;
        int                     i, j, count;
 
-       in = (void *)(mod_base + l->fileofs);
-       if (l->filelen % sizeof(*in))
-               Host_Error("Mod_Q1BSP_LoadSubmodels: funny lump size in %s",loadmodel->name);
-       count = l->filelen / sizeof(*in);
-       out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
+       index = (unsigned char *)(mod_base + l->fileofs);
+       if (l->filelen % (48+4*hullinfo->filehulls))
+               Host_Error ("Mod_Q1BSP_LoadSubmodels: funny lump size in %s", loadmodel->name);
+
+       count = l->filelen / (48+4*hullinfo->filehulls);
+       out = (dmodel_t *)Mem_Alloc (loadmodel->mempool, count*sizeof(*out));
 
        loadmodel->brushq1.submodels = out;
        loadmodel->brush.numsubmodels = count;
 
-       for ( i=0 ; i<count ; i++, in++, out++)
+       for (i = 0; i < count; i++, out++)
        {
-               for (j=0 ; j<3 ; j++)
-               {
-                       // spread the mins / maxs by a pixel
-                       out->mins[j] = LittleFloat(in->mins[j]) - 1;
-                       out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
-                       out->origin[j] = LittleFloat(in->origin[j]);
-               }
-               for (j=0 ; j<MAX_MAP_HULLS ; j++)
-                       out->headnode[j] = LittleLong(in->headnode[j]);
-               out->visleafs = LittleLong(in->visleafs);
-               out->firstface = LittleLong(in->firstface);
-               out->numfaces = LittleLong(in->numfaces);
+       // spread out the mins / maxs by a pixel
+               out->mins[0] = SB_ReadFloat (&index) - 1;
+               out->mins[1] = SB_ReadFloat (&index) - 1;
+               out->mins[2] = SB_ReadFloat (&index) - 1;
+               out->maxs[0] = SB_ReadFloat (&index) + 1;
+               out->maxs[1] = SB_ReadFloat (&index) + 1;
+               out->maxs[2] = SB_ReadFloat (&index) + 1;
+               out->origin[0] = SB_ReadFloat (&index);
+               out->origin[1] = SB_ReadFloat (&index);
+               out->origin[2] = SB_ReadFloat (&index);
+               for (j = 0; j < hullinfo->filehulls; j++)
+                       out->headnode[j] = SB_ReadInt (&index);
+               out->visleafs = SB_ReadInt (&index);
+               out->firstface = SB_ReadInt (&index);
+               out->numfaces = SB_ReadInt (&index);
        }
 }
 
@@ -1295,11 +1772,11 @@ static void Mod_Q1BSP_LoadEdges(lump_t *l)
        medge_t *out;
        int     i, count;
 
-       in = (void *)(mod_base + l->fileofs);
+       in = (dedge_t *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
                Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name);
        count = l->filelen / sizeof(*in);
-       out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+       out = (medge_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
 
        loadmodel->brushq1.edges = out;
        loadmodel->brushq1.numedges = count;
@@ -1308,6 +1785,12 @@ static void Mod_Q1BSP_LoadEdges(lump_t *l)
        {
                out->v[0] = (unsigned short)LittleShort(in->v[0]);
                out->v[1] = (unsigned short)LittleShort(in->v[1]);
+               if (out->v[0] >= loadmodel->brushq1.numvertexes || out->v[1] >= loadmodel->brushq1.numvertexes)
+               {
+                       Con_Printf("Mod_Q1BSP_LoadEdges: %s has invalid vertex indices in edge %i (vertices %i %i >= numvertices %i)\n", loadmodel->name, i, out->v[0], out->v[1], loadmodel->brushq1.numvertexes);
+                       out->v[0] = 0;
+                       out->v[1] = 0;
+               }
        }
 }
 
@@ -1317,11 +1800,11 @@ static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
        mtexinfo_t *out;
        int i, j, k, count, miptex;
 
-       in = (void *)(mod_base + l->fileofs);
+       in = (texinfo_t *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
                Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
        count = l->filelen / sizeof(*in);
-       out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+       out = (mtexinfo_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
 
        loadmodel->brushq1.texinfo = out;
        loadmodel->brushq1.numtexinfo = count;
@@ -1336,25 +1819,25 @@ static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
                out->flags = LittleLong(in->flags);
 
                out->texture = NULL;
-               if (loadmodel->brushq1.textures)
+               if (loadmodel->data_textures)
                {
-                       if ((unsigned int) miptex >= (unsigned int) loadmodel->brushq1.numtextures)
-                               Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->brushq1.numtextures);
+                       if ((unsigned int) miptex >= (unsigned int) loadmodel->num_textures)
+                               Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->num_textures);
                        else
-                               out->texture = loadmodel->brushq1.textures + miptex;
+                               out->texture = loadmodel->data_textures + miptex;
                }
                if (out->flags & TEX_SPECIAL)
                {
                        // if texture chosen is NULL or the shader needs a lightmap,
                        // force to notexture water shader
-                       if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
-                               out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 1);
+                       if (out->texture == NULL || out->texture->basematerialflags & MATERIALFLAG_WALL)
+                               out->texture = loadmodel->data_textures + (loadmodel->num_textures - 1);
                }
                else
                {
                        // if texture chosen is NULL, force to notexture
                        if (out->texture == NULL)
-                               out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 2);
+                               out->texture = loadmodel->data_textures + (loadmodel->num_textures - 2);
                }
        }
 }
@@ -1463,7 +1946,7 @@ static void SubdividePolygon(int numverts, float *verts)
        {
                if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
                {
-                       Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
+                       Con_Print("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
                        return;
                }
 
@@ -1479,7 +1962,7 @@ static void SubdividePolygon(int numverts, float *verts)
 //Breaks a polygon up along axial 64 unit
 //boundaries so that turbulent and sky warps
 //can be done reasonably.
-static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surf)
+static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surface)
 {
        int i, j;
        surfvertex_t *v;
@@ -1487,276 +1970,269 @@ static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surf)
 
        subdivpolytriangles = 0;
        subdivpolyverts = 0;
-       SubdividePolygon(surf->poly_numverts, surf->poly_verts);
+       SubdividePolygon(surface->num_vertices, (surface->mesh->data_vertex3f + 3 * surface->num_firstvertex));
        if (subdivpolytriangles < 1)
-               Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?\n");
+               Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?");
 
-       surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
-       mesh->numverts = subdivpolyverts;
-       mesh->numtriangles = subdivpolytriangles;
+       surface->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
+       mesh->num_vertices = subdivpolyverts;
+       mesh->num_triangles = subdivpolytriangles;
        mesh->vertex = (surfvertex_t *)(mesh + 1);
-       mesh->index = (int *)(mesh->vertex + mesh->numverts);
-       memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
+       mesh->index = (int *)(mesh->vertex + mesh->num_vertices);
+       memset(mesh->vertex, 0, mesh->num_vertices * sizeof(surfvertex_t));
 
-       for (i = 0;i < mesh->numtriangles;i++)
+       for (i = 0;i < mesh->num_triangles;i++)
                for (j = 0;j < 3;j++)
                        mesh->index[i*3+j] = subdivpolyindex[i][j];
 
        for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
        {
                VectorCopy(subdivpolyvert[i], v->v);
-               v->st[0] = DotProduct(v->v, surf->texinfo->vecs[0]);
-               v->st[1] = DotProduct(v->v, surf->texinfo->vecs[1]);
+               v->st[0] = DotProduct(v->v, surface->lightmapinfo->texinfo->vecs[0]);
+               v->st[1] = DotProduct(v->v, surface->lightmapinfo->texinfo->vecs[1]);
        }
 }
 #endif
 
-static surfmesh_t *Mod_Q1BSP_AllocSurfMesh(int numverts, int numtriangles)
-{
-       surfmesh_t *mesh;
-       mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (3 + 2 + 2 + 2 + 3 + 3 + 3 + 1) * sizeof(float));
-       mesh->numverts = numverts;
-       mesh->numtriangles = numtriangles;
-       mesh->vertex3f = (float *)(mesh + 1);
-       mesh->texcoordtexture2f = mesh->vertex3f + mesh->numverts * 3;
-       mesh->texcoordlightmap2f = mesh->texcoordtexture2f + mesh->numverts * 2;
-       mesh->texcoorddetail2f = mesh->texcoordlightmap2f + mesh->numverts * 2;
-       mesh->svector3f = (float *)(mesh->texcoorddetail2f + mesh->numverts * 2);
-       mesh->tvector3f = mesh->svector3f + mesh->numverts * 3;
-       mesh->normal3f = mesh->tvector3f + mesh->numverts * 3;
-       mesh->lightmapoffsets = (int *)(mesh->normal3f + mesh->numverts * 3);
-       mesh->element3i = mesh->lightmapoffsets + mesh->numverts;
-       mesh->neighbor3i = mesh->element3i + mesh->numtriangles * 3;
-       return mesh;
-}
-
-static void Mod_Q1BSP_GenerateSurfacePolygon(msurface_t *surf, int firstedge, int numedges)
+static qboolean Mod_Q1BSP_AllocLightmapBlock(int *lineused, int totalwidth, int totalheight, int blockwidth, int blockheight, int *outx, int *outy)
 {
-       int i, lindex, j;
-       float *vec, *vert, mins[3], maxs[3], val, *v;
-       mtexinfo_t *tex;
-
-       // convert edges back to a normal polygon
-       surf->poly_numverts = numedges;
-       vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * numedges);
-       for (i = 0;i < numedges;i++)
-       {
-               lindex = loadmodel->brushq1.surfedges[firstedge + i];
-               if (lindex > 0)
-                       vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position;
-               else
-                       vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position;
-               VectorCopy(vec, vert);
-               vert += 3;
-       }
-
-       // calculate polygon bounding box and center
-       vert = surf->poly_verts;
-       VectorCopy(vert, mins);
-       VectorCopy(vert, maxs);
-       vert += 3;
-       for (i = 1;i < surf->poly_numverts;i++, vert += 3)
-       {
-               if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
-               if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
-               if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
-       }
-       VectorCopy(mins, surf->poly_mins);
-       VectorCopy(maxs, surf->poly_maxs);
-       surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
-       surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
-       surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
-
-       // generate surface extents information
-       tex = surf->texinfo;
-       mins[0] = maxs[0] = DotProduct(surf->poly_verts, tex->vecs[0]) + tex->vecs[0][3];
-       mins[1] = maxs[1] = DotProduct(surf->poly_verts, tex->vecs[1]) + tex->vecs[1][3];
-       for (i = 1, v = surf->poly_verts + 3;i < surf->poly_numverts;i++, v += 3)
+       int y, x2, y2;
+       int bestx = totalwidth, besty = 0;
+       // find the left-most space we can find
+       for (y = 0;y <= totalheight - blockheight;y++)
        {
-               for (j = 0;j < 2;j++)
+               x2 = 0;
+               for (y2 = 0;y2 < blockheight;y2++)
+                       x2 = max(x2, lineused[y+y2]);
+               if (bestx > x2)
                {
-                       val = DotProduct(v, tex->vecs[j]) + tex->vecs[j][3];
-                       if (mins[j] > val)
-                               mins[j] = val;
-                       if (maxs[j] < val)
-                               maxs[j] = val;
+                       bestx = x2;
+                       besty = y;
                }
        }
-       for (i = 0;i < 2;i++)
-       {
-               surf->texturemins[i] = (int) floor(mins[i] / 16) * 16;
-               surf->extents[i] = (int) ceil(maxs[i] / 16) * 16 - surf->texturemins[i];
-       }
+       // if the best was not good enough, return failure
+       if (bestx > totalwidth - blockwidth)
+               return false;
+       // we found a good spot
+       if (outx)
+               *outx = bestx;
+       if (outy)
+               *outy = besty;
+       // now mark the space used
+       for (y2 = 0;y2 < blockheight;y2++)
+               lineused[besty+y2] = bestx + blockwidth;
+       // return success
+       return true;
 }
 
 static void Mod_Q1BSP_LoadFaces(lump_t *l)
 {
        dface_t *in;
-       msurface_t *surf;
-       int i, count, surfnum, planenum, ssize, tsize, firstedge, numedges, totalverts, totaltris, totalmeshes;
-       surfmesh_t *mesh;
-       float s, t;
-
-       in = (void *)(mod_base + l->fileofs);
+       msurface_t *surface;
+       int i, j, count, surfacenum, planenum, smax, tmax, ssize, tsize, firstedge, numedges, totalverts, totaltris, lightmapnumber;
+       float texmins[2], texmaxs[2], val, lightmaptexcoordscale;
+#define LIGHTMAPSIZE 256
+       rtexture_t *lightmaptexture, *deluxemaptexture;
+       int lightmap_lineused[LIGHTMAPSIZE];
+
+       in = (dface_t *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
                Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
        count = l->filelen / sizeof(*in);
-       loadmodel->brushq1.surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
+       loadmodel->data_surfaces = (msurface_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
+       loadmodel->data_surfaces_lightmapinfo = (msurface_lightmapinfo_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_lightmapinfo_t));
+
+       loadmodel->num_surfaces = count;
+
+       totalverts = 0;
+       totaltris = 0;
+       for (surfacenum = 0, in = (dface_t *)(mod_base + l->fileofs);surfacenum < count;surfacenum++, in++)
+       {
+               numedges = LittleShort(in->numedges);
+               totalverts += numedges;
+               totaltris += numedges - 2;
+       }
 
-       loadmodel->brushq1.numsurfaces = count;
-       loadmodel->brushq1.surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
-       loadmodel->brushq1.surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
-       loadmodel->brushq1.pvssurflist = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
+       Mod_AllocSurfMesh(loadmodel->mempool, totalverts, totaltris, true, false, false);
 
-       for (surfnum = 0, surf = loadmodel->brushq1.surfaces, totalverts = 0, totaltris = 0, totalmeshes = 0;surfnum < count;surfnum++, totalverts += surf->poly_numverts, totaltris += surf->poly_numverts - 2, totalmeshes++, in++, surf++)
+       lightmaptexture = NULL;
+       deluxemaptexture = r_texture_blanknormalmap;
+       lightmapnumber = 1;
+       lightmaptexcoordscale = 1.0f / (float)LIGHTMAPSIZE;
+
+       totalverts = 0;
+       totaltris = 0;
+       for (surfacenum = 0, in = (dface_t *)(mod_base + l->fileofs), surface = loadmodel->data_surfaces;surfacenum < count;surfacenum++, in++, surface++)
        {
-               surf->number = surfnum;
+               surface->lightmapinfo = loadmodel->data_surfaces_lightmapinfo + surfacenum;
+
                // FIXME: validate edges, texinfo, etc?
                firstedge = LittleLong(in->firstedge);
                numedges = LittleShort(in->numedges);
                if ((unsigned int) firstedge > (unsigned int) loadmodel->brushq1.numsurfedges || (unsigned int) numedges > (unsigned int) loadmodel->brushq1.numsurfedges || (unsigned int) firstedge + (unsigned int) numedges > (unsigned int) loadmodel->brushq1.numsurfedges)
-                       Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", firstedge, numedges, loadmodel->brushq1.numsurfedges);
+                       Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)", firstedge, numedges, loadmodel->brushq1.numsurfedges);
                i = LittleShort(in->texinfo);
                if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
-                       Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)\n", i, loadmodel->brushq1.numtexinfo);
-               surf->texinfo = loadmodel->brushq1.texinfo + i;
-               surf->flags = surf->texinfo->texture->flags;
+                       Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)", i, loadmodel->brushq1.numtexinfo);
+               surface->lightmapinfo->texinfo = loadmodel->brushq1.texinfo + i;
+               surface->texture = surface->lightmapinfo->texinfo->texture;
 
                planenum = LittleShort(in->planenum);
-               if ((unsigned int) planenum >= (unsigned int) loadmodel->brushq1.numplanes)
-                       Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->brushq1.numplanes);
-
-               if (LittleShort(in->side))
-                       surf->flags |= SURF_PLANEBACK;
-
-               surf->plane = loadmodel->brushq1.planes + planenum;
+               if ((unsigned int) planenum >= (unsigned int) loadmodel->brush.num_planes)
+                       Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)", planenum, loadmodel->brush.num_planes);
+
+               //surface->flags = surface->texture->flags;
+               //if (LittleShort(in->side))
+               //      surface->flags |= SURF_PLANEBACK;
+               //surface->plane = loadmodel->brush.data_planes + planenum;
+
+               surface->num_firstvertex = totalverts;
+               surface->num_vertices = numedges;
+               surface->num_firsttriangle = totaltris;
+               surface->num_triangles = numedges - 2;
+               totalverts += numedges;
+               totaltris += numedges - 2;
+
+               // convert edges back to a normal polygon
+               for (i = 0;i < surface->num_vertices;i++)
+               {
+                       int lindex = loadmodel->brushq1.surfedges[firstedge + i];
+                       float s, t;
+                       if (lindex > 0)
+                               VectorCopy(loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position, (loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3);
+                       else
+                               VectorCopy(loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position, (loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3);
+                       s = DotProduct(((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3), surface->lightmapinfo->texinfo->vecs[0]) + surface->lightmapinfo->texinfo->vecs[0][3];
+                       t = DotProduct(((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3), surface->lightmapinfo->texinfo->vecs[1]) + surface->lightmapinfo->texinfo->vecs[1][3];
+                       (loadmodel->surfmesh.data_texcoordtexture2f + 2 * surface->num_firstvertex)[i * 2 + 0] = s / surface->texture->width;
+                       (loadmodel->surfmesh.data_texcoordtexture2f + 2 * surface->num_firstvertex)[i * 2 + 1] = t / surface->texture->height;
+                       (loadmodel->surfmesh.data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 0] = 0;
+                       (loadmodel->surfmesh.data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 1] = 0;
+                       (loadmodel->surfmesh.data_lightmapoffsets + surface->num_firstvertex)[i] = 0;
+               }
 
-               // clear lightmap (filled in later)
-               surf->lightmaptexture = NULL;
+               for (i = 0;i < surface->num_triangles;i++)
+               {
+                       (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle)[i * 3 + 0] = 0 + surface->num_firstvertex;
+                       (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle)[i * 3 + 1] = i + 1 + surface->num_firstvertex;
+                       (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle)[i * 3 + 2] = i + 2 + surface->num_firstvertex;
+               }
 
-               // force lightmap upload on first time seeing the surface
-               surf->cached_dlight = true;
+               // compile additional data about the surface geometry
+               Mod_BuildTextureVectorsAndNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_texcoordtexture2f, (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle), loadmodel->surfmesh.data_svector3f, loadmodel->surfmesh.data_tvector3f, loadmodel->surfmesh.data_normal3f, true);
+               BoxFromPoints(surface->mins, surface->maxs, surface->num_vertices, (loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex));
 
-               Mod_Q1BSP_GenerateSurfacePolygon(surf, firstedge, numedges);
+               // generate surface extents information
+               texmins[0] = texmaxs[0] = DotProduct((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex), surface->lightmapinfo->texinfo->vecs[0]) + surface->lightmapinfo->texinfo->vecs[0][3];
+               texmins[1] = texmaxs[1] = DotProduct((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex), surface->lightmapinfo->texinfo->vecs[1]) + surface->lightmapinfo->texinfo->vecs[1][3];
+               for (i = 1;i < surface->num_vertices;i++)
+               {
+                       for (j = 0;j < 2;j++)
+                       {
+                               val = DotProduct((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3, surface->lightmapinfo->texinfo->vecs[j]) + surface->lightmapinfo->texinfo->vecs[j][3];
+                               texmins[j] = min(texmins[j], val);
+                               texmaxs[j] = max(texmaxs[j], val);
+                       }
+               }
+               for (i = 0;i < 2;i++)
+               {
+                       surface->lightmapinfo->texturemins[i] = (int) floor(texmins[i] / 16.0) * 16;
+                       surface->lightmapinfo->extents[i] = (int) ceil(texmaxs[i] / 16.0) * 16 - surface->lightmapinfo->texturemins[i];
+               }
 
-               ssize = (surf->extents[0] >> 4) + 1;
-               tsize = (surf->extents[1] >> 4) + 1;
+               smax = surface->lightmapinfo->extents[0] >> 4;
+               tmax = surface->lightmapinfo->extents[1] >> 4;
+               ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
+               tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
 
                // lighting info
                for (i = 0;i < MAXLIGHTMAPS;i++)
-                       surf->styles[i] = in->styles[i];
+                       surface->lightmapinfo->styles[i] = in->styles[i];
+               surface->lightmaptexture = NULL;
+               surface->deluxemaptexture = r_texture_blanknormalmap;
                i = LittleLong(in->lightofs);
                if (i == -1)
-                       surf->samples = NULL;
-               else if (loadmodel->brush.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
-                       surf->samples = loadmodel->brushq1.lightdata + i;
-               else // LordHavoc: white lighting (bsp version 29)
-                       surf->samples = loadmodel->brushq1.lightdata + (i * 3);
-
-               if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
-               {
-                       if ((surf->extents[0] >> 4) + 1 > (256) || (surf->extents[1] >> 4) + 1 > (256))
-                               Host_Error("Bad surface extents");
-                       // stainmap for permanent marks on walls
-                       surf->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
-                       // clear to white
-                       memset(surf->stainsamples, 255, ssize * tsize * 3);
-               }
-       }
-
-       loadmodel->brushq1.entiremesh = Mod_Q1BSP_AllocSurfMesh(totalverts, totaltris);
-       loadmodel->brushq1.surfmeshes = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) * totalmeshes);
-
-       for (surfnum = 0, surf = loadmodel->brushq1.surfaces, totalverts = 0, totaltris = 0, totalmeshes = 0;surfnum < count;surfnum++, totalverts += surf->poly_numverts, totaltris += surf->poly_numverts - 2, totalmeshes++, surf++)
-       {
-               mesh = surf->mesh = loadmodel->brushq1.surfmeshes + totalmeshes;
-               mesh->numverts = surf->poly_numverts;
-               mesh->numtriangles = surf->poly_numverts - 2;
-               mesh->vertex3f = loadmodel->brushq1.entiremesh->vertex3f + totalverts * 3;
-               mesh->texcoordtexture2f = loadmodel->brushq1.entiremesh->texcoordtexture2f + totalverts * 2;
-               mesh->texcoordlightmap2f = loadmodel->brushq1.entiremesh->texcoordlightmap2f + totalverts * 2;
-               mesh->texcoorddetail2f = loadmodel->brushq1.entiremesh->texcoorddetail2f + totalverts * 2;
-               mesh->svector3f = loadmodel->brushq1.entiremesh->svector3f + totalverts * 3;
-               mesh->tvector3f = loadmodel->brushq1.entiremesh->tvector3f + totalverts * 3;
-               mesh->normal3f = loadmodel->brushq1.entiremesh->normal3f + totalverts * 3;
-               mesh->lightmapoffsets = loadmodel->brushq1.entiremesh->lightmapoffsets + totalverts;
-               mesh->element3i = loadmodel->brushq1.entiremesh->element3i + totaltris * 3;
-               mesh->neighbor3i = loadmodel->brushq1.entiremesh->neighbor3i + totaltris * 3;
-
-               surf->lightmaptexturestride = 0;
-               surf->lightmaptexture = NULL;
-
-               for (i = 0;i < mesh->numverts;i++)
                {
-                       mesh->vertex3f[i * 3 + 0] = surf->poly_verts[i * 3 + 0];
-                       mesh->vertex3f[i * 3 + 1] = surf->poly_verts[i * 3 + 1];
-                       mesh->vertex3f[i * 3 + 2] = surf->poly_verts[i * 3 + 2];
-                       s = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
-                       t = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
-                       mesh->texcoordtexture2f[i * 2 + 0] = s / surf->texinfo->texture->width;
-                       mesh->texcoordtexture2f[i * 2 + 1] = t / surf->texinfo->texture->height;
-                       mesh->texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f);
-                       mesh->texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f);
-                       mesh->texcoordlightmap2f[i * 2 + 0] = 0;
-                       mesh->texcoordlightmap2f[i * 2 + 1] = 0;
-                       mesh->lightmapoffsets[i] = 0;
+                       surface->lightmapinfo->samples = NULL;
+                       // give non-lightmapped water a 1x white lightmap
+                       if ((surface->texture->basematerialflags & MATERIALFLAG_WATER) && (surface->lightmapinfo->texinfo->flags & TEX_SPECIAL) && ssize <= 256 && tsize <= 256)
+                       {
+                               surface->lightmapinfo->samples = (unsigned char *)Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
+                               surface->lightmapinfo->styles[0] = 0;
+                               memset(surface->lightmapinfo->samples, 128, ssize * tsize * 3);
+                       }
                }
-
-               for (i = 0;i < mesh->numtriangles;i++)
+               else if (loadmodel->brush.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
+                       surface->lightmapinfo->samples = loadmodel->brushq1.lightdata + i;
+               else // LordHavoc: white lighting (bsp version 29)
                {
-                       mesh->element3i[i * 3 + 0] = 0;
-                       mesh->element3i[i * 3 + 1] = i + 1;
-                       mesh->element3i[i * 3 + 2] = i + 2;
+                       surface->lightmapinfo->samples = loadmodel->brushq1.lightdata + (i * 3);
+                       if (loadmodel->brushq1.nmaplightdata)
+                               surface->lightmapinfo->nmapsamples = loadmodel->brushq1.nmaplightdata + (i * 3);
                }
 
-               Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
-               Mod_BuildTextureVectorsAndNormals(mesh->numverts, mesh->numtriangles, mesh->vertex3f, mesh->texcoordtexture2f, mesh->element3i, mesh->svector3f, mesh->tvector3f, mesh->normal3f);
-
-               if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
+               // check if we should apply a lightmap to this
+               if (!(surface->lightmapinfo->texinfo->flags & TEX_SPECIAL) || surface->lightmapinfo->samples)
                {
-                       int i, iu, iv, smax, tmax;
+                       int i, iu, iv, lightmapx, lightmapy;
                        float u, v, ubase, vbase, uscale, vscale;
 
-                       smax = surf->extents[0] >> 4;
-                       tmax = surf->extents[1] >> 4;
+                       if (ssize > 256 || tsize > 256)
+                               Host_Error("Bad surface extents");
+                       // force lightmap upload on first time seeing the surface
+                       surface->cached_dlight = true;
+                       // stainmap for permanent marks on walls
+                       surface->lightmapinfo->stainsamples = (unsigned char *)Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
+                       // clear to white
+                       memset(surface->lightmapinfo->stainsamples, 255, ssize * tsize * 3);
 
-                       surf->flags |= SURF_LIGHTMAP;
-                       if (r_miplightmaps.integer)
+                       // find a place for this lightmap
+                       if (!lightmaptexture || !Mod_Q1BSP_AllocLightmapBlock(lightmap_lineused, LIGHTMAPSIZE, LIGHTMAPSIZE, ssize, tsize, &lightmapx, &lightmapy))
                        {
-                               surf->lightmaptexturestride = smax+1;
-                               surf->lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_MIPMAP | TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
+                               // could not find room, make a new lightmap
+                               lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%i", lightmapnumber), LIGHTMAPSIZE, LIGHTMAPSIZE, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
+                               if (loadmodel->brushq1.nmaplightdata)
+                                       deluxemaptexture = R_LoadTexture2D(loadmodel->texturepool, va("deluxemap%i", lightmapnumber), LIGHTMAPSIZE, LIGHTMAPSIZE, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
+                               lightmapnumber++;
+                               memset(lightmap_lineused, 0, sizeof(lightmap_lineused));
+                               Mod_Q1BSP_AllocLightmapBlock(lightmap_lineused, LIGHTMAPSIZE, LIGHTMAPSIZE, ssize, tsize, &lightmapx, &lightmapy);
                        }
-                       else
-                       {
-                               surf->lightmaptexturestride = R_CompatibleFragmentWidth(smax+1, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
-                               surf->lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FRAGMENT | TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
-                       }
-                       R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
-                       uscale = (uscale - ubase) / (smax + 1);
-                       vscale = (vscale - vbase) / (tmax + 1);
 
-                       for (i = 0;i < mesh->numverts;i++)
+                       surface->lightmaptexture = lightmaptexture;
+                       surface->deluxemaptexture = deluxemaptexture;
+                       surface->lightmapinfo->lightmaporigin[0] = lightmapx;
+                       surface->lightmapinfo->lightmaporigin[1] = lightmapy;
+
+                       ubase = lightmapx * lightmaptexcoordscale;
+                       vbase = lightmapy * lightmaptexcoordscale;
+                       uscale = lightmaptexcoordscale;
+                       vscale = lightmaptexcoordscale;
+
+                       for (i = 0;i < surface->num_vertices;i++)
                        {
-                               u = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) + 8 - surf->texturemins[0]) * (1.0 / 16.0);
-                               v = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) + 8 - surf->texturemins[1]) * (1.0 / 16.0);
-                               mesh->texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase;
-                               mesh->texcoordlightmap2f[i * 2 + 1] = v * vscale + vbase;
+                               u = ((DotProduct(((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3), surface->lightmapinfo->texinfo->vecs[0]) + surface->lightmapinfo->texinfo->vecs[0][3]) + 8 - surface->lightmapinfo->texturemins[0]) * (1.0 / 16.0);
+                               v = ((DotProduct(((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3), surface->lightmapinfo->texinfo->vecs[1]) + surface->lightmapinfo->texinfo->vecs[1][3]) + 8 - surface->lightmapinfo->texturemins[1]) * (1.0 / 16.0);
+                               (loadmodel->surfmesh.data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 0] = u * uscale + ubase;
+                               (loadmodel->surfmesh.data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 1] = v * vscale + vbase;
                                // LordHavoc: calc lightmap data offset for vertex lighting to use
                                iu = (int) u;
                                iv = (int) v;
-                               mesh->lightmapoffsets[i] = (bound(0, iv, tmax) * (smax+1) + bound(0, iu, smax)) * 3;
+                               (loadmodel->surfmesh.data_lightmapoffsets + surface->num_firstvertex)[i] = (bound(0, iv, tmax) * ssize + bound(0, iu, smax)) * 3;
                        }
                }
        }
 }
 
-static void Mod_Q1BSP_SetParent(mnode_t *node, mnode_t *parent)
+static void Mod_Q1BSP_LoadNodes_RecursiveSetParent(mnode_t *node, mnode_t *parent)
 {
+       //if (node->parent)
+       //      Host_Error("Mod_Q1BSP_LoadNodes_RecursiveSetParent: runaway recursion");
        node->parent = parent;
-       if (node->contents < 0)
-               return;
-       Mod_Q1BSP_SetParent(node->children[0], node);
-       Mod_Q1BSP_SetParent(node->children[1], node);
+       if (node->plane)
+       {
+               Mod_Q1BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
+               Mod_Q1BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
+       }
 }
 
 static void Mod_Q1BSP_LoadNodes(lump_t *l)
@@ -1765,14 +2241,14 @@ static void Mod_Q1BSP_LoadNodes(lump_t *l)
        dnode_t         *in;
        mnode_t         *out;
 
-       in = (void *)(mod_base + l->fileofs);
+       in = (dnode_t *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
                Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name);
        count = l->filelen / sizeof(*in);
-       out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
+       out = (mnode_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
 
-       loadmodel->brushq1.nodes = out;
-       loadmodel->brushq1.numnodes = count;
+       loadmodel->brush.data_nodes = out;
+       loadmodel->brush.num_nodes = count;
 
        for ( i=0 ; i<count ; i++, in++, out++)
        {
@@ -1783,7 +2259,7 @@ static void Mod_Q1BSP_LoadNodes(lump_t *l)
                }
 
                p = LittleLong(in->planenum);
-               out->plane = loadmodel->brushq1.planes + p;
+               out->plane = loadmodel->brush.data_planes + p;
 
                out->firstsurface = LittleShort(in->firstface);
                out->numsurfaces = LittleShort(in->numfaces);
@@ -1792,32 +2268,34 @@ static void Mod_Q1BSP_LoadNodes(lump_t *l)
                {
                        p = LittleShort(in->children[j]);
                        if (p >= 0)
-                               out->children[j] = loadmodel->brushq1.nodes + p;
+                               out->children[j] = loadmodel->brush.data_nodes + p;
                        else
-                               out->children[j] = (mnode_t *)(loadmodel->brushq1.leafs + (-1 - p));
+                               out->children[j] = (mnode_t *)(loadmodel->brush.data_leafs + (-1 - p));
                }
        }
 
-       Mod_Q1BSP_SetParent(loadmodel->brushq1.nodes, NULL);    // sets nodes and leafs
+       Mod_Q1BSP_LoadNodes_RecursiveSetParent(loadmodel->brush.data_nodes, NULL);      // sets nodes and leafs
 }
 
 static void Mod_Q1BSP_LoadLeafs(lump_t *l)
 {
        dleaf_t *in;
        mleaf_t *out;
-       int i, j, count, p, pvschainbytes;
-       qbyte *pvs;
+       int i, j, count, p;
 
-       in = (void *)(mod_base + l->fileofs);
+       in = (dleaf_t *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
                Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
        count = l->filelen / sizeof(*in);
-       out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
+       out = (mleaf_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
 
-       loadmodel->brushq1.leafs = out;
-       loadmodel->brushq1.numleafs = count;
-       pvschainbytes = ((loadmodel->brushq1.numleafs - 1)+7)>>3;
-       loadmodel->brushq1.data_decompressedpvs = pvs = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numleafs * pvschainbytes);
+       loadmodel->brush.data_leafs = out;
+       loadmodel->brush.num_leafs = count;
+       // get visleafs from the submodel data
+       loadmodel->brush.num_pvsclusters = loadmodel->brushq1.submodels[0].visleafs;
+       loadmodel->brush.num_pvsclusterbytes = (loadmodel->brush.num_pvsclusters+7)>>3;
+       loadmodel->brush.data_pvsclusters = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_pvsclusters * loadmodel->brush.num_pvsclusterbytes);
+       memset(loadmodel->brush.data_pvsclusters, 0xFF, loadmodel->brush.num_pvsclusters * loadmodel->brush.num_pvsclusterbytes);
 
        for ( i=0 ; i<count ; i++, in++, out++)
        {
@@ -1831,17 +2309,28 @@ static void Mod_Q1BSP_LoadLeafs(lump_t *l)
 
                out->contents = LittleLong(in->contents);
 
-               out->firstmarksurface = loadmodel->brushq1.marksurfaces + LittleShort(in->firstmarksurface);
-               out->nummarksurfaces = LittleShort(in->nummarksurfaces);
+               out->firstleafsurface = loadmodel->brush.data_leafsurfaces + LittleShort(in->firstmarksurface);
+               out->numleafsurfaces = LittleShort(in->nummarksurfaces);
+               if (out->firstleafsurface < 0 || LittleShort(in->firstmarksurface) + out->numleafsurfaces > loadmodel->brush.num_leafsurfaces)
+               {
+                       Con_Printf("Mod_Q1BSP_LoadLeafs: invalid leafsurface range %i:%i outside range %i:%i\n", out->firstleafsurface, out->firstleafsurface + out->numleafsurfaces, 0, loadmodel->brush.num_leafsurfaces);
+                       out->firstleafsurface = NULL;
+                       out->numleafsurfaces = 0;
+               }
 
-               out->pvsdata = pvs;
-               pvs += pvschainbytes;
+               out->clusterindex = i - 1;
+               if (out->clusterindex >= loadmodel->brush.num_pvsclusters)
+                       out->clusterindex = -1;
 
                p = LittleLong(in->visofs);
-               if (p >= 0)
-                       Mod_Q1BSP_DecompressVis(loadmodel->brushq1.data_compressedpvs + p, loadmodel->brushq1.data_compressedpvs + loadmodel->brushq1.num_compressedpvs, out->pvsdata, out->pvsdata + pvschainbytes);
-               else
-                       memset(out->pvsdata, 0xFF, pvschainbytes);
+               // ignore visofs errors on leaf 0 (solid)
+               if (p >= 0 && out->clusterindex >= 0)
+               {
+                       if (p >= loadmodel->brushq1.num_compressedpvs)
+                               Con_Print("Mod_Q1BSP_LoadLeafs: invalid visofs\n");
+                       else
+                               Mod_Q1BSP_DecompressVis(loadmodel->brushq1.data_compressedpvs + p, loadmodel->brushq1.data_compressedpvs + loadmodel->brushq1.num_compressedpvs, loadmodel->brush.data_pvsclusters + out->clusterindex * loadmodel->brush.num_pvsclusterbytes, loadmodel->brush.data_pvsclusters + (out->clusterindex + 1) * loadmodel->brush.num_pvsclusterbytes);
+               }
 
                for (j = 0;j < 4;j++)
                        out->ambient_sound_level[j] = in->ambient_level[j];
@@ -1850,88 +2339,34 @@ static void Mod_Q1BSP_LoadLeafs(lump_t *l)
        }
 }
 
-static void Mod_Q1BSP_LoadClipnodes(lump_t *l)
+static void Mod_Q1BSP_LoadClipnodes(lump_t *l, hullinfo_t *hullinfo)
 {
        dclipnode_t *in, *out;
        int                     i, count;
        hull_t          *hull;
 
-       in = (void *)(mod_base + l->fileofs);
+       in = (dclipnode_t *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
                Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
        count = l->filelen / sizeof(*in);
-       out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
+       out = (dclipnode_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
 
        loadmodel->brushq1.clipnodes = out;
        loadmodel->brushq1.numclipnodes = count;
 
-       if (loadmodel->brush.ishlbsp)
-       {
-               hull = &loadmodel->brushq1.hulls[1];
-               hull->clipnodes = out;
-               hull->firstclipnode = 0;
-               hull->lastclipnode = count-1;
-               hull->planes = loadmodel->brushq1.planes;
-               hull->clip_mins[0] = -16;
-               hull->clip_mins[1] = -16;
-               hull->clip_mins[2] = -36;
-               hull->clip_maxs[0] = 16;
-               hull->clip_maxs[1] = 16;
-               hull->clip_maxs[2] = 36;
-               VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
-
-               hull = &loadmodel->brushq1.hulls[2];
-               hull->clipnodes = out;
-               hull->firstclipnode = 0;
-               hull->lastclipnode = count-1;
-               hull->planes = loadmodel->brushq1.planes;
-               hull->clip_mins[0] = -32;
-               hull->clip_mins[1] = -32;
-               hull->clip_mins[2] = -32;
-               hull->clip_maxs[0] = 32;
-               hull->clip_maxs[1] = 32;
-               hull->clip_maxs[2] = 32;
-               VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
-
-               hull = &loadmodel->brushq1.hulls[3];
-               hull->clipnodes = out;
-               hull->firstclipnode = 0;
-               hull->lastclipnode = count-1;
-               hull->planes = loadmodel->brushq1.planes;
-               hull->clip_mins[0] = -16;
-               hull->clip_mins[1] = -16;
-               hull->clip_mins[2] = -18;
-               hull->clip_maxs[0] = 16;
-               hull->clip_maxs[1] = 16;
-               hull->clip_maxs[2] = 18;
-               VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
-       }
-       else
+       for (i = 1; i < hullinfo->numhulls; i++)
        {
-               hull = &loadmodel->brushq1.hulls[1];
-               hull->clipnodes = out;
-               hull->firstclipnode = 0;
-               hull->lastclipnode = count-1;
-               hull->planes = loadmodel->brushq1.planes;
-               hull->clip_mins[0] = -16;
-               hull->clip_mins[1] = -16;
-               hull->clip_mins[2] = -24;
-               hull->clip_maxs[0] = 16;
-               hull->clip_maxs[1] = 16;
-               hull->clip_maxs[2] = 32;
-               VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
-
-               hull = &loadmodel->brushq1.hulls[2];
+               hull = &loadmodel->brushq1.hulls[i];
                hull->clipnodes = out;
                hull->firstclipnode = 0;
                hull->lastclipnode = count-1;
-               hull->planes = loadmodel->brushq1.planes;
-               hull->clip_mins[0] = -32;
-               hull->clip_mins[1] = -32;
-               hull->clip_mins[2] = -24;
-               hull->clip_maxs[0] = 32;
-               hull->clip_maxs[1] = 32;
-               hull->clip_maxs[2] = 64;
+               hull->planes = loadmodel->brush.data_planes;
+               hull->clip_mins[0] = hullinfo->hullsizes[i][0][0];
+               hull->clip_mins[1] = hullinfo->hullsizes[i][0][1];
+               hull->clip_mins[2] = hullinfo->hullsizes[i][0][2];
+               hull->clip_maxs[0] = hullinfo->hullsizes[i][1][0];
+               hull->clip_maxs[1] = hullinfo->hullsizes[i][1][1];
+               hull->clip_maxs[2] = hullinfo->hullsizes[i][1][2];
                VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
        }
 
@@ -1940,8 +2375,10 @@ static void Mod_Q1BSP_LoadClipnodes(lump_t *l)
                out->planenum = LittleLong(in->planenum);
                out->children[0] = LittleShort(in->children[0]);
                out->children[1] = LittleShort(in->children[1]);
+               if (out->planenum < 0 || out->planenum >= loadmodel->brush.num_planes)
+                       Host_Error("Corrupt clipping hull(out of range planenum)");
                if (out->children[0] >= count || out->children[1] >= count)
-                       Host_Error("Corrupt clipping hull(out of range child)\n");
+                       Host_Error("Corrupt clipping hull(out of range child)");
        }
 }
 
@@ -1955,39 +2392,39 @@ static void Mod_Q1BSP_MakeHull0(void)
 
        hull = &loadmodel->brushq1.hulls[0];
 
-       in = loadmodel->brushq1.nodes;
-       out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numnodes * sizeof(dclipnode_t));
+       in = loadmodel->brush.data_nodes;
+       out = (dclipnode_t *)Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_nodes * sizeof(dclipnode_t));
 
        hull->clipnodes = out;
        hull->firstclipnode = 0;
-       hull->lastclipnode = loadmodel->brushq1.numnodes - 1;
-       hull->planes = loadmodel->brushq1.planes;
+       hull->lastclipnode = loadmodel->brush.num_nodes - 1;
+       hull->planes = loadmodel->brush.data_planes;
 
-       for (i = 0;i < loadmodel->brushq1.numnodes;i++, out++, in++)
+       for (i = 0;i < loadmodel->brush.num_nodes;i++, out++, in++)
        {
-               out->planenum = in->plane - loadmodel->brushq1.planes;
-               out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->brushq1.nodes;
-               out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->brushq1.nodes;
+               out->planenum = in->plane - loadmodel->brush.data_planes;
+               out->children[0] = in->children[0]->plane ? in->children[0] - loadmodel->brush.data_nodes : ((mleaf_t *)in->children[0])->contents;
+               out->children[1] = in->children[1]->plane ? in->children[1] - loadmodel->brush.data_nodes : ((mleaf_t *)in->children[1])->contents;
        }
 }
 
-static void Mod_Q1BSP_LoadMarksurfaces(lump_t *l)
+static void Mod_Q1BSP_LoadLeaffaces(lump_t *l)
 {
        int i, j;
        short *in;
 
-       in = (void *)(mod_base + l->fileofs);
+       in = (short *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
-               Host_Error("Mod_Q1BSP_LoadMarksurfaces: funny lump size in %s",loadmodel->name);
-       loadmodel->brushq1.nummarksurfaces = l->filelen / sizeof(*in);
-       loadmodel->brushq1.marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.nummarksurfaces * sizeof(int));
+               Host_Error("Mod_Q1BSP_LoadLeaffaces: funny lump size in %s",loadmodel->name);
+       loadmodel->brush.num_leafsurfaces = l->filelen / sizeof(*in);
+       loadmodel->brush.data_leafsurfaces = (int *)Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_leafsurfaces * sizeof(int));
 
-       for (i = 0;i < loadmodel->brushq1.nummarksurfaces;i++)
+       for (i = 0;i < loadmodel->brush.num_leafsurfaces;i++)
        {
                j = (unsigned) LittleShort(in[i]);
-               if (j >= loadmodel->brushq1.numsurfaces)
-                       Host_Error("Mod_Q1BSP_LoadMarksurfaces: bad surface number");
-               loadmodel->brushq1.marksurfaces[i] = j;
+               if (j >= loadmodel->num_surfaces)
+                       Host_Error("Mod_Q1BSP_LoadLeaffaces: bad surface number");
+               loadmodel->brush.data_leafsurfaces[i] = j;
        }
 }
 
@@ -1996,11 +2433,11 @@ static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
        int             i;
        int             *in;
 
-       in = (void *)(mod_base + l->fileofs);
+       in = (int *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
                Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
        loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in);
-       loadmodel->brushq1.surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
+       loadmodel->brushq1.surfedges = (int *)Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
 
        for (i = 0;i < loadmodel->brushq1.numsurfedges;i++)
                loadmodel->brushq1.surfedges[i] = LittleLong(in[i]);
@@ -2013,14 +2450,14 @@ static void Mod_Q1BSP_LoadPlanes(lump_t *l)
        mplane_t        *out;
        dplane_t        *in;
 
-       in = (void *)(mod_base + l->fileofs);
+       in = (dplane_t *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
                Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
 
-       loadmodel->brushq1.numplanes = l->filelen / sizeof(*in);
-       loadmodel->brushq1.planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numplanes * sizeof(*out));
+       loadmodel->brush.num_planes = l->filelen / sizeof(*in);
+       loadmodel->brush.data_planes = out = (mplane_t *)Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_planes * sizeof(*out));
 
-       for (i = 0;i < loadmodel->brushq1.numplanes;i++, in++, out++)
+       for (i = 0;i < loadmodel->brush.num_planes;i++, in++, out++)
        {
                out->normal[0] = LittleFloat(in->normal[0]);
                out->normal[1] = LittleFloat(in->normal[1]);
@@ -2031,12 +2468,111 @@ 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 = (unsigned char*) FS_LoadFile(mapfilename, tempmempool, false, NULL);
+       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
 {
        mplane_t plane;
        mnode_t *nodes[2];              // [0] = front side of plane
        struct portal_s *next[2];
-       winding_t *winding;
+       int numpoints;
+       double points[3*MAX_PORTALPOINTS];
        struct portal_s *chain; // all portals are linked into a list
 }
 portal_t;
@@ -2051,7 +2587,7 @@ AllocPortal
 static portal_t *AllocPortal(void)
 {
        portal_t *p;
-       p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
+       p = (portal_t *)Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
        p->chain = portalchain;
        portalchain = p;
        return p;
@@ -2064,11 +2600,13 @@ static void FreePortal(portal_t *p)
 
 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
 {
+       // process only nodes (leafs already had their box calculated)
+       if (!node->plane)
+               return;
+
        // calculate children first
-       if (node->children[0]->contents >= 0)
-               Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
-       if (node->children[1]->contents >= 0)
-               Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
+       Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
+       Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
 
        // make combined bounding box from children
        node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
@@ -2086,85 +2624,55 @@ static void Mod_Q1BSP_FinalizePortals(void)
        mportal_t *portal;
        mvertex_t *point;
        mleaf_t *leaf, *endleaf;
-       winding_t *w;
 
-       // recalculate bounding boxes for all leafs(because qbsp is very sloppy)
-       leaf = loadmodel->brushq1.leafs;
-       endleaf = leaf + loadmodel->brushq1.numleafs;
+       // tally up portal and point counts and recalculate bounding boxes for all
+       // leafs (because qbsp is very sloppy)
+       leaf = loadmodel->brush.data_leafs;
+       endleaf = leaf + loadmodel->brush.num_leafs;
        for (;leaf < endleaf;leaf++)
        {
                VectorSet(leaf->mins,  2000000000,  2000000000,  2000000000);
                VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
        }
        p = portalchain;
+       numportals = 0;
+       numpoints = 0;
        while (p)
        {
-               if (p->winding)
-               {
-                       for (i = 0;i < 2;i++)
-                       {
-                               leaf = (mleaf_t *)p->nodes[i];
-                               w = p->winding;
-                               for (j = 0;j < w->numpoints;j++)
-                               {
-                                       if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
-                                       if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
-                                       if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
-                                       if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
-                                       if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
-                                       if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
-                               }
-                       }
-               }
-               p = p->chain;
-       }
-
-       Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brushq1.nodes);
-
-       // tally up portal and point counts
-       p = portalchain;
-       numportals = 0;
-       numpoints = 0;
-       while (p)
-       {
-               // note: this check must match the one below or it will usually corrupt memory
-               // the nodes[0] != nodes[1] check is because leaf 0 is the shared solid leaf, it can have many portals inside with leaf 0 on both sides
-               if (p->winding && p->nodes[0] != p->nodes[1]
-                && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
-                && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
+               // note: this check must match the one below or it will usually corrupt memory
+               // the nodes[0] != nodes[1] check is because leaf 0 is the shared solid leaf, it can have many portals inside with leaf 0 on both sides
+               if (p->numpoints >= 3 && p->nodes[0] != p->nodes[1] && ((mleaf_t *)p->nodes[0])->clusterindex >= 0 && ((mleaf_t *)p->nodes[1])->clusterindex >= 0)
                {
                        numportals += 2;
-                       numpoints += p->winding->numpoints * 2;
+                       numpoints += p->numpoints * 2;
                }
                p = p->chain;
        }
-       loadmodel->brushq1.portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
-       loadmodel->brushq1.numportals = numportals;
-       loadmodel->brushq1.portalpoints = (void *)((qbyte *) loadmodel->brushq1.portals + numportals * sizeof(mportal_t));
-       loadmodel->brushq1.numportalpoints = numpoints;
+       loadmodel->brush.data_portals = (mportal_t *)Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
+       loadmodel->brush.num_portals = numportals;
+       loadmodel->brush.data_portalpoints = (mvertex_t *)((unsigned char *) loadmodel->brush.data_portals + numportals * sizeof(mportal_t));
+       loadmodel->brush.num_portalpoints = numpoints;
        // clear all leaf portal chains
-       for (i = 0;i < loadmodel->brushq1.numleafs;i++)
-               loadmodel->brushq1.leafs[i].portals = NULL;
+       for (i = 0;i < loadmodel->brush.num_leafs;i++)
+               loadmodel->brush.data_leafs[i].portals = NULL;
        // process all portals in the global portal chain, while freeing them
-       portal = loadmodel->brushq1.portals;
-       point = loadmodel->brushq1.portalpoints;
+       portal = loadmodel->brush.data_portals;
+       point = loadmodel->brush.data_portalpoints;
        p = portalchain;
        portalchain = NULL;
        while (p)
        {
                pnext = p->chain;
 
-               if (p->winding)
+               if (p->numpoints >= 3 && p->nodes[0] != p->nodes[1])
                {
                        // note: this check must match the one above or it will usually corrupt memory
                        // the nodes[0] != nodes[1] check is because leaf 0 is the shared solid leaf, it can have many portals inside with leaf 0 on both sides
-                       if (p->nodes[0] != p->nodes[1]
-                        && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
-                        && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
+                       if (((mleaf_t *)p->nodes[0])->clusterindex >= 0 && ((mleaf_t *)p->nodes[1])->clusterindex >= 0)
                        {
                                // first make the back to front portal(forward portal)
                                portal->points = point;
-                               portal->numpoints = p->winding->numpoints;
+                               portal->numpoints = p->numpoints;
                                portal->plane.dist = p->plane.dist;
                                VectorCopy(p->plane.normal, portal->plane.normal);
                                portal->here = (mleaf_t *)p->nodes[1];
@@ -2172,9 +2680,10 @@ static void Mod_Q1BSP_FinalizePortals(void)
                                // copy points
                                for (j = 0;j < portal->numpoints;j++)
                                {
-                                       VectorCopy(p->winding->points[j], point->position);
+                                       VectorCopy(p->points + j*3, point->position);
                                        point++;
                                }
+                               BoxFromPoints(portal->mins, portal->maxs, portal->numpoints, portal->points->position);
                                PlaneClassify(&portal->plane);
 
                                // link into leaf's portal chain
@@ -2186,7 +2695,7 @@ static void Mod_Q1BSP_FinalizePortals(void)
 
                                // then make the front to back portal(backward portal)
                                portal->points = point;
-                               portal->numpoints = p->winding->numpoints;
+                               portal->numpoints = p->numpoints;
                                portal->plane.dist = -p->plane.dist;
                                VectorNegate(p->plane.normal, portal->plane.normal);
                                portal->here = (mleaf_t *)p->nodes[0];
@@ -2194,9 +2703,10 @@ static void Mod_Q1BSP_FinalizePortals(void)
                                // copy points
                                for (j = portal->numpoints - 1;j >= 0;j--)
                                {
-                                       VectorCopy(p->winding->points[j], point->position);
+                                       VectorCopy(p->points + j*3, point->position);
                                        point++;
                                }
+                               BoxFromPoints(portal->mins, portal->maxs, portal->numpoints, portal->points->position);
                                PlaneClassify(&portal->plane);
 
                                // link into leaf's portal chain
@@ -2206,11 +2716,26 @@ static void Mod_Q1BSP_FinalizePortals(void)
                                // advance to next portal
                                portal++;
                        }
-                       Winding_Free(p->winding);
+                       // add the portal's polygon points to the leaf bounding boxes
+                       for (i = 0;i < 2;i++)
+                       {
+                               leaf = (mleaf_t *)p->nodes[i];
+                               for (j = 0;j < p->numpoints;j++)
+                               {
+                                       if (leaf->mins[0] > p->points[j*3+0]) leaf->mins[0] = p->points[j*3+0];
+                                       if (leaf->mins[1] > p->points[j*3+1]) leaf->mins[1] = p->points[j*3+1];
+                                       if (leaf->mins[2] > p->points[j*3+2]) leaf->mins[2] = p->points[j*3+2];
+                                       if (leaf->maxs[0] < p->points[j*3+0]) leaf->maxs[0] = p->points[j*3+0];
+                                       if (leaf->maxs[1] < p->points[j*3+1]) leaf->maxs[1] = p->points[j*3+1];
+                                       if (leaf->maxs[2] < p->points[j*3+2]) leaf->maxs[2] = p->points[j*3+2];
+                               }
+                       }
                }
                FreePortal(p);
                p = pnext;
        }
+       // now recalculate the node bounding boxes from the leafs
+       Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brush.data_nodes);
 }
 
 /*
@@ -2255,7 +2780,7 @@ static void RemovePortalFromNodes(portal_t *portal)
                portalpointer = (void **) &node->portals;
                while (1)
                {
-                       t = *portalpointer;
+                       t = (portal_t *)*portalpointer;
                        if (!t)
                                Host_Error("RemovePortalFromNodes: portal not in leaf");
 
@@ -2288,14 +2813,15 @@ static void RemovePortalFromNodes(portal_t *portal)
 
 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
 {
-       int side;
+       int i, side;
        mnode_t *front, *back, *other_node;
        mplane_t clipplane, *plane;
        portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
-       winding_t *nodeportalwinding, *frontwinding, *backwinding;
+       int numfrontpoints, numbackpoints;
+       double frontpoints[3*MAX_PORTALPOINTS], backpoints[3*MAX_PORTALPOINTS];
 
        // if a leaf, we're done
-       if (node->contents)
+       if (!node->plane)
                return;
 
        plane = node->plane;
@@ -2310,7 +2836,8 @@ static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
        nodeportal = AllocPortal();
        nodeportal->plane = *plane;
 
-       nodeportalwinding = Winding_NewFromPlane(nodeportal->plane.normal[0], nodeportal->plane.normal[1], nodeportal->plane.normal[2], nodeportal->plane.dist);
+       PolygonD_QuadForPlane(nodeportal->points, nodeportal->plane.normal[0], nodeportal->plane.normal[1], nodeportal->plane.normal[2], nodeportal->plane.dist, 1024.0*1024.0*1024.0);
+       nodeportal->numpoints = 4;
        side = 0;       // shut up compiler warning
        for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
        {
@@ -2328,21 +2855,26 @@ static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
                else
                        Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
 
-               nodeportalwinding = Winding_Clip(nodeportalwinding, clipplane.normal[0], clipplane.normal[1], clipplane.normal[2], clipplane.dist, true);
-               if (!nodeportalwinding)
-               {
-                       Con_Printf("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
+               for (i = 0;i < nodeportal->numpoints*3;i++)
+                       frontpoints[i] = nodeportal->points[i];
+               PolygonD_Divide(nodeportal->numpoints, frontpoints, clipplane.normal[0], clipplane.normal[1], clipplane.normal[2], clipplane.dist, 1.0/32.0, MAX_PORTALPOINTS, nodeportal->points, &nodeportal->numpoints, 0, NULL, NULL, NULL);
+               if (nodeportal->numpoints <= 0 || nodeportal->numpoints >= MAX_PORTALPOINTS)
                        break;
-               }
        }
 
-       if (nodeportalwinding)
+       if (nodeportal->numpoints < 3)
        {
-               // if the plane was not clipped on all sides, there was an error
-               nodeportal->winding = nodeportalwinding;
-               AddPortalToNodes(nodeportal, front, back);
+               Con_Print("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
+               nodeportal->numpoints = 0;
+       }
+       else if (nodeportal->numpoints >= MAX_PORTALPOINTS)
+       {
+               Con_Print("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal has too many points\n");
+               nodeportal->numpoints = 0;
        }
 
+       AddPortalToNodes(nodeportal, front, back);
+
        // split the portals of this node along this node's plane and assign them to the children of this node
        // (migrating the portals downward through the tree)
        for (portal = (portal_t *)node->portals;portal;portal = nextportal)
@@ -2356,14 +2888,16 @@ static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
                else
                        Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
                nextportal = portal->next[side];
+               if (!portal->numpoints)
+                       continue;
 
                other_node = portal->nodes[!side];
                RemovePortalFromNodes(portal);
 
                // cut the portal into two portals, one on each side of the node plane
-               Winding_Divide(portal->winding, plane->normal[0], plane->normal[1], plane->normal[2], plane->dist, &frontwinding, &backwinding);
+               PolygonD_Divide(portal->numpoints, portal->points, plane->normal[0], plane->normal[1], plane->normal[2], plane->dist, 1.0/32.0, MAX_PORTALPOINTS, frontpoints, &numfrontpoints, MAX_PORTALPOINTS, backpoints, &numbackpoints, NULL);
 
-               if (!frontwinding)
+               if (!numfrontpoints)
                {
                        if (side == 0)
                                AddPortalToNodes(portal, back, other_node);
@@ -2371,7 +2905,7 @@ static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
                                AddPortalToNodes(portal, other_node, back);
                        continue;
                }
-               if (!backwinding)
+               if (!numbackpoints)
                {
                        if (side == 0)
                                AddPortalToNodes(portal, front, other_node);
@@ -2380,14 +2914,17 @@ static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
                        continue;
                }
 
-               // the winding is split
+               // the portal is split
                splitportal = AllocPortal();
                temp = splitportal->chain;
                *splitportal = *portal;
                splitportal->chain = temp;
-               splitportal->winding = backwinding;
-               Winding_Free(portal->winding);
-               portal->winding = frontwinding;
+               for (i = 0;i < numbackpoints*3;i++)
+                       splitportal->points[i] = backpoints[i];
+               splitportal->numpoints = numbackpoints;
+               for (i = 0;i < numfrontpoints*3;i++)
+                       portal->points[i] = frontpoints[i];
+               portal->numpoints = numfrontpoints;
 
                if (side == 0)
                {
@@ -2408,66 +2945,20 @@ static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
 static void Mod_Q1BSP_MakePortals(void)
 {
        portalchain = NULL;
-       Mod_Q1BSP_RecursiveNodePortals(loadmodel->brushq1.nodes);
+       Mod_Q1BSP_RecursiveNodePortals(loadmodel->brush.data_nodes);
        Mod_Q1BSP_FinalizePortals();
 }
 
-static void Mod_Q1BSP_BuildSurfaceNeighbors(msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
-{
-#if 0
-       int surfnum, vertnum, vertnum2, snum, vnum, vnum2;
-       msurface_t *surf, *s;
-       float *v0, *v1, *v2, *v3;
-       for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
-               surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
-       for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
-       {
-               for (vertnum = surf->poly_numverts - 1, vertnum2 = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = surf->poly_verts;vertnum2 < surf->poly_numverts;vertnum = vertnum2, vertnum2++, v0 = v1, v1 += 3)
-               {
-                       if (surf->neighborsurfaces[vertnum])
-                               continue;
-                       surf->neighborsurfaces[vertnum] = NULL;
-                       for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
-                       {
-                               if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1)
-                                || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1)
-                                || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
-                                || s == surf)
-                                       continue;
-                               for (vnum = 0;vnum < s->poly_numverts;vnum++)
-                                       if (s->neighborsurfaces[vnum] == surf)
-                                               break;
-                               if (vnum < s->poly_numverts)
-                                       continue;
-                               for (vnum = s->poly_numverts - 1, vnum2 = 0, v2 = s->poly_verts + (s->poly_numverts - 1) * 3, v3 = s->poly_verts;vnum2 < s->poly_numverts;vnum = vnum2, vnum2++, v2 = v3, v3 += 3)
-                               {
-                                       if (s->neighborsurfaces[vnum] == NULL
-                                        && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
-                                         || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2])))
-                                       {
-                                               surf->neighborsurfaces[vertnum] = s;
-                                               s->neighborsurfaces[vnum] = surf;
-                                               break;
-                                       }
-                               }
-                               if (vnum < s->poly_numverts)
-                                       break;
-                       }
-               }
-       }
-#endif
-}
-
 static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
 {
        int i, j, stylecounts[256], totalcount, remapstyles[256];
-       msurface_t *surf;
+       msurface_t *surface;
        memset(stylecounts, 0, sizeof(stylecounts));
-       for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
+       for (i = 0;i < model->nummodelsurfaces;i++)
        {
-               surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
+               surface = model->data_surfaces + model->firstmodelsurface + i;
                for (j = 0;j < MAXLIGHTMAPS;j++)
-                       stylecounts[surf->styles[j]]++;
+                       stylecounts[surface->lightmapinfo->styles[j]]++;
        }
        totalcount = 0;
        model->brushq1.light_styles = 0;
@@ -2481,10 +2972,10 @@ static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *mod
        }
        if (!totalcount)
                return;
-       model->brushq1.light_style = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(qbyte));
-       model->brushq1.light_stylevalue = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int));
-       model->brushq1.light_styleupdatechains = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **));
-       model->brushq1.light_styleupdatechainsbuffer = Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
+       model->brushq1.light_style = (unsigned char *)Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(unsigned char));
+       model->brushq1.light_stylevalue = (int *)Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int));
+       model->brushq1.light_styleupdatechains = (msurface_t ***)Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **));
+       model->brushq1.light_styleupdatechainsbuffer = (msurface_t **)Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
        model->brushq1.light_styles = 0;
        for (i = 0;i < 255;i++)
                if (stylecounts[i])
@@ -2495,12 +2986,12 @@ static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *mod
                model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
                j += stylecounts[model->brushq1.light_style[i]] + 1;
        }
-       for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
+       for (i = 0;i < model->nummodelsurfaces;i++)
        {
-               surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
+               surface = model->data_surfaces + model->firstmodelsurface + i;
                for (j = 0;j < MAXLIGHTMAPS;j++)
-                       if (surf->styles[j] != 255)
-                               *model->brushq1.light_styleupdatechains[remapstyles[surf->styles[j]]]++ = surf;
+                       if (surface->lightmapinfo->styles[j] != 255)
+                               *model->brushq1.light_styleupdatechains[remapstyles[surface->lightmapinfo->styles[j]]]++ = surface;
        }
        j = 0;
        for (i = 0;i < model->brushq1.light_styles;i++)
@@ -2511,50 +3002,25 @@ static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *mod
        }
 }
 
-static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model)
+//Returns PVS data for a given point
+//(note: can return NULL)
+static unsigned char *Mod_Q1BSP_GetPVS(model_t *model, const vec3_t p)
 {
-       int i, j;
-       for (i = 0;i < model->brushq1.numtextures;i++)
-               model->brushq1.pvstexturechainslength[i] = 0;
-       for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
-       {
-               if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
-               {
-                       model->brushq1.pvssurflist[model->brushq1.pvssurflistlength++] = j;
-                       model->brushq1.pvstexturechainslength[model->brushq1.surfaces[j].texinfo->texture->number]++;
-               }
-       }
-       for (i = 0, j = 0;i < model->brushq1.numtextures;i++)
-       {
-               if (model->brushq1.pvstexturechainslength[i])
-               {
-                       model->brushq1.pvstexturechains[i] = model->brushq1.pvstexturechainsbuffer + j;
-                       j += model->brushq1.pvstexturechainslength[i] + 1;
-               }
-               else
-                       model->brushq1.pvstexturechains[i] = NULL;
-       }
-       for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
-               if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
-                       *model->brushq1.pvstexturechains[model->brushq1.surfaces[j].texinfo->texture->number]++ = model->brushq1.surfaces + j;
-       for (i = 0;i < model->brushq1.numtextures;i++)
-       {
-               if (model->brushq1.pvstexturechainslength[i])
-               {
-                       *model->brushq1.pvstexturechains[i] = NULL;
-                       model->brushq1.pvstexturechains[i] -= model->brushq1.pvstexturechainslength[i];
-               }
-       }
+       mnode_t *node;
+       node = model->brush.data_nodes;
+       while (node->plane)
+               node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
+       if (((mleaf_t *)node)->clusterindex >= 0)
+               return model->brush.data_pvsclusters + ((mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes;
+       else
+               return NULL;
 }
 
-static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, mnode_t *node)
+static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, unsigned char *pvsbuffer, int pvsbytes, mnode_t *node)
 {
-       int i;
-       float d;
-
-       while (node->contents >= 0)
+       while (node->plane)
        {
-               d = PlaneDiff(org, node->plane);
+               float d = PlaneDiff(org, node->plane);
                if (d > radius)
                        node = node->children[0];
                else if (d < -radius)
@@ -2566,45 +3032,48 @@ static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org,
                        node = node->children[1];
                }
        }
-       // FIXME: code!
-       // if this is a leaf, accumulate the pvs bits
-       if (node->contents != CONTENTS_SOLID && ((mleaf_t *)node)->pvsdata)
+       // if this leaf is in a cluster, accumulate the pvs bits
+       if (((mleaf_t *)node)->clusterindex >= 0)
+       {
+               int i;
+               unsigned char *pvs = model->brush.data_pvsclusters + ((mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes;
                for (i = 0;i < pvsbytes;i++)
-                       pvsbuffer[i] |= ((mleaf_t *)node)->pvsdata[i];
+                       pvsbuffer[i] |= pvs[i];
+       }
 }
 
 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
 //of the given point.
-static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
+static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, unsigned char *pvsbuffer, int pvsbufferlength)
 {
-       int bytes = ((model->brushq1.numleafs - 1) + 7) >> 3;
+       int bytes = model->brush.num_pvsclusterbytes;
        bytes = min(bytes, pvsbufferlength);
+       if (r_novis.integer || !model->brush.num_pvsclusters || !Mod_Q1BSP_GetPVS(model, org))
+       {
+               memset(pvsbuffer, 0xFF, bytes);
+               return bytes;
+       }
        memset(pvsbuffer, 0, bytes);
-       Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq1.nodes);
+       Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brush.data_nodes);
        return bytes;
 }
 
-//Returns PVS data for a given point
-//(note: always returns valid data, never NULL)
-static qbyte *Mod_Q1BSP_GetPVS(model_t *model, const vec3_t p)
-{
-       mnode_t *node;
-       Mod_CheckLoaded(model);
-       // LordHavoc: modified to start at first clip node,
-       // in other words: first node of the (sub)model
-       node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
-       while (node->contents == 0)
-               node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
-       return ((mleaf_t *)node)->pvsdata;
-}
-
 static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, const vec3_t inmaxs, vec3_t outmins, vec3_t outmaxs)
 {
        vec3_t size;
        const hull_t *hull;
 
        VectorSubtract(inmaxs, inmins, size);
-       if (cmodel->brush.ishlbsp)
+       if (cmodel->brush.ismcbsp)
+       {
+               if (size[0] < 3)
+                       hull = &cmodel->brushq1.hulls[0]; // 0x0x0
+               else if (size[2] < 48) // pick the nearest of 40 or 56
+                       hull = &cmodel->brushq1.hulls[2]; // 16x16x40
+               else
+                       hull = &cmodel->brushq1.hulls[1]; // 16x16x56
+       }
+       else if (cmodel->brush.ishlbsp)
        {
                if (size[0] < 3)
                        hull = &cmodel->brushq1.hulls[0]; // 0x0x0
@@ -2631,62 +3100,142 @@ static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, co
        VectorAdd(inmins, hull->clip_size, outmaxs);
 }
 
-extern void R_Model_Brush_DrawSky(entity_render_t *ent);
-extern void R_Model_Brush_Draw(entity_render_t *ent);
-extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
-extern void R_Model_Brush_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz);
-void Mod_Q1BSP_Load(model_t *mod, void *buffer)
+void Mod_Q1BSP_Load(model_t *mod, void *buffer, void *bufferend)
 {
        int i, j, k;
        dheader_t *header;
        dmodel_t *bm;
        mempool_t *mainmempool;
-       char *loadname;
-       model_t *originalloadmodel;
        float dist, modelyawradius, modelradius, *vec;
-       msurface_t *surf;
-       surfmesh_t *mesh;
+       msurface_t *surface;
+       int numshadowmeshtriangles;
+       dheader_t _header;
+       hullinfo_t hullinfo;
 
-       mod->type = mod_brush;
+       mod->type = mod_brushq1;
 
-       header = (dheader_t *)buffer;
+       if (!memcmp (buffer, "MCBSPpad", 8))
+       {
+               unsigned char   *index;
 
-       i = LittleLong(header->version);
-       if (i != BSPVERSION && i != 30)
-               Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION);
-       mod->brush.ishlbsp = i == 30;
+               mod->brush.ismcbsp = true;
+               mod->brush.ishlbsp = false;
+
+               mod_base = (unsigned char*)buffer;
+
+               index = mod_base;
+               index += 8;
+               i = SB_ReadInt (&index);
+               if (i != MCBSPVERSION)
+                       Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i)", mod->name, i, MCBSPVERSION);
+
+       // read hull info
+               hullinfo.numhulls = LittleLong(*(int*)index); index += 4;
+               hullinfo.filehulls = hullinfo.numhulls;
+               VectorClear (hullinfo.hullsizes[0][0]);
+               VectorClear (hullinfo.hullsizes[0][1]);
+               for (i = 1; i < hullinfo.numhulls; i++)
+               {
+                       hullinfo.hullsizes[i][0][0] = SB_ReadFloat (&index);
+                       hullinfo.hullsizes[i][0][1] = SB_ReadFloat (&index);
+                       hullinfo.hullsizes[i][0][2] = SB_ReadFloat (&index);
+                       hullinfo.hullsizes[i][1][0] = SB_ReadFloat (&index);
+                       hullinfo.hullsizes[i][1][1] = SB_ReadFloat (&index);
+                       hullinfo.hullsizes[i][1][2] = SB_ReadFloat (&index);
+               }
+
+       // read lumps
+               _header.version = 0;
+               for (i = 0; i < HEADER_LUMPS; i++)
+               {
+                       _header.lumps[i].fileofs = SB_ReadInt (&index);
+                       _header.lumps[i].filelen = SB_ReadInt (&index);
+               }
+
+               header = &_header;
+       }
+       else
+       {
+               header = (dheader_t *)buffer;
+
+               i = LittleLong(header->version);
+               if (i != BSPVERSION && i != 30)
+                       Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife)", mod->name, i, BSPVERSION);
+               mod->brush.ishlbsp = i == 30;
+               mod->brush.ismcbsp = false;
+
+       // fill in hull info
+               VectorClear (hullinfo.hullsizes[0][0]);
+               VectorClear (hullinfo.hullsizes[0][1]);
+               if (mod->brush.ishlbsp)
+               {
+                       hullinfo.numhulls = 4;
+                       hullinfo.filehulls = 4;
+                       VectorSet (hullinfo.hullsizes[1][0], -16, -16, -36);
+                       VectorSet (hullinfo.hullsizes[1][1], 16, 16, 36);
+                       VectorSet (hullinfo.hullsizes[2][0], -32, -32, -32);
+                       VectorSet (hullinfo.hullsizes[2][1], 32, 32, 32);
+                       VectorSet (hullinfo.hullsizes[3][0], -16, -16, -18);
+                       VectorSet (hullinfo.hullsizes[3][1], 16, 16, 18);
+               }
+               else
+               {
+                       hullinfo.numhulls = 3;
+                       hullinfo.filehulls = 4;
+                       VectorSet (hullinfo.hullsizes[1][0], -16, -16, -24);
+                       VectorSet (hullinfo.hullsizes[1][1], 16, 16, 32);
+                       VectorSet (hullinfo.hullsizes[2][0], -32, -32, -24);
+                       VectorSet (hullinfo.hullsizes[2][1], 32, 32, 64);
+               }
+
+       // read lumps
+               mod_base = (unsigned char*)buffer;
+               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);
+               }
+       }
 
+       mod->soundfromcenter = true;
+       mod->TraceBox = Mod_Q1BSP_TraceBox;
        mod->brush.SuperContentsFromNativeContents = Mod_Q1BSP_SuperContentsFromNativeContents;
        mod->brush.NativeContentsFromSuperContents = Mod_Q1BSP_NativeContentsFromSuperContents;
        mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
        mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
        mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
+       mod->brush.BoxTouchingLeafPVS = Mod_Q1BSP_BoxTouchingLeafPVS;
+       mod->brush.BoxTouchingVisibleLeafs = Mod_Q1BSP_BoxTouchingVisibleLeafs;
+       mod->brush.FindBoxClusters = Mod_Q1BSP_FindBoxClusters;
        mod->brush.LightPoint = Mod_Q1BSP_LightPoint;
        mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
-       mod->brush.TraceBox = Mod_Q1BSP_TraceBox;
        mod->brush.AmbientSoundLevelsForPoint = Mod_Q1BSP_AmbientSoundLevelsForPoint;
        mod->brush.RoundUpToHullSize = Mod_Q1BSP_RoundUpToHullSize;
-       mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
-       mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
+       mod->brush.PointInLeaf = Mod_Q1BSP_PointInLeaf;
 
        if (loadmodel->isworldmodel)
        {
                Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
-               // until we get a texture for it...
-               R_ResetQuakeSky();
+               Cvar_SetValue("mcbsp", mod->brush.ismcbsp);
        }
 
-// 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]);
-
 // load into heap
 
        // store which lightmap format to use
        mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
 
+       mod->brush.qw_md4sum = 0;
+       mod->brush.qw_md4sum2 = 0;
+       for (i = 0;i < HEADER_LUMPS;i++)
+       {
+               if (i == LUMP_ENTITIES)
+                       continue;
+               mod->brush.qw_md4sum ^= LittleLong(Com_BlockChecksum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen));
+               if (i == LUMP_VISIBILITY || i == LUMP_LEAFS || i == LUMP_NODES)
+                       continue;
+               mod->brush.qw_md4sum2 ^= LittleLong(Com_BlockChecksum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen));
+       }
+
        Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
        Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
        Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
@@ -2696,12 +3245,16 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer)
        Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
        Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
        Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
-       Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]);
+       Mod_Q1BSP_LoadLeaffaces(&header->lumps[LUMP_MARKSURFACES]);
        Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
+       // load submodels before leafs because they contain the number of vis leafs
+       Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS], &hullinfo);
        Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
        Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
-       Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
-       Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
+       Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES], &hullinfo);
+
+       if (!mod->brushq1.lightdata)
+               mod->brush.LightPoint = NULL;
 
        if (mod->brushq1.data_compressedpvs)
                Mem_Free(mod->brushq1.data_compressedpvs);
@@ -2711,22 +3264,79 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer)
        Mod_Q1BSP_MakeHull0();
        Mod_Q1BSP_MakePortals();
 
-       if (developer.integer)
-               Con_Printf("Some stats for q1bsp model \"%s\": %i faces, %i nodes, %i leafs, %i visleafs, %i visleafportals\n", loadmodel->name, loadmodel->brushq1.numsurfaces, loadmodel->brushq1.numnodes, loadmodel->brushq1.numleafs, loadmodel->brushq1.numleafs - 1, loadmodel->brushq1.numportals);
-
        mod->numframes = 2;             // regular and alternate animation
+       mod->numskins = 1;
 
        mainmempool = mod->mempool;
-       loadname = mod->name;
 
-       Mod_Q1BSP_LoadLightList();
-       originalloadmodel = loadmodel;
+       // make a single combined shadow mesh to allow optimized shadow volume creation
+       numshadowmeshtriangles = 0;
+       for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
+       {
+               surface->num_firstshadowmeshtriangle = numshadowmeshtriangles;
+               numshadowmeshtriangles += surface->num_triangles;
+       }
+       loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true);
+       for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
+               Mod_ShadowMesh_AddMesh(loadmodel->mempool, loadmodel->brush.shadowmesh, NULL, NULL, NULL, loadmodel->surfmesh.data_vertex3f, NULL, NULL, NULL, NULL, surface->num_triangles, (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
+       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 = (model_t **)Mem_Alloc(loadmodel->mempool, loadmodel->brush.numsubmodels * sizeof(model_t *));
+
+       if (loadmodel->isworldmodel)
+       {
+               // clear out any stale submodels or worldmodels lying around
+               // if we did this clear before now, an error might abort loading and
+               // leave things in a bad state
+               Mod_RemoveStaleWorldModels(loadmodel);
+       }
 
-//
-// set up the submodels(FIXME: this is confusing)
-//
+       // LordHavoc: to clear the fog around the original quake submodel code, I
+       // will explain:
+       // first of all, some background info on the submodels:
+       // model 0 is the map model (the world, named maps/e1m1.bsp for example)
+       // model 1 and higher are submodels (doors and the like, named *1, *2, etc)
+       // now the weird for loop itself:
+       // the loop functions in an odd way, on each iteration it sets up the
+       // current 'mod' model (which despite the confusing code IS the model of
+       // the number i), at the end of the loop it duplicates the model to become
+       // the next submodel, and loops back to set up the new submodel.
+
+       // LordHavoc: now the explanation of my sane way (which works identically):
+       // set up the world model, then on each submodel copy from the world model
+       // and set up the submodel with the respective model info.
        for (i = 0;i < mod->brush.numsubmodels;i++)
        {
+               // LordHavoc: this code was originally at the end of this loop, but
+               // has been transformed to something more readable at the start here.
+
+               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);
+                       // copy the base model to this one
+                       *mod = *loadmodel;
+                       // rename the clone back to its proper name
+                       strcpy(mod->name, name);
+                       // textures and memory belong to the main model
+                       mod->texturepool = NULL;
+                       mod->mempool = NULL;
+               }
+
+               mod->brush.submodel = i;
+
+               if (loadmodel->brush.submodels)
+                       loadmodel->brush.submodels[i] = mod;
+
                bm = &mod->brushq1.submodels[i];
 
                mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
@@ -2736,53 +3346,60 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer)
                        mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
                }
 
-               mod->brushq1.firstmodelsurface = bm->firstface;
-               mod->brushq1.nummodelsurfaces = bm->numfaces;
+               mod->firstmodelsurface = bm->firstface;
+               mod->nummodelsurfaces = bm->numfaces;
+
+               // make the model surface list (used by shadowing/lighting)
+               mod->surfacelist = (int *)Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->surfacelist));
+               for (j = 0;j < mod->nummodelsurfaces;j++)
+                       mod->surfacelist[j] = mod->firstmodelsurface + j;
 
                // this gets altered below if sky is used
                mod->DrawSky = NULL;
-               mod->Draw = R_Model_Brush_Draw;
-               mod->DrawFakeShadow = NULL;
-               mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
-               mod->DrawLight = R_Model_Brush_DrawLight;
-               mod->brushq1.pvstexturechains = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **));
-               mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(originalloadmodel->mempool,(mod->brushq1.nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *));
-               mod->brushq1.pvstexturechainslength = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(int));
-               Mod_Q1BSP_BuildPVSTextureChains(mod);
-               Mod_Q1BSP_BuildLightmapUpdateChains(originalloadmodel->mempool, mod);
-               if (mod->brushq1.nummodelsurfaces)
+               mod->Draw = R_Q1BSP_Draw;
+               mod->GetLightInfo = R_Q1BSP_GetLightInfo;
+               mod->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
+               mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
+               mod->DrawLight = R_Q1BSP_DrawLight;
+               if (i != 0)
+               {
+                       mod->brush.GetPVS = NULL;
+                       mod->brush.FatPVS = NULL;
+                       mod->brush.BoxTouchingPVS = NULL;
+                       mod->brush.BoxTouchingLeafPVS = NULL;
+                       mod->brush.BoxTouchingVisibleLeafs = NULL;
+                       mod->brush.FindBoxClusters = NULL;
+                       mod->brush.LightPoint = NULL;
+                       mod->brush.AmbientSoundLevelsForPoint = NULL;
+               }
+               Mod_Q1BSP_BuildLightmapUpdateChains(loadmodel->mempool, mod);
+               if (mod->nummodelsurfaces)
                {
                        // LordHavoc: calculate bmodel bounding box rather than trusting what it says
                        mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
                        mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
                        modelyawradius = 0;
                        modelradius = 0;
-                       for (j = 0, surf = &mod->brushq1.surfaces[mod->brushq1.firstmodelsurface];j < mod->brushq1.nummodelsurfaces;j++, surf++)
+                       for (j = 0, surface = &mod->data_surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surface++)
                        {
                                // we only need to have a drawsky function if it is used(usually only on world model)
-                               if (surf->texinfo->texture->shader == &Cshader_sky)
-                                       mod->DrawSky = R_Model_Brush_DrawSky;
-                               // LordHavoc: submodels always clip, even if water
-                               if (mod->brush.numsubmodels - 1)
-                                       surf->flags |= SURF_SOLIDCLIP;
+                               if (surface->texture->basematerialflags & MATERIALFLAG_SKY)
+                                       mod->DrawSky = R_Q1BSP_DrawSky;
                                // calculate bounding shapes
-                               for (mesh = surf->mesh;mesh;mesh = mesh->chain)
+                               for (k = 0, vec = (loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex);k < surface->num_vertices;k++, vec += 3)
                                {
-                                       for (k = 0, vec = mesh->vertex3f;k < mesh->numverts;k++, vec += 3)
-                                       {
-                                               if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
-                                               if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
-                                               if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
-                                               if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
-                                               if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
-                                               if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
-                                               dist = vec[0]*vec[0]+vec[1]*vec[1];
-                                               if (modelyawradius < dist)
-                                                       modelyawradius = dist;
-                                               dist += vec[2]*vec[2];
-                                               if (modelradius < dist)
-                                                       modelradius = dist;
-                                       }
+                                       if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
+                                       if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
+                                       if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
+                                       if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
+                                       if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
+                                       if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
+                                       dist = vec[0]*vec[0]+vec[1]*vec[1];
+                                       if (modelyawradius < dist)
+                                               modelyawradius = dist;
+                                       dist += vec[2]*vec[2];
+                                       if (modelradius < dist)
+                                               modelradius = dist;
                                }
                        }
                        modelyawradius = sqrt(modelyawradius);
@@ -2798,31 +3415,17 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer)
                else
                {
                        // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
-                       Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
-               }
-               Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->brushq1.firstmodelsurface, mod->brushq1.nummodelsurfaces, originalloadmodel->mempool);
-
-               mod->brushq1.visleafs = bm->visleafs;
-
-               // LordHavoc: only register submodels if it is the world
-               // (prevents bsp models from replacing world submodels)
-               if (loadmodel->isworldmodel && i < (mod->brush.numsubmodels - 1))
-               {
-                       char    name[10];
-                       // duplicate the basic information
-                       sprintf(name, "*%i", i+1);
-                       loadmodel = Mod_FindName(name);
-                       *loadmodel = *mod;
-                       strcpy(loadmodel->name, name);
-                       // textures and memory belong to the main model
-                       loadmodel->texturepool = NULL;
-                       loadmodel->mempool = NULL;
-                       mod = loadmodel;
+                       Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadmodel->name);
                }
+               //mod->brushq1.num_visleafs = bm->visleafs;
        }
 
-       loadmodel = originalloadmodel;
+       Mod_Q1BSP_LoadMapBrushes();
+
        //Mod_Q1BSP_ProcessLightList();
+
+       if (developer.integer >= 10)
+               Con_Printf("Some stats for q1bsp model \"%s\": %i faces, %i nodes, %i leafs, %i visleafs, %i visleafportals\n", loadmodel->name, loadmodel->num_surfaces, loadmodel->brush.num_nodes, loadmodel->brush.num_leafs, mod->brush.num_pvsclusters, loadmodel->brush.num_portals);
 }
 
 static void Mod_Q2BSP_LoadEntities(lump_t *l)
@@ -3203,12 +3806,12 @@ static void Mod_Q2BSP_LoadModels(lump_t *l)
 */
 }
 
-void static Mod_Q2BSP_Load(model_t *mod, void *buffer)
+void static Mod_Q2BSP_Load(model_t *mod, void *buffer, void *bufferend)
 {
        int i;
        q2dheader_t *header;
 
-       Host_Error("Mod_Q2BSP_Load: not yet implemented\n");
+       Host_Error("Mod_Q2BSP_Load: not yet implemented");
 
        mod->type = mod_brushq2;
 
@@ -3218,14 +3821,14 @@ void static Mod_Q2BSP_Load(model_t *mod, void *buffer)
        if (i != Q2BSPVERSION)
                Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
        mod->brush.ishlbsp = false;
+       mod->brush.ismcbsp = false;
        if (loadmodel->isworldmodel)
        {
                Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
-               // until we get a texture for it...
-               R_ResetQuakeSky();
+               Cvar_SetValue("mcbsp", mod->brush.ismcbsp);
        }
 
-       mod_base = (qbyte *)header;
+       mod_base = (unsigned char *)header;
 
        // swap all the lumps
        for (i = 0;i < (int) sizeof(*header) / 4;i++)
@@ -3234,6 +3837,18 @@ void static Mod_Q2BSP_Load(model_t *mod, void *buffer)
        // store which lightmap format to use
        mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
 
+       mod->brush.qw_md4sum = 0;
+       mod->brush.qw_md4sum2 = 0;
+       for (i = 0;i < Q2HEADER_LUMPS;i++)
+       {
+               if (i == Q2LUMP_ENTITIES)
+                       continue;
+               mod->brush.qw_md4sum ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen);
+               if (i == Q2LUMP_VISIBILITY || i == Q2LUMP_LEAFS || i == Q2LUMP_NODES)
+                       continue;
+               mod->brush.qw_md4sum2 ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen);
+       }
+
        Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
        Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
        Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
@@ -3261,14 +3876,14 @@ static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int superco
 static void Mod_Q3BSP_LoadEntities(lump_t *l)
 {
        const char *data;
-       char key[128], value[4096];
+       char key[128], value[MAX_INPUTLINE];
        float v[3];
        loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
        loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
        loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
        if (!l->filelen)
                return;
-       loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
+       loadmodel->brush.entities = (char *)Mem_Alloc(loadmodel->mempool, l->filelen);
        memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
        data = loadmodel->brush.entities;
        // some Q3 maps override the lightgrid_cellsize with a worldspawn key
@@ -3301,31 +3916,299 @@ static void Mod_Q3BSP_LoadEntities(lump_t *l)
 static void Mod_Q3BSP_LoadTextures(lump_t *l)
 {
        q3dtexture_t *in;
-       q3mtexture_t *out;
+       texture_t *out;
        int i, count;
-
-       in = (void *)(mod_base + l->fileofs);
+       int j, c;
+       fssearch_t *search;
+       char *f;
+       const char *text;
+       int flags, flags2, numparameters, passnumber;
+       char shadername[Q3PATHLENGTH];
+       char sky[Q3PATHLENGTH];
+       char firstpasstexturename[Q3PATHLENGTH];
+       char parameter[4][Q3PATHLENGTH];
+
+       in = (q3dtexture_t *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
                Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
        count = l->filelen / sizeof(*in);
-       out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+       out = (texture_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
 
-       loadmodel->brushq3.data_textures = out;
-       loadmodel->brushq3.num_textures = count;
+       loadmodel->data_textures = out;
+       loadmodel->num_textures = count;
 
        for (i = 0;i < count;i++, in++, out++)
        {
-               strncpy(out->name, in->name, sizeof(out->name) - 1);
+               strlcpy (out->name, in->name, sizeof (out->name));
                out->surfaceflags = LittleLong(in->surfaceflags);
-               out->nativecontents = LittleLong(in->contents);
-               out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, out->nativecontents);
-               out->renderflags = 0;
-               if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk"))
-                       out->renderflags |= Q3MTEXTURERENDERFLAGS_NODRAW;
+               out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, LittleLong(in->contents));
+               out->surfaceparms = -1;
+       }
+
+       // do a quick parse of shader files to get surfaceparms
+       if ((search = FS_Search("scripts/*.shader", true, false)))
+       {
+               for (i = 0;i < search->numfilenames;i++)
+               {
+                       if ((f = (char *)FS_LoadFile(search->filenames[i], tempmempool, false, NULL)))
+                       {
+                               text = f;
+                               while (COM_ParseToken(&text, false))
+                               {
+                                       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))
+                                               {
+                                                       if (!strcasecmp(com_token, "}"))
+                                                               break;
+                                                       else if (!strcasecmp(com_token, "{"))
+                                                       {
+                                                               while (COM_ParseToken(&text, false))
+                                                               {
+                                                                       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 >= 100)
+                                                                       {
+                                                                               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") && (flags & Q3SURFACEPARM_TRANS))
+                                                                               {
+                                                                                       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));
+                                                                               else if (numparameters >= 2 && !strcasecmp(parameter[0], "alphafunc"))
+                                                                                       flags2 |= Q3TEXTUREFLAG_ALPHATEST;
+                                                                       }
+                                                                       // break out a level if it was }
+                                                                       if (!strcasecmp(com_token, "}"))
+                                                                               break;
+                                                               }
+                                                               passnumber++;
+                                                               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 (i == 0 && !strcasecmp(com_token, "}"))
+                                                               break;
+                                                       if (developer.integer >= 100)
+                                                       {
+                                                               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 if (!strcasecmp(parameter[1], "pointlight"))
+                                                                       flags |= Q3SURFACEPARM_POINTLIGHT;
+                                                               else
+                                                                       Con_Printf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[i], parameter[1]);
+                                                       }
+                                                       else if (!strcasecmp(parameter[0], "sky") && numparameters >= 2)
+                                                               strlcpy(sky, parameter[1], sizeof(sky));
+                                                       else if (!strcasecmp(parameter[0], "skyparms") && numparameters >= 2)
+                                                       {
+                                                               if (!atoi(parameter[1]) && strcasecmp(parameter[1], "-"))
+                                                                       strlcpy(sky, parameter[1], sizeof(sky));
+                                                       }
+                                                       else if (!strcasecmp(parameter[0], "cull") && numparameters >= 2)
+                                                       {
+                                                               if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "twosided"))
+                                                                       flags2 |= Q3TEXTUREFLAG_TWOSIDED;
+                                                       }
+                                                       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)
+                                                       {
+                                                               if (!strcasecmp(parameter[1], "autosprite") && numparameters == 2)
+                                                                       flags2 |= Q3TEXTUREFLAG_AUTOSPRITE;
+                                                               if (!strcasecmp(parameter[1], "autosprite2") && numparameters == 2)
+                                                                       flags2 |= Q3TEXTUREFLAG_AUTOSPRITE2;
+                                                       }
+                                               }
+                                               // add shader to list (shadername and flags)
+                                               // actually here we just poke into the texture settings
+                                               for (j = 0, out = loadmodel->data_textures;j < loadmodel->num_textures;j++, out++)
+                                               {
+                                                       if (!strcasecmp(out->name, shadername))
+                                                       {
+                                                               out->surfaceparms = flags;
+                                                               out->textureflags = flags2;
+                                                               out->basematerialflags = 0;
+                                                               if (out->surfaceparms & Q3SURFACEPARM_NODRAW)
+                                                                       out->basematerialflags |= MATERIALFLAG_NODRAW;
+                                                               else if (out->surfaceparms & Q3SURFACEPARM_SKY)
+                                                                       out->basematerialflags |= MATERIALFLAG_SKY;
+                                                               else if (out->surfaceparms & Q3SURFACEPARM_LAVA)
+                                                                       out->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_FULLBRIGHT;
+                                                               else if (out->surfaceparms & Q3SURFACEPARM_SLIME)
+                                                                       out->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_WATERALPHA;
+                                                               else if (out->surfaceparms & Q3SURFACEPARM_WATER)
+                                                                       out->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_WATERALPHA;
+                                                               else
+                                                                       out->basematerialflags |= MATERIALFLAG_WALL;
+                                                               if (out->textureflags & Q3TEXTUREFLAG_ALPHATEST)
+                                                               {
+                                                                       // FIXME: support alpha test?
+                                                                       out->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_TRANSPARENT;
+                                                               }
+                                                               else if (out->surfaceparms & Q3SURFACEPARM_TRANS)
+                                                               {
+                                                                       if (out->textureflags & Q3TEXTUREFLAG_ADDITIVE)
+                                                                               out->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_TRANSPARENT;
+                                                                       else
+                                                                               out->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_TRANSPARENT;
+                                                               }
+                                                               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
+                                                                       dpsnprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", sky);
+                                                               }
+                                                       }
+                                               }
+                                       }
+                                       else
+                                       {
+                                               Con_Printf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[i], com_token);
+                                               goto parseerror;
+                                       }
+                               }
+parseerror:
+                               Mem_Free(f);
+                       }
+               }
+       }
 
-               out->number = i;
-               Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true);
+       c = 0;
+       for (j = 0, out = loadmodel->data_textures;j < loadmodel->num_textures;j++, out++)
+       {
+               if (out->surfaceparms == -1)
+               {
+                       c++;
+                       Con_DPrintf("%s: No shader found for texture \"%s\"\n", loadmodel->name, out->name);
+                       out->surfaceparms = 0;
+                       if (out->surfaceflags & Q3SURFACEFLAG_NODRAW)
+                               out->basematerialflags |= MATERIALFLAG_NODRAW;
+                       else if (out->surfaceflags & Q3SURFACEFLAG_SKY)
+                               out->basematerialflags |= MATERIALFLAG_SKY;
+                       else
+                               out->basematerialflags |= MATERIALFLAG_WALL;
+                       // these are defaults
+                       //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 (cls.state != ca_dedicated)
+                       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))
+                               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))
+                                       Con_Printf("%s: texture loading for shader \"%s\" failed (first layer \"%s\" not found either)\n", loadmodel->name, out->name, out->firstpasstexturename);
+               // no animation
+               out->currentframe = out;
        }
+       if (c)
+               Con_DPrintf("%s: %i textures missing shaders\n", loadmodel->name, c);
 }
 
 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
@@ -3334,21 +4217,21 @@ static void Mod_Q3BSP_LoadPlanes(lump_t *l)
        mplane_t *out;
        int i, count;
 
-       in = (void *)(mod_base + l->fileofs);
+       in = (q3dplane_t *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
                Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
        count = l->filelen / sizeof(*in);
-       out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+       out = (mplane_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
 
-       loadmodel->brushq3.data_planes = out;
-       loadmodel->brushq3.num_planes = count;
+       loadmodel->brush.data_planes = out;
+       loadmodel->brush.num_planes = count;
 
        for (i = 0;i < count;i++, in++, out++)
        {
-               out->normal[0] = LittleLong(in->normal[0]);
-               out->normal[1] = LittleLong(in->normal[1]);
-               out->normal[2] = LittleLong(in->normal[2]);
-               out->dist = LittleLong(in->dist);
+               out->normal[0] = LittleFloat(in->normal[0]);
+               out->normal[1] = LittleFloat(in->normal[1]);
+               out->normal[2] = LittleFloat(in->normal[2]);
+               out->dist = LittleFloat(in->dist);
                PlaneClassify(out);
        }
 }
@@ -3359,25 +4242,25 @@ static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
        q3mbrushside_t *out;
        int i, n, count;
 
-       in = (void *)(mod_base + l->fileofs);
+       in = (q3dbrushside_t *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
                Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
        count = l->filelen / sizeof(*in);
-       out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+       out = (q3mbrushside_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
 
-       loadmodel->brushq3.data_brushsides = out;
-       loadmodel->brushq3.num_brushsides = count;
+       loadmodel->brush.data_brushsides = out;
+       loadmodel->brush.num_brushsides = count;
 
        for (i = 0;i < count;i++, in++, out++)
        {
                n = LittleLong(in->planeindex);
-               if (n < 0 || n >= loadmodel->brushq3.num_planes)
-                       Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
-               out->plane = loadmodel->brushq3.data_planes + n;
+               if (n < 0 || n >= loadmodel->brush.num_planes)
+                       Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)", n, loadmodel->brush.num_planes);
+               out->plane = loadmodel->brush.data_planes + n;
                n = LittleLong(in->textureindex);
-               if (n < 0 || n >= loadmodel->brushq3.num_textures)
-                       Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
-               out->texture = loadmodel->brushq3.data_textures + n;
+               if (n < 0 || n >= loadmodel->num_textures)
+                       Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)", n, loadmodel->num_textures);
+               out->texture = loadmodel->data_textures + n;
        }
 }
 
@@ -3386,16 +4269,16 @@ static void Mod_Q3BSP_LoadBrushes(lump_t *l)
        q3dbrush_t *in;
        q3mbrush_t *out;
        int i, j, n, c, count, maxplanes;
-       mplane_t *planes;
+       colplanef_t *planes;
 
-       in = (void *)(mod_base + l->fileofs);
+       in = (q3dbrush_t *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
                Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
        count = l->filelen / sizeof(*in);
-       out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+       out = (q3mbrush_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
 
-       loadmodel->brushq3.data_brushes = out;
-       loadmodel->brushq3.num_brushes = count;
+       loadmodel->brush.data_brushes = out;
+       loadmodel->brush.num_brushes = count;
 
        maxplanes = 0;
        planes = NULL;
@@ -3404,14 +4287,14 @@ static void Mod_Q3BSP_LoadBrushes(lump_t *l)
        {
                n = LittleLong(in->firstbrushside);
                c = LittleLong(in->numbrushsides);
-               if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
-                       Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
-               out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
+               if (n < 0 || n + c > loadmodel->brush.num_brushsides)
+                       Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)", n, n + c, loadmodel->brush.num_brushsides);
+               out->firstbrushside = loadmodel->brush.data_brushsides + n;
                out->numbrushsides = c;
                n = LittleLong(in->textureindex);
-               if (n < 0 || n >= loadmodel->brushq3.num_textures)
-                       Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
-               out->texture = loadmodel->brushq3.data_textures + n;
+               if (n < 0 || n >= loadmodel->num_textures)
+                       Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)", n, loadmodel->num_textures);
+               out->texture = loadmodel->data_textures + n;
 
                // make a list of mplane_t structs to construct a colbrush from
                if (maxplanes < out->numbrushsides)
@@ -3419,15 +4302,18 @@ static void Mod_Q3BSP_LoadBrushes(lump_t *l)
                        maxplanes = out->numbrushsides;
                        if (planes)
                                Mem_Free(planes);
-                       planes = Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes);
+                       planes = (colplanef_t *)Mem_Alloc(tempmempool, sizeof(colplanef_t) * maxplanes);
                }
                for (j = 0;j < out->numbrushsides;j++)
                {
                        VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
                        planes[j].dist = out->firstbrushside[j].plane->dist;
+                       planes[j].supercontents = out->firstbrushside[j].texture->supercontents;
+                       planes[j].q3surfaceflags = out->firstbrushside[j].texture->surfaceflags;
+                       planes[j].texture = out->firstbrushside[j].texture;
                }
                // make the colbrush from the planes
-               out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents);
+               out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes);
        }
        if (planes)
                Mem_Free(planes);
@@ -3436,25 +4322,28 @@ static void Mod_Q3BSP_LoadBrushes(lump_t *l)
 static void Mod_Q3BSP_LoadEffects(lump_t *l)
 {
        q3deffect_t *in;
-       q3meffect_t *out;
+       q3deffect_t *out;
        int i, n, count;
 
-       in = (void *)(mod_base + l->fileofs);
+       in = (q3deffect_t *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
                Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
        count = l->filelen / sizeof(*in);
-       out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+       out = (q3deffect_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
 
        loadmodel->brushq3.data_effects = out;
        loadmodel->brushq3.num_effects = count;
 
        for (i = 0;i < count;i++, in++, out++)
        {
-               strncpy(out->shadername, in->shadername, sizeof(out->shadername) - 1);
+               strlcpy (out->shadername, in->shadername, sizeof (out->shadername));
                n = LittleLong(in->brushindex);
-               if (n < 0 || n >= loadmodel->brushq3.num_brushes)
-                       Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
-               out->brush = loadmodel->brushq3.data_brushes + n;
+               if (n >= loadmodel->brush.num_brushes)
+               {
+                       Con_Printf("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes), setting to -1\n", n, loadmodel->brush.num_brushes);
+                       n = -1;
+               }
+               out->brushindex = n;
                out->unknown = LittleLong(in->unknown);
        }
 }
@@ -3464,17 +4353,14 @@ static void Mod_Q3BSP_LoadVertices(lump_t *l)
        q3dvertex_t *in;
        int i, count;
 
-       in = (void *)(mod_base + l->fileofs);
+       in = (q3dvertex_t *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
                Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
        loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
-       loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
-       loadmodel->brushq3.data_texcoordtexture2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
-       loadmodel->brushq3.data_texcoordlightmap2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
-       loadmodel->brushq3.data_svector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
-       loadmodel->brushq3.data_tvector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
-       loadmodel->brushq3.data_normal3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
-       loadmodel->brushq3.data_color4f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[4]));
+       loadmodel->brushq3.data_vertex3f = (float *)Mem_Alloc(loadmodel->mempool, count * (sizeof(float) * (3 + 2 + 2 + 4)));
+       loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + count * 3;
+       loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + count * 2;
+       loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_texcoordlightmap2f + count * 2;
 
        for (i = 0;i < count;i++, in++)
        {
@@ -3486,15 +4372,6 @@ static void Mod_Q3BSP_LoadVertices(lump_t *l)
                loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
                loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
                // svector/tvector are calculated later in face loading
-               loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
-               loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
-               loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
-               loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
-               loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
-               loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
-               loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
-               loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
-               loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
                loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
                loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
                loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
@@ -3508,15 +4385,14 @@ static void Mod_Q3BSP_LoadTriangles(lump_t *l)
        int *out;
        int i, count;
 
-       in = (void *)(mod_base + l->fileofs);
+       in = (int *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(int[3]))
                Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
        count = l->filelen / sizeof(*in);
-       out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+       out = (int *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
 
        loadmodel->brushq3.num_triangles = count / 3;
        loadmodel->brushq3.data_element3i = out;
-       loadmodel->brushq3.data_neighbor3i = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
 
        for (i = 0;i < count;i++, in++, out++)
        {
@@ -3535,24 +4411,46 @@ static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
        rtexture_t **out;
        int i, count;
 
-       in = (void *)(mod_base + l->fileofs);
+       if (!l->filelen)
+               return;
+       in = (q3dlightmap_t *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
                Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
        count = l->filelen / sizeof(*in);
-       out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+       out = (rtexture_t **)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
 
        loadmodel->brushq3.data_lightmaps = out;
        loadmodel->brushq3.num_lightmaps = count;
 
+       loadmodel->brushq3.deluxemapping_modelspace = false;
        for (i = 0;i < count;i++, in++, out++)
+       {
+               // if this may be a deluxemap, check if it's in modelspace or not
+               if ((i & 1) && !loadmodel->brushq3.deluxemapping_modelspace)
+               {
+                       int j;
+                       unsigned char *b = in->rgb;
+                       for (j = 2;j < 128*128*3;j += 3)
+                       {
+                               // if this is definitely negative Z, it is not facing outward,
+                               // and thus must be in modelspace, as negative Z would never
+                               // occur in tangentspace
+                               if (b[j] < 120)
+                               {
+                                       loadmodel->brushq3.deluxemapping_modelspace = true;
+                                       break;
+                               }
+                       }
+               }
                *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
+       }
 }
 
 static void Mod_Q3BSP_LoadFaces(lump_t *l)
 {
-       q3dface_t *in;
-       q3mface_t *out;
-       int i, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xlevel, ylevel, row0, row1, x, y, *e, finalvertices, finaltriangles;
+       q3dface_t *in, *oldin;
+       msurface_t *out, *oldout;
+       int i, oldi, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xtess, ytess, finalvertices, finaltriangles, firstvertex, firstelement, type, oldnumtriangles, oldnumtriangles2, meshvertices, meshtriangles, numvertices, numtriangles;
        //int *originalelement3i;
        //int *originalneighbor3i;
        float *originalvertex3f;
@@ -3564,42 +4462,69 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
        float *originaltexcoordlightmap2f;
        float *v;
 
-       in = (void *)(mod_base + l->fileofs);
+       in = (q3dface_t *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
                Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
        count = l->filelen / sizeof(*in);
-       out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
-
-       loadmodel->brushq3.data_faces = out;
-       loadmodel->brushq3.num_faces = count;
+       out = (msurface_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+
+       loadmodel->data_surfaces = out;
+       loadmodel->num_surfaces = count;
+
+       // deluxemapped q3bsp files have an even number of lightmaps, and surfaces
+       // always index even numbered ones (0, 2, 4, ...), the odd numbered
+       // lightmaps are the deluxemaps (light direction textures), so if we
+       // encounter any odd numbered lightmaps it is not a deluxemapped bsp, it
+       // is also not a deluxemapped bsp if it has an odd number of lightmaps or
+       // less than 2
+       loadmodel->brushq3.deluxemapping = true;
+       if (loadmodel->brushq3.num_lightmaps >= 2 && !(loadmodel->brushq3.num_lightmaps & 1))
+       {
+               for (i = 0;i < count;i++)
+               {
+                       n = LittleLong(in[i].lightmapindex);
+                       if (n >= 0 && ((n & 1) || n + 1 >= loadmodel->brushq3.num_lightmaps))
+                       {
+                               loadmodel->brushq3.deluxemapping = false;
+                               break;
+                       }
+               }
+       }
+       else
+               loadmodel->brushq3.deluxemapping = false;
+       Con_DPrintf("%s is %sdeluxemapped\n", loadmodel->name, loadmodel->brushq3.deluxemapping ? "" : "not ");
 
-       for (i = 0;i < count;i++, in++, out++)
+       i = 0;
+       oldi = i;
+       oldin = in;
+       oldout = out;
+       meshvertices = 0;
+       meshtriangles = 0;
+       for (;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)
-               {
-                       Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
-                       out->type = 0; // error
+               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, type);
                        continue;
                }
 
                n = LittleLong(in->textureindex);
-               if (n < 0 || n >= loadmodel->brushq3.num_textures)
+               if (n < 0 || n >= loadmodel->num_textures)
                {
-                       Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
-                       out->type = 0; // error
+                       Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->num_textures);
                        continue;
-                       n = 0;
                }
-               out->texture = loadmodel->brushq3.data_textures + n;
+               out->texture = loadmodel->data_textures + n;
                n = LittleLong(in->effectindex);
                if (n < -1 || n >= loadmodel->brushq3.num_effects)
                {
-                       Con_Printf("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 >= 100)
+                               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)
@@ -3607,49 +4532,47 @@ 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_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;
+                       out->deluxemaptexture = r_texture_blanknormalmap;
+               }
                else
+               {
                        out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
+                       if (loadmodel->brushq3.deluxemapping)
+                               out->deluxemaptexture = loadmodel->brushq3.data_lightmaps[n+1];
+                       else
+                               out->deluxemaptexture = r_texture_blanknormalmap;
+               }
 
-               out->firstvertex = LittleLong(in->firstvertex);
-               out->numvertices = LittleLong(in->numvertices);
-               out->firstelement = LittleLong(in->firstelement);
-               out->numelements = LittleLong(in->numelements);
-               out->numtriangles = out->numelements / 3;
-               if (out->firstvertex < 0 || out->firstvertex + out->numvertices > loadmodel->brushq3.num_vertices)
+               firstvertex = LittleLong(in->firstvertex);
+               numvertices = LittleLong(in->numvertices);
+               firstelement = LittleLong(in->firstelement);
+               numtriangles = LittleLong(in->numelements) / 3;
+               if (numtriangles * 3 != LittleLong(in->numelements))
                {
-                       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->numvertices, loadmodel->brushq3.num_vertices);
-                       out->type = 0; // error
+                       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));
                        continue;
                }
-               if (out->firstelement < 0 || out->firstelement + out->numelements > loadmodel->brushq3.num_triangles * 3)
+               if (firstvertex < 0 || firstvertex + numvertices > loadmodel->brushq3.num_vertices)
                {
-                       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->numelements, loadmodel->brushq3.num_triangles * 3);
-                       out->type = 0; // error
+                       Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid vertex range %i : %i (%i vertices)\n", i, out->texture->name, firstvertex, firstvertex + numvertices, loadmodel->brushq3.num_vertices);
                        continue;
                }
-               if (out->numtriangles * 3 != out->numelements)
+               if (firstelement < 0 || firstelement + numtriangles * 3 > loadmodel->brushq3.num_triangles * 3)
                {
-                       Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): numelements %i is not a multiple of 3\n", i, out->texture->name, out->numelements);
-                       out->type = 0; // error
+                       Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid element range %i : %i (%i elements)\n", i, out->texture->name, firstelement, firstelement + numtriangles * 3, loadmodel->brushq3.num_triangles * 3);
                        continue;
                }
-               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;
-               switch(out->type)
+               switch(type)
                {
                case Q3FACETYPE_POLYGON:
                case Q3FACETYPE_MESH:
@@ -3658,110 +4581,196 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                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 (numvertices != (patchsize[0] * patchsize[1]) || 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->type = 0; // error
                                continue;
                        }
+                       originalvertex3f = loadmodel->brushq3.data_vertex3f + firstvertex * 3;
                        // convert patch to Q3FACETYPE_MESH
-                       xlevel = mod_q3bsp_curves_subdivide_level.integer;
-                       ylevel = mod_q3bsp_curves_subdivide_level.integer;
-                       finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
-                       finalheight = ((patchsize[1] - 1) << ylevel) + 1;
+                       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
+                       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
+                       xtess = bound(1, xtess, 1024);
+                       ytess = bound(1, ytess, 1024);
+                       // bound to user limit on vertices
+                       while ((xtess > 1 || ytess > 1) && (((patchsize[0] - 1) * xtess) + 1) * (((patchsize[1] - 1) * ytess) + 1) > min(r_subdivisions_maxvertices.integer, 262144))
+                       {
+                               if (xtess > ytess)
+                                       xtess--;
+                               else
+                                       ytess--;
+                       }
+                       finalwidth = ((patchsize[0] - 1) * xtess) + 1;
+                       finalheight = ((patchsize[1] - 1) * ytess) + 1;
+                       numvertices = finalwidth * finalheight;
+                       numtriangles = (finalwidth - 1) * (finalheight - 1) * 2;
+                       break;
+               case Q3FACETYPE_FLARE:
+                       if (developer.integer >= 100)
+                               Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
+                       // don't render it
+                       continue;
+               }
+               out->num_vertices = numvertices;
+               out->num_triangles = numtriangles;
+               meshvertices += out->num_vertices;
+               meshtriangles += out->num_triangles;
+       }
+
+       i = oldi;
+       in = oldin;
+       out = oldout;
+       Mod_AllocSurfMesh(loadmodel->mempool, meshvertices, meshtriangles, false, true, false);
+       meshvertices = 0;
+       meshtriangles = 0;
+       for (;i < count && meshvertices + out->num_vertices <= loadmodel->surfmesh.num_vertices;i++, in++, out++)
+       {
+               if (out->num_vertices < 3 || out->num_triangles < 1)
+                       continue;
+
+               type = LittleLong(in->type);
+               firstvertex = LittleLong(in->firstvertex);
+               firstelement = LittleLong(in->firstelement);
+               out->num_firstvertex = meshvertices;
+               out->num_firsttriangle = meshtriangles;
+               switch(type)
+               {
+               case Q3FACETYPE_POLYGON:
+               case Q3FACETYPE_MESH:
+                       // no processing necessary
+                       for (j = 0;j < out->num_vertices;j++)
+                       {
+                               (loadmodel->surfmesh.data_vertex3f + 3 * out->num_firstvertex)[j * 3 + 0] = loadmodel->brushq3.data_vertex3f[(firstvertex + j) * 3 + 0];
+                               (loadmodel->surfmesh.data_vertex3f + 3 * out->num_firstvertex)[j * 3 + 1] = loadmodel->brushq3.data_vertex3f[(firstvertex + j) * 3 + 1];
+                               (loadmodel->surfmesh.data_vertex3f + 3 * out->num_firstvertex)[j * 3 + 2] = loadmodel->brushq3.data_vertex3f[(firstvertex + j) * 3 + 2];
+                               (loadmodel->surfmesh.data_texcoordtexture2f + 2 * out->num_firstvertex)[j * 2 + 0] = loadmodel->brushq3.data_texcoordtexture2f[(firstvertex + j) * 2 + 0];
+                               (loadmodel->surfmesh.data_texcoordtexture2f + 2 * out->num_firstvertex)[j * 2 + 1] = loadmodel->brushq3.data_texcoordtexture2f[(firstvertex + j) * 2 + 1];
+                               (loadmodel->surfmesh.data_texcoordlightmap2f + 2 * out->num_firstvertex)[j * 2 + 0] = loadmodel->brushq3.data_texcoordlightmap2f[(firstvertex + j) * 2 + 0];
+                               (loadmodel->surfmesh.data_texcoordlightmap2f + 2 * out->num_firstvertex)[j * 2 + 1] = loadmodel->brushq3.data_texcoordlightmap2f[(firstvertex + j) * 2 + 1];
+                               (loadmodel->surfmesh.data_lightmapcolor4f + 4 * out->num_firstvertex)[j * 4 + 0] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 0];
+                               (loadmodel->surfmesh.data_lightmapcolor4f + 4 * out->num_firstvertex)[j * 4 + 1] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 1];
+                               (loadmodel->surfmesh.data_lightmapcolor4f + 4 * out->num_firstvertex)[j * 4 + 2] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 2];
+                               (loadmodel->surfmesh.data_lightmapcolor4f + 4 * out->num_firstvertex)[j * 4 + 3] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 3];
+                       }
+                       for (j = 0;j < out->num_triangles*3;j++)
+                               (loadmodel->surfmesh.data_element3i + 3 * out->num_firsttriangle)[j] = loadmodel->brushq3.data_element3i[firstelement + j] + out->num_firstvertex;
+                       break;
+               case Q3FACETYPE_PATCH:
+                       patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
+                       patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
+                       originalvertex3f = loadmodel->brushq3.data_vertex3f + firstvertex * 3;
+                       originaltexcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + firstvertex * 2;
+                       originaltexcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + firstvertex * 2;
+                       originalcolor4f = loadmodel->brushq3.data_color4f + firstvertex * 4;
+                       // convert patch to Q3FACETYPE_MESH
+                       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
+                       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
+                       xtess = bound(1, xtess, 1024);
+                       ytess = bound(1, ytess, 1024);
+                       // bound to user limit on vertices
+                       while ((xtess > 1 || ytess > 1) && (((patchsize[0] - 1) * xtess) + 1) * (((patchsize[1] - 1) * ytess) + 1) > min(r_subdivisions_maxvertices.integer, 262144))
+                       {
+                               if (xtess > ytess)
+                                       xtess--;
+                               else
+                                       ytess--;
+                       }
+                       finalwidth = ((patchsize[0] - 1) * xtess) + 1;
+                       finalheight = ((patchsize[1] - 1) * ytess) + 1;
                        finalvertices = finalwidth * finalheight;
                        finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
-                       originalvertex3f = out->data_vertex3f;
-                       //originalsvector3f = out->data_svector3f;
-                       //originaltvector3f = out->data_tvector3f;
-                       //originalnormal3f = out->data_normal3f;
-                       originalcolor4f = out->data_color4f;
-                       originaltexcoordtexture2f = out->data_texcoordtexture2f;
-                       originaltexcoordlightmap2f = out->data_texcoordlightmap2f;
-                       //originalelement3i = out->data_element3i;
-                       //originalneighbor3i = out->data_neighbor3i;
-                       out->data_vertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[20]) * finalvertices + sizeof(int[6]) * finaltriangles);
-                       out->data_svector3f = out->data_vertex3f + finalvertices * 3;
-                       out->data_tvector3f = out->data_svector3f + finalvertices * 3;
-                       out->data_normal3f = out->data_tvector3f + finalvertices * 3;
-                       out->data_color4f = out->data_normal3f + finalvertices * 3;
-                       out->data_texcoordtexture2f = out->data_color4f + finalvertices * 4;
-                       out->data_texcoordlightmap2f = out->data_texcoordtexture2f + finalvertices * 2;
-                       out->data_element3i = (int *)(out->data_texcoordlightmap2f + finalvertices * 2);
-                       out->data_neighbor3i = out->data_element3i + finaltriangles * 3;
-                       out->type = Q3FACETYPE_MESH;
-                       out->firstvertex = -1;
-                       out->numvertices = finalvertices;
-                       out->firstelement = -1;
-                       out->numtriangles = finaltriangles;
-                       out->numelements = finaltriangles * 3;
+                       type = Q3FACETYPE_MESH;
                        // generate geometry
                        // (note: normals are skipped because they get recalculated)
-                       QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_vertex3f);
-                       QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordtexture2f, out->data_texcoordtexture2f);
-                       QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordlightmap2f, out->data_texcoordlightmap2f);
-                       QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 4, originalcolor4f, out->data_color4f);
-                       // generate elements
-                       e = out->data_element3i;
-                       for (y = 0;y < finalheight - 1;y++)
+                       Q3PatchTesselateFloat(3, sizeof(float[3]), (loadmodel->surfmesh.data_vertex3f + 3 * out->num_firstvertex), patchsize[0], patchsize[1], sizeof(float[3]), originalvertex3f, xtess, ytess);
+                       Q3PatchTesselateFloat(2, sizeof(float[2]), (loadmodel->surfmesh.data_texcoordtexture2f + 2 * out->num_firstvertex), patchsize[0], patchsize[1], sizeof(float[2]), originaltexcoordtexture2f, xtess, ytess);
+                       Q3PatchTesselateFloat(2, sizeof(float[2]), (loadmodel->surfmesh.data_texcoordlightmap2f + 2 * out->num_firstvertex), patchsize[0], patchsize[1], sizeof(float[2]), originaltexcoordlightmap2f, xtess, ytess);
+                       Q3PatchTesselateFloat(4, sizeof(float[4]), (loadmodel->surfmesh.data_lightmapcolor4f + 4 * out->num_firstvertex), patchsize[0], patchsize[1], sizeof(float[4]), originalcolor4f, xtess, ytess);
+                       Q3PatchTriangleElements((loadmodel->surfmesh.data_element3i + 3 * out->num_firsttriangle), finalwidth, finalheight, out->num_firstvertex);
+                       out->num_triangles = Mod_RemoveDegenerateTriangles(out->num_triangles, (loadmodel->surfmesh.data_element3i + 3 * out->num_firsttriangle), (loadmodel->surfmesh.data_element3i + 3 * out->num_firsttriangle), loadmodel->surfmesh.data_vertex3f);
+                       if (developer.integer >= 100)
                        {
-                               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++;
-                               }
+                               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);
+                               else
+                                       Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles\n", patchsize[0], patchsize[1], out->num_vertices, out->num_triangles);
                        }
-                       out->numtriangles = Mod_RemoveDegenerateTriangles(out->numtriangles, out->data_element3i, out->data_element3i, out->data_vertex3f);
-                       if (developer.integer)
+                       // q3map does not put in collision brushes for curves... ugh
+                       // build the lower quality collision geometry
+                       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
+                       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
+                       xtess = bound(1, xtess, 1024);
+                       ytess = bound(1, ytess, 1024);
+                       // bound to user limit on vertices
+                       while ((xtess > 1 || ytess > 1) && (((patchsize[0] - 1) * xtess) + 1) * (((patchsize[1] - 1) * ytess) + 1) > min(r_subdivisions_collision_maxvertices.integer, 262144))
                        {
-                               if (out->numtriangles < 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->numvertices, finaltriangles, finaltriangles - out->numtriangles, out->numtriangles);
+                               if (xtess > ytess)
+                                       xtess--;
                                else
-                                       Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles\n", patchsize[0], patchsize[1], out->numvertices, out->numtriangles);
+                                       ytess--;
                        }
-                       // q3map does not put in collision brushes for curves... ugh
-                       out->collisions = true;
+                       finalwidth = ((patchsize[0] - 1) * xtess) + 1;
+                       finalheight = ((patchsize[1] - 1) * ytess) + 1;
+                       finalvertices = finalwidth * finalheight;
+                       finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
+
+                       out->data_collisionvertex3f = (float *)Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * finalvertices);
+                       out->data_collisionelement3i = (int *)Mem_Alloc(loadmodel->mempool, sizeof(int[3]) * finaltriangles);
+                       out->num_collisionvertices = finalvertices;
+                       out->num_collisiontriangles = finaltriangles;
+                       Q3PatchTesselateFloat(3, sizeof(float[3]), out->data_collisionvertex3f, patchsize[0], patchsize[1], sizeof(float[3]), originalvertex3f, xtess, ytess);
+                       Q3PatchTriangleElements(out->data_collisionelement3i, finalwidth, finalheight, 0);
+
+                       //Mod_SnapVertices(3, out->num_vertices, (loadmodel->surfmesh.data_vertex3f + 3 * out->num_firstvertex), 0.25);
+                       Mod_SnapVertices(3, out->num_collisionvertices, out->data_collisionvertex3f, 1);
+
+                       oldnumtriangles = out->num_triangles;
+                       oldnumtriangles2 = out->num_collisiontriangles;
+                       out->num_collisiontriangles = Mod_RemoveDegenerateTriangles(out->num_collisiontriangles, out->data_collisionelement3i, out->data_collisionelement3i, out->data_collisionvertex3f);
+                       if (developer.integer >= 100)
+                               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);
-                       // don't render it
-                       out->numtriangles = 0;
+               default:
                        break;
                }
-               for (j = 0, invalidelements = 0;j < out->numelements;j++)
-                       if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
+               meshvertices += out->num_vertices;
+               meshtriangles += out->num_triangles;
+               for (j = 0, invalidelements = 0;j < out->num_triangles * 3;j++)
+                       if ((loadmodel->surfmesh.data_element3i + 3 * out->num_firsttriangle)[j] < out->num_firstvertex || (loadmodel->surfmesh.data_element3i + 3 * out->num_firsttriangle)[j] >= out->num_firstvertex + out->num_vertices)
                                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->numvertices, out->firstelement, out->numelements);
-                       for (j = 0;j < out->numelements;j++)
+                       Con_Printf("Mod_Q3BSP_LoadFaces: Warning: face #%i has %i invalid elements, type = %i, texture->name = \"%s\", texture->surfaceflags = %i, firstvertex = %i, numvertices = %i, firstelement = %i, numelements = %i, elements list:\n", i, invalidelements, type, out->texture->name, out->texture->surfaceflags, 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]);
-                               if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
-                                       out->data_element3i[j] = 0;
+                               Con_Printf(" %i", (loadmodel->surfmesh.data_element3i + 3 * out->num_firsttriangle)[j] - out->num_firstvertex);
+                               if ((loadmodel->surfmesh.data_element3i + 3 * out->num_firsttriangle)[j] < out->num_firstvertex || (loadmodel->surfmesh.data_element3i + 3 * out->num_firsttriangle)[j] >= out->num_firstvertex + out->num_vertices)
+                                       (loadmodel->surfmesh.data_element3i + 3 * out->num_firsttriangle)[j] = out->num_firstvertex;
                        }
-                       Con_Printf("\n");
+                       Con_Print("\n");
                }
-               // for shadow volumes
-               Mod_BuildTriangleNeighbors(out->data_neighbor3i, out->data_element3i, out->numtriangles);
                // for per pixel lighting
-               Mod_BuildTextureVectorsAndNormals(out->numvertices, out->numtriangles, out->data_vertex3f, out->data_texcoordtexture2f, out->data_element3i, out->data_svector3f, out->data_tvector3f, out->data_normal3f);
+               Mod_BuildTextureVectorsAndNormals(out->num_firstvertex, out->num_vertices, out->num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_texcoordtexture2f, (loadmodel->surfmesh.data_element3i + 3 * out->num_firsttriangle), loadmodel->surfmesh.data_svector3f, loadmodel->surfmesh.data_tvector3f, loadmodel->surfmesh.data_normal3f, true);
                // calculate a bounding box
                VectorClear(out->mins);
                VectorClear(out->maxs);
-               if (out->numvertices)
+               if (out->num_vertices)
                {
-                       VectorCopy(out->data_vertex3f, out->mins);
-                       VectorCopy(out->data_vertex3f, out->maxs);
-                       for (j = 1, v = out->data_vertex3f + 3;j < out->numvertices;j++, v += 3)
+                       VectorCopy((loadmodel->surfmesh.data_vertex3f + 3 * out->num_firstvertex), out->mins);
+                       VectorCopy((loadmodel->surfmesh.data_vertex3f + 3 * out->num_firstvertex), out->maxs);
+                       for (j = 1, v = (loadmodel->surfmesh.data_vertex3f + 3 * out->num_firstvertex) + 3;j < out->num_vertices;j++, v += 3)
                        {
                                out->mins[0] = min(out->mins[0], v[0]);
                                out->maxs[0] = max(out->maxs[0], v[0]);
@@ -3777,20 +4786,37 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                        out->maxs[1] += 1.0f;
                        out->maxs[2] += 1.0f;
                }
+               // set lightmap styles for consistency with q1bsp
+               //out->lightmapinfo->styles[0] = 0;
+               //out->lightmapinfo->styles[1] = 255;
+               //out->lightmapinfo->styles[2] = 255;
+               //out->lightmapinfo->styles[3] = 255;
        }
+
+       // free the no longer needed vertex data
+       loadmodel->brushq3.num_vertices = 0;
+       Mem_Free(loadmodel->brushq3.data_vertex3f);
+       loadmodel->brushq3.data_vertex3f = NULL;
+       loadmodel->brushq3.data_texcoordtexture2f = NULL;
+       loadmodel->brushq3.data_texcoordlightmap2f = NULL;
+       loadmodel->brushq3.data_color4f = NULL;
+       // free the no longer needed triangle data
+       loadmodel->brushq3.num_triangles = 0;
+       Mem_Free(loadmodel->brushq3.data_element3i);
+       loadmodel->brushq3.data_element3i = NULL;
 }
 
 static void Mod_Q3BSP_LoadModels(lump_t *l)
 {
        q3dmodel_t *in;
-       q3mmodel_t *out;
+       q3dmodel_t *out;
        int i, j, n, c, count;
 
-       in = (void *)(mod_base + l->fileofs);
+       in = (q3dmodel_t *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
                Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
        count = l->filelen / sizeof(*in);
-       out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+       out = (q3dmodel_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
 
        loadmodel->brushq3.data_models = out;
        loadmodel->brushq3.num_models = count;
@@ -3804,15 +4830,15 @@ static void Mod_Q3BSP_LoadModels(lump_t *l)
                }
                n = LittleLong(in->firstface);
                c = LittleLong(in->numfaces);
-               if (n < 0 || n + c > loadmodel->brushq3.num_faces)
-                       Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
-               out->firstface = loadmodel->brushq3.data_faces + n;
+               if (n < 0 || n + c > loadmodel->num_surfaces)
+                       Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)", n, n + c, loadmodel->num_surfaces);
+               out->firstface = n;
                out->numfaces = c;
                n = LittleLong(in->firstbrush);
                c = LittleLong(in->numbrushes);
-               if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
-                       Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
-               out->firstbrush = loadmodel->brushq3.data_brushes + n;
+               if (n < 0 || n + c > loadmodel->brush.num_brushes)
+                       Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)", n, n + c, loadmodel->brush.num_brushes);
+               out->firstbrush = n;
                out->numbrushes = c;
        }
 }
@@ -3820,155 +4846,142 @@ static void Mod_Q3BSP_LoadModels(lump_t *l)
 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
 {
        int *in;
-       q3mbrush_t **out;
+       int *out;
        int i, n, count;
 
-       in = (void *)(mod_base + l->fileofs);
+       in = (int *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
                Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
        count = l->filelen / sizeof(*in);
-       out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+       out = (int *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
 
-       loadmodel->brushq3.data_leafbrushes = out;
-       loadmodel->brushq3.num_leafbrushes = count;
+       loadmodel->brush.data_leafbrushes = out;
+       loadmodel->brush.num_leafbrushes = count;
 
        for (i = 0;i < count;i++, in++, out++)
        {
                n = LittleLong(*in);
-               if (n < 0 || n >= loadmodel->brushq3.num_brushes)
-                       Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
-               *out = loadmodel->brushq3.data_brushes + n;
+               if (n < 0 || n >= loadmodel->brush.num_brushes)
+                       Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)", n, loadmodel->brush.num_brushes);
+               *out = n;
        }
 }
 
 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
 {
        int *in;
-       q3mface_t **out;
+       int *out;
        int i, n, count;
 
-       in = (void *)(mod_base + l->fileofs);
+       in = (int *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
                Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
        count = l->filelen / sizeof(*in);
-       out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+       out = (int *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
 
-       loadmodel->brushq3.data_leaffaces = out;
-       loadmodel->brushq3.num_leaffaces = count;
+       loadmodel->brush.data_leafsurfaces = out;
+       loadmodel->brush.num_leafsurfaces = count;
 
        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;
+               if (n < 0 || n >= loadmodel->num_surfaces)
+                       Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)", n, loadmodel->num_surfaces);
+               *out = n;
        }
 }
 
 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
 {
        q3dleaf_t *in;
-       q3mleaf_t *out;
+       mleaf_t *out;
        int i, j, n, c, count;
 
-       in = (void *)(mod_base + l->fileofs);
+       in = (q3dleaf_t *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
                Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
        count = l->filelen / sizeof(*in);
-       out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+       out = (mleaf_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
 
-       loadmodel->brushq3.data_leafs = out;
-       loadmodel->brushq3.num_leafs = count;
+       loadmodel->brush.data_leafs = out;
+       loadmodel->brush.num_leafs = count;
 
        for (i = 0;i < count;i++, in++, out++)
        {
-               out->isnode = false;
                out->parent = NULL;
+               out->plane = NULL;
                out->clusterindex = LittleLong(in->clusterindex);
                out->areaindex = LittleLong(in->areaindex);
                for (j = 0;j < 3;j++)
                {
                        // yes the mins/maxs are ints
-                       out->mins[j] = LittleLong(in->mins[j]);
-                       out->maxs[j] = LittleLong(in->maxs[j]);
+                       out->mins[j] = LittleLong(in->mins[j]) - 1;
+                       out->maxs[j] = LittleLong(in->maxs[j]) + 1;
                }
                n = LittleLong(in->firstleafface);
                c = LittleLong(in->numleaffaces);
-               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->numleaffaces = c;
+               if (n < 0 || n + c > loadmodel->brush.num_leafsurfaces)
+                       Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafsurface range %i : %i (%i leafsurfaces)", n, n + c, loadmodel->brush.num_leafsurfaces);
+               out->firstleafsurface = loadmodel->brush.data_leafsurfaces + n;
+               out->numleafsurfaces = c;
                n = LittleLong(in->firstleafbrush);
                c = LittleLong(in->numleafbrushes);
-               if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
-                       Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
-               out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
+               if (n < 0 || n + c > loadmodel->brush.num_leafbrushes)
+                       Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)", n, n + c, loadmodel->brush.num_leafbrushes);
+               out->firstleafbrush = loadmodel->brush.data_leafbrushes + n;
                out->numleafbrushes = c;
        }
 }
 
-static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
-{
-       if (node->parent)
-               Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
-       node->parent = parent;
-       if (node->isnode)
-       {
-               Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
-               Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
-       }
-}
-
 static void Mod_Q3BSP_LoadNodes(lump_t *l)
 {
        q3dnode_t *in;
-       q3mnode_t *out;
+       mnode_t *out;
        int i, j, n, count;
 
-       in = (void *)(mod_base + l->fileofs);
+       in = (q3dnode_t *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
                Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
        count = l->filelen / sizeof(*in);
-       out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+       out = (mnode_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
 
-       loadmodel->brushq3.data_nodes = out;
-       loadmodel->brushq3.num_nodes = count;
+       loadmodel->brush.data_nodes = out;
+       loadmodel->brush.num_nodes = count;
 
        for (i = 0;i < count;i++, in++, out++)
        {
-               out->isnode = true;
                out->parent = NULL;
                n = LittleLong(in->planeindex);
-               if (n < 0 || n >= loadmodel->brushq3.num_planes)
-                       Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
-               out->plane = loadmodel->brushq3.data_planes + n;
+               if (n < 0 || n >= loadmodel->brush.num_planes)
+                       Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)", n, loadmodel->brush.num_planes);
+               out->plane = loadmodel->brush.data_planes + n;
                for (j = 0;j < 2;j++)
                {
                        n = LittleLong(in->childrenindex[j]);
                        if (n >= 0)
                        {
-                               if (n >= loadmodel->brushq3.num_nodes)
-                                       Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
-                               out->children[j] = loadmodel->brushq3.data_nodes + n;
+                               if (n >= loadmodel->brush.num_nodes)
+                                       Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)", n, loadmodel->brush.num_nodes);
+                               out->children[j] = loadmodel->brush.data_nodes + n;
                        }
                        else
                        {
                                n = -1 - n;
-                               if (n >= loadmodel->brushq3.num_leafs)
-                                       Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
-                               out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
+                               if (n >= loadmodel->brush.num_leafs)
+                                       Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)", n, loadmodel->brush.num_leafs);
+                               out->children[j] = (mnode_t *)(loadmodel->brush.data_leafs + n);
                        }
                }
                for (j = 0;j < 3;j++)
                {
                        // yes the mins/maxs are ints
-                       out->mins[j] = LittleLong(in->mins[j]);
-                       out->maxs[j] = LittleLong(in->maxs[j]);
+                       out->mins[j] = LittleLong(in->mins[j]) - 1;
+                       out->maxs[j] = LittleLong(in->maxs[j]) + 1;
                }
        }
 
        // set the parent pointers
-       Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
+       Mod_Q1BSP_LoadNodes_RecursiveSetParent(loadmodel->brush.data_nodes, NULL);
 }
 
 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
@@ -3977,36 +4990,38 @@ static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
        q3dlightgrid_t *out;
        int count;
 
-       in = (void *)(mod_base + l->fileofs);
+       in = (q3dlightgrid_t *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
                Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
        loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
        loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
        loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
-       loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
-       loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
-       loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
-       loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
-       loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
-       loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
+       loadmodel->brushq3.num_lightgrid_imins[0] = (int)ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
+       loadmodel->brushq3.num_lightgrid_imins[1] = (int)ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
+       loadmodel->brushq3.num_lightgrid_imins[2] = (int)ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
+       loadmodel->brushq3.num_lightgrid_imaxs[0] = (int)floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
+       loadmodel->brushq3.num_lightgrid_imaxs[1] = (int)floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
+       loadmodel->brushq3.num_lightgrid_imaxs[2] = (int)floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
        loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
        loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
        loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
        count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
-       if (l->filelen < count * (int)sizeof(*in))
-               Host_Error("Mod_Q3BSP_LoadLightGrid: invalid lightgrid lump size %i bytes, should be %i bytes (%ix%ix%i)\n", l->filelen, count * sizeof(*in), loadmodel->brushq3.num_lightgrid_dimensions[0], loadmodel->brushq3.num_lightgrid_dimensions[1], loadmodel->brushq3.num_lightgrid_dimensions[2]);
-       if (l->filelen != count * (int)sizeof(*in))
-               Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
-
-       out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
-       loadmodel->brushq3.data_lightgrid = out;
-       loadmodel->brushq3.num_lightgrid = count;
-
-       // no swapping or validation necessary
-       memcpy(out, in, count * (int)sizeof(*out));
-
        Matrix4x4_CreateScale3(&loadmodel->brushq3.num_lightgrid_indexfromworld, loadmodel->brushq3.num_lightgrid_scale[0], loadmodel->brushq3.num_lightgrid_scale[1], loadmodel->brushq3.num_lightgrid_scale[2]);
        Matrix4x4_ConcatTranslate(&loadmodel->brushq3.num_lightgrid_indexfromworld, -loadmodel->brushq3.num_lightgrid_imins[0] * loadmodel->brushq3.num_lightgrid_cellsize[0], -loadmodel->brushq3.num_lightgrid_imins[1] * loadmodel->brushq3.num_lightgrid_cellsize[1], -loadmodel->brushq3.num_lightgrid_imins[2] * loadmodel->brushq3.num_lightgrid_cellsize[2]);
+
+       // if lump is empty there is nothing to load, we can deal with that in the LightPoint code
+       if (l->filelen)
+       {
+               if (l->filelen < count * (int)sizeof(*in))
+                       Host_Error("Mod_Q3BSP_LoadLightGrid: invalid lightgrid lump size %i bytes, should be %i bytes (%ix%ix%i)", l->filelen, count * sizeof(*in), loadmodel->brushq3.num_lightgrid_dimensions[0], loadmodel->brushq3.num_lightgrid_dimensions[1], loadmodel->brushq3.num_lightgrid_dimensions[2]);
+               if (l->filelen != count * (int)sizeof(*in))
+                       Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i", count * sizeof(*in), l->filelen);
+               out = (q3dlightgrid_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+               loadmodel->brushq3.data_lightgrid = out;
+               loadmodel->brushq3.num_lightgrid = count;
+               // no swapping or validation necessary
+               memcpy(out, in, count * (int)sizeof(*out));
+       }
 }
 
 static void Mod_Q3BSP_LoadPVS(lump_t *l)
@@ -4015,28 +5030,36 @@ static void Mod_Q3BSP_LoadPVS(lump_t *l)
        int totalchains;
 
        if (l->filelen == 0)
+       {
+               int i;
+               // unvised maps often have cluster indices even without pvs, so check
+               // leafs to find real number of clusters
+               loadmodel->brush.num_pvsclusters = 1;
+               for (i = 0;i < loadmodel->brush.num_leafs;i++)
+                       loadmodel->brush.num_pvsclusters = max(loadmodel->brush.num_pvsclusters, loadmodel->brush.data_leafs[i].clusterindex + 1);
+
+               // create clusters
+               loadmodel->brush.num_pvsclusterbytes = (loadmodel->brush.num_pvsclusters + 7) / 8;
+               totalchains = loadmodel->brush.num_pvsclusterbytes * loadmodel->brush.num_pvsclusters;
+               loadmodel->brush.data_pvsclusters = (unsigned char *)Mem_Alloc(loadmodel->mempool, totalchains);
+               memset(loadmodel->brush.data_pvsclusters, 0xFF, totalchains);
                return;
+       }
 
-       in = (void *)(mod_base + l->fileofs);
+       in = (q3dpvs_t *)(mod_base + l->fileofs);
        if (l->filelen < 9)
                Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
 
-       loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters);
-       loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength);
-       if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8))
-               Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters);
-       totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters;
+       loadmodel->brush.num_pvsclusters = LittleLong(in->numclusters);
+       loadmodel->brush.num_pvsclusterbytes = LittleLong(in->chainlength);
+       if (loadmodel->brush.num_pvsclusterbytes < ((loadmodel->brush.num_pvsclusters + 7) / 8))
+               Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8", loadmodel->brush.num_pvsclusterbytes, loadmodel->brush.num_pvsclusters);
+       totalchains = loadmodel->brush.num_pvsclusterbytes * loadmodel->brush.num_pvsclusters;
        if (l->filelen < totalchains + (int)sizeof(*in))
-               Host_Error("Mod_Q3BSP_LoadPVS: lump too small ((numclusters = %i) * (chainlength = %i) + sizeof(q3dpvs_t) == %i bytes, lump is %i bytes)\n", loadmodel->brushq3.num_pvsclusters, loadmodel->brushq3.num_pvschainlength, totalchains + sizeof(*in), l->filelen);
-
-       loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains);
-       memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains);
-}
+               Host_Error("Mod_Q3BSP_LoadPVS: lump too small ((numclusters = %i) * (chainlength = %i) + sizeof(q3dpvs_t) == %i bytes, lump is %i bytes)", loadmodel->brush.num_pvsclusters, loadmodel->brush.num_pvsclusterbytes, totalchains + sizeof(*in), l->filelen);
 
-static void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
-{
-       // FIXME: finish this code
-       VectorCopy(in, out);
+       loadmodel->brush.data_pvsclusters = (unsigned char *)Mem_Alloc(loadmodel->mempool, totalchains);
+       memcpy(loadmodel->brush.data_pvsclusters, (unsigned char *)(in + 1), totalchains);
 }
 
 static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
@@ -4044,20 +5067,19 @@ static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientc
        int i, j, k, index[3];
        float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
        q3dlightgrid_t *a, *s;
-       // FIXME: write this
        if (!model->brushq3.num_lightgrid)
        {
-               ambientcolor[0] += 128;
-               ambientcolor[1] += 128;
-               ambientcolor[2] += 128;
+               ambientcolor[0] = 1;
+               ambientcolor[1] = 1;
+               ambientcolor[2] = 1;
                return;
        }
        Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
        //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
        //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
-       transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0]);
-       transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1]);
-       transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2]);
+       transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0] - 1);
+       transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1] - 1);
+       transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2] - 1);
        index[0] = (int)floor(transformed[0]);
        index[1] = (int)floor(transformed[1]);
        index[2] = (int)floor(transformed[2]);
@@ -4068,12 +5090,18 @@ static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientc
        for (k = 0;k < 2;k++)
        {
                blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2])));
+               if (blend1 < 0.001f || index[2] + k >= model->brushq3.num_lightgrid_isize[2])
+                       continue;
                for (j = 0;j < 2;j++)
                {
                        blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
+                       if (blend2 < 0.001f || index[1] + j >= model->brushq3.num_lightgrid_isize[1])
+                               continue;
                        for (i = 0;i < 2;i++)
                        {
                                blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
+                               if (blend < 0.001f || index[0] + i >= model->brushq3.num_lightgrid_isize[0])
+                                       continue;
                                s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
                                VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
                                VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
@@ -4091,21 +5119,52 @@ static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientc
        //Con_Printf("result: ambient %f %f %f diffuse %f %f %f diffusenormal %f %f %f\n", ambientcolor[0], ambientcolor[1], ambientcolor[2], diffusecolor[0], diffusecolor[1], diffusecolor[2], diffusenormal[0], diffusenormal[1], diffusenormal[2]);
 }
 
-static void Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const vec3_t start, const vec3_t end, vec_t startfrac, vec_t endfrac, const vec3_t linestart, const vec3_t lineend, int markframe)
+static void Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace_t *trace, model_t *model, mnode_t *node, const vec3_t point, int markframe)
+{
+       int i;
+       mleaf_t *leaf;
+       colbrushf_t *brush;
+       // find which leaf the point is in
+       while (node->plane)
+               node = node->children[DotProduct(point, node->plane->normal) < node->plane->dist];
+       // point trace the brushes
+       leaf = (mleaf_t *)node;
+       for (i = 0;i < leaf->numleafbrushes;i++)
+       {
+               brush = model->brush.data_brushes[leaf->firstleafbrush[i]].colbrushf;
+               if (brush && brush->markframe != markframe && BoxesOverlap(point, point, brush->mins, brush->maxs))
+               {
+                       brush->markframe = markframe;
+                       Collision_TracePointBrushFloat(trace, point, brush);
+               }
+       }
+       // can't do point traces on curves (they have no thickness)
+}
+
+static void Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace_t *trace, model_t *model, mnode_t *node, const vec3_t start, const vec3_t end, vec_t startfrac, vec_t endfrac, const vec3_t linestart, const vec3_t lineend, int markframe, const vec3_t segmentmins, const vec3_t segmentmaxs)
 {
        int i, startside, endside;
-       float dist1, dist2, midfrac, mid[3], segmentmins[3], segmentmaxs[3];
-       q3mleaf_t *leaf;
-       q3mface_t *face;
+       float dist1, dist2, midfrac, mid[3], nodesegmentmins[3], nodesegmentmaxs[3];
+       mleaf_t *leaf;
+       msurface_t *surface;
+       mplane_t *plane;
        colbrushf_t *brush;
-       if (startfrac >= trace->fraction)
-               return;
-       // note: all line fragments past first impact fraction are ignored
-       while (node->isnode)
+       // walk the tree until we hit a leaf, recursing for any split cases
+       while (node->plane)
        {
-               // recurse down node sides
-               dist1 = PlaneDiff(start, node->plane);
-               dist2 = PlaneDiff(end, node->plane);
+               plane = node->plane;
+               // axial planes are much more common than non-axial, so an optimized
+               // axial case pays off here
+               if (plane->type < 3)
+               {
+                       dist1 = start[plane->type] - plane->dist;
+                       dist2 = end[plane->type] - plane->dist;
+               }
+               else
+               {
+                       dist1 = DotProduct(start, plane->normal) - plane->dist;
+                       dist2 = DotProduct(end, plane->normal) - plane->dist;
+               }
                startside = dist1 < 0;
                endside = dist2 < 0;
                if (startside == endside)
@@ -4116,305 +5175,231 @@ static void Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace_t *trace, q3mnode_t *node
                else
                {
                        // line crosses node plane, split the line
+                       dist1 = PlaneDiff(linestart, plane);
+                       dist2 = PlaneDiff(lineend, plane);
                        midfrac = dist1 / (dist1 - dist2);
                        VectorLerp(linestart, midfrac, lineend, mid);
                        // take the near side first
-                       Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe);
-                       if (midfrac < trace->fraction)
-                               Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe);
+                       Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
+                       // if we found an impact on the front side, don't waste time
+                       // exploring the far side
+                       if (midfrac <= trace->realfraction)
+                               Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
                        return;
                }
        }
        // hit a leaf
-       segmentmins[0] = min(start[0], end[0]);
-       segmentmins[1] = min(start[1], end[1]);
-       segmentmins[2] = min(start[2], end[2]);
-       segmentmaxs[0] = max(start[0], end[0]);
-       segmentmaxs[1] = max(start[1], end[1]);
-       segmentmaxs[2] = max(start[2], end[2]);
-       leaf = (q3mleaf_t *)node;
+       nodesegmentmins[0] = min(start[0], end[0]) - 1;
+       nodesegmentmins[1] = min(start[1], end[1]) - 1;
+       nodesegmentmins[2] = min(start[2], end[2]) - 1;
+       nodesegmentmaxs[0] = max(start[0], end[0]) + 1;
+       nodesegmentmaxs[1] = max(start[1], end[1]) + 1;
+       nodesegmentmaxs[2] = max(start[2], end[2]) + 1;
+       // line trace the brushes
+       leaf = (mleaf_t *)node;
        for (i = 0;i < leaf->numleafbrushes;i++)
        {
-               if (startfrac >= trace->fraction)
-                       return;
-               brush = leaf->firstleafbrush[i]->colbrushf;
-               if (brush && brush->markframe != markframe)
+               brush = model->brush.data_brushes[leaf->firstleafbrush[i]].colbrushf;
+               if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
                {
                        brush->markframe = markframe;
-                       if (BoxesOverlap(segmentmins, segmentmaxs, brush->mins, brush->maxs))
-                               Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
+                       Collision_TraceLineBrushFloat(trace, linestart, lineend, brush, brush);
                }
        }
-       if (mod_q3bsp_curves_collisions.integer)
+       // can't do point traces on curves (they have no thickness)
+       if (mod_q3bsp_curves_collisions.integer && !VectorCompare(start, end))
        {
-               for (i = 0;i < leaf->numleaffaces;i++)
+               // line trace the curves
+               for (i = 0;i < leaf->numleafsurfaces;i++)
                {
-                       if (startfrac >= trace->fraction)
-                               return;
-                       face = leaf->firstleafface[i];
-                       if (face->collisions && face->collisionmarkframe != markframe)
+                       surface = model->data_surfaces + leaf->firstleafsurface[i];
+                       if (surface->num_collisiontriangles && surface->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, surface->mins, surface->maxs))
                        {
-                               face->collisionmarkframe = markframe;
-                               if (BoxesOverlap(segmentmins, segmentmaxs, face->mins, face->maxs))
-                                       Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
+                               surface->collisionmarkframe = markframe;
+                               Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
                        }
                }
        }
 }
 
-static void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int markframe, const vec3_t segmentmins, const vec3_t segmentmaxs)
+static void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, model_t *model, mnode_t *node, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int markframe, const vec3_t segmentmins, const vec3_t segmentmaxs)
 {
-       int i, sides;
-       float nodesegmentmins[3], nodesegmentmaxs[3];
-       q3mleaf_t *leaf;
+       int i;
+       int sides;
+       mleaf_t *leaf;
        colbrushf_t *brush;
-       q3mface_t *face;
-       nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
-       nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
-       nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
-       nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
-       nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
-       nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
-       if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
-               return;
-       if (node->isnode)
+       msurface_t *surface;
+       mplane_t *plane;
+       float nodesegmentmins[3], nodesegmentmaxs[3];
+       // walk the tree until we hit a leaf, recursing for any split cases
+       while (node->plane)
        {
-               // recurse down node sides
-               sides = BoxOnPlaneSide(segmentmins, segmentmaxs, node->plane);
-               if (sides == 3)
+               plane = node->plane;
+               // axial planes are much more common than non-axial, so an optimized
+               // axial case pays off here
+               if (plane->type < 3)
                {
-                       Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
-                       Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
+                       // this is an axial plane, compare bounding box directly to it and
+                       // recurse sides accordingly
+                       // recurse down node sides
+                       // use an inlined axial BoxOnPlaneSide to slightly reduce overhead
+                       //sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, plane);
+                       //sides = ((segmentmaxs[plane->type] >= plane->dist) | ((segmentmins[plane->type] < plane->dist) << 1));
+                       sides = ((segmentmaxs[plane->type] >= plane->dist) + ((segmentmins[plane->type] < plane->dist) * 2));
                }
-               else if (sides == 2)
-                       Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
-               else // sides == 1
-                       Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
-               /*
-               dist = node->plane->dist - (1.0f / 8.0f);
-               for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
+               else
                {
-                       if (DotProduct(ps->v, node->plane->normal) >= dist || DotProduct(pe->v, node->plane->normal) >= dist)
-                       {
-                               Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
-                               break;
-                       }
+                       // this is a non-axial plane, so check if the start and end boxes
+                       // are both on one side of the plane to handle 'diagonal' cases
+                       sides = BoxOnPlaneSide(thisbrush_start->mins, thisbrush_start->maxs, plane) | BoxOnPlaneSide(thisbrush_end->mins, thisbrush_end->maxs, plane);
                }
-               */
-               /*
-               dist = node->plane->dist + (1.0f / 8.0f);
-               for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
+               if (sides == 3)
                {
-                       if (DotProduct(ps->v, node->plane->normal) <= dist || DotProduct(pe->v, node->plane->normal) <= dist)
-                       {
-                               Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
-                               break;
-                       }
+                       // segment crosses plane
+                       Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
+                       sides = 2;
                }
-               */
-               /*
-               sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane);
-               if (sides & 1)
-                       Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[0], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
-               if (sides & 2)
-                       Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[1], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
-               */
+               // if sides == 0 then the trace itself is bogus (Not A Number values),
+               // in this case we simply pretend the trace hit nothing
+               if (sides == 0)
+                       return; // ERROR: NAN bounding box!
+               // take whichever side the segment box is on
+               node = node->children[sides - 1];
        }
-       else
+       nodesegmentmins[0] = max(segmentmins[0], node->mins[0] - 1);
+       nodesegmentmins[1] = max(segmentmins[1], node->mins[1] - 1);
+       nodesegmentmins[2] = max(segmentmins[2], node->mins[2] - 1);
+       nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0] + 1);
+       nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1] + 1);
+       nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2] + 1);
+       // hit a leaf
+       leaf = (mleaf_t *)node;
+       for (i = 0;i < leaf->numleafbrushes;i++)
        {
-               // hit a leaf
-               leaf = (q3mleaf_t *)node;
-               for (i = 0;i < leaf->numleafbrushes;i++)
+               brush = model->brush.data_brushes[leaf->firstleafbrush[i]].colbrushf;
+               if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
                {
-                       brush = leaf->firstleafbrush[i]->colbrushf;
-                       if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
-                       {
-                               brush->markframe = markframe;
-                               Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
-                       }
+                       brush->markframe = markframe;
+                       Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, brush, brush);
                }
-               if (mod_q3bsp_curves_collisions.integer)
+       }
+       if (mod_q3bsp_curves_collisions.integer)
+       {
+               for (i = 0;i < leaf->numleafsurfaces;i++)
                {
-                       for (i = 0;i < leaf->numleaffaces;i++)
+                       surface = model->data_surfaces + leaf->firstleafsurface[i];
+                       if (surface->num_collisiontriangles && surface->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, surface->mins, surface->maxs))
                        {
-                               face = leaf->firstleafface[i];
-                               // note: this can not be optimized with a face->collisionmarkframe because each triangle of the face would need to be marked as done individually (because each one is bbox culled individually), and if all are marked, then the face could be marked as done
-                               if (face->collisions && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
-                                       Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
+                               surface->collisionmarkframe = markframe;
+                               Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
                        }
                }
        }
 }
 
-static void Mod_Q3BSP_TraceBox(model_t *model, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask)
+static void Mod_Q3BSP_TraceBox(model_t *model, int frame, trace_t *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask)
 {
        int i;
        float segmentmins[3], segmentmaxs[3];
-       colbrushf_t *thisbrush_start, *thisbrush_end;
-       matrix4x4_t startmatrix, endmatrix;
        static int markframe = 0;
-       q3mface_t *face;
+       msurface_t *surface;
+       q3mbrush_t *brush;
        memset(trace, 0, sizeof(*trace));
        trace->fraction = 1;
+       trace->realfraction = 1;
        trace->hitsupercontentsmask = hitsupercontentsmask;
-       Matrix4x4_CreateIdentity(&startmatrix);
-       Matrix4x4_CreateIdentity(&endmatrix);
-       segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
-       segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
-       segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
-       segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
-       segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
-       segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
-       if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs))
-       {
-               // line trace
-               if (model->brushq3.submodel)
-               {
-                       for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
-                               if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
-                                       Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
-                       if (mod_q3bsp_curves_collisions.integer)
+       if (mod_q3bsp_optimizedtraceline.integer && VectorLength2(boxmins) + VectorLength2(boxmaxs) == 0)
+       {
+               if (VectorCompare(start, end))
+               {
+                       // point trace
+                       if (model->brush.submodel)
                        {
-                               for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
-                               {
-                                       face = model->brushq3.data_thismodel->firstface + i;
-                                       if (face->collisions)
-                                               Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
-                               }
+                               for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
+                                       if (brush->colbrushf)
+                                               Collision_TracePointBrushFloat(trace, start, brush->colbrushf);
                        }
+                       else
+                               Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace, model, model->brush.data_nodes, start, ++markframe);
                }
                else
-                       Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe);
-       }
-       else
-       {
-               // box trace, performed as brush trace
-               thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
-               thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
-               if (model->brushq3.submodel)
                {
-                       for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
-                               if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
-                                       Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
-                       if (mod_q3bsp_curves_collisions.integer)
+                       // line trace
+                       segmentmins[0] = min(start[0], end[0]) - 1;
+                       segmentmins[1] = min(start[1], end[1]) - 1;
+                       segmentmins[2] = min(start[2], end[2]) - 1;
+                       segmentmaxs[0] = max(start[0], end[0]) + 1;
+                       segmentmaxs[1] = max(start[1], end[1]) + 1;
+                       segmentmaxs[2] = max(start[2], end[2]) + 1;
+                       if (model->brush.submodel)
                        {
-                               for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
-                               {
-                                       face = model->brushq3.data_thismodel->firstface + i;
-                                       if (face->collisions)
-                                               Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
-                               }
+                               for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
+                                       if (brush->colbrushf)
+                                               Collision_TraceLineBrushFloat(trace, start, end, brush->colbrushf, brush->colbrushf);
+                               if (mod_q3bsp_curves_collisions.integer)
+                                       for (i = 0, surface = model->data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
+                                               if (surface->num_collisiontriangles)
+                                                       Collision_TraceLineTriangleMeshFloat(trace, start, end, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
                        }
+                       else
+                               Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, model->brush.data_nodes, start, end, 0, 1, start, end, ++markframe, segmentmins, segmentmaxs);
                }
-               else
-                       Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
-       }
-}
-
-
-static int Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(const model_t *model, const q3mnode_t *node, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
-{
-       int clusterindex;
-loc0:
-       if (!node->isnode)
-       {
-               // leaf
-               clusterindex = ((q3mleaf_t *)node)->clusterindex;
-               return pvs[clusterindex >> 3] & (1 << (clusterindex & 7));
-       }
-
-       // node - recurse down the BSP tree
-       switch (BoxOnPlaneSide(mins, maxs, node->plane))
-       {
-       case 1: // front
-               node = node->children[0];
-               goto loc0;
-       case 2: // back
-               node = node->children[1];
-               goto loc0;
-       default: // crossing
-               if (Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
-                       return true;
-               node = node->children[1];
-               goto loc0;
        }
-       // never reached
-       return false;
-}
-
-static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
-{
-       return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs);
-}
-
-//Returns PVS data for a given point
-//(note: can return NULL)
-static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p)
-{
-       q3mnode_t *node;
-       Mod_CheckLoaded(model);
-       node = model->brushq3.data_nodes;
-       while (node->isnode)
-               node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
-       if (((q3mleaf_t *)node)->clusterindex >= 0)
-               return model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
        else
-               return NULL;
-}
-
-static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, q3mnode_t *node)
-{
-       int i;
-       float d;
-       qbyte *pvs;
-
-       while (node->isnode)
        {
-               d = PlaneDiff(org, node->plane);
-               if (d > radius)
-                       node = node->children[0];
-               else if (d < -radius)
-                       node = node->children[1];
-               else
+               // box trace, performed as brush trace
+               colbrushf_t *thisbrush_start, *thisbrush_end;
+               vec3_t boxstartmins, boxstartmaxs, boxendmins, boxendmaxs;
+               segmentmins[0] = min(start[0], end[0]) + boxmins[0] - 1;
+               segmentmins[1] = min(start[1], end[1]) + boxmins[1] - 1;
+               segmentmins[2] = min(start[2], end[2]) + boxmins[2] - 1;
+               segmentmaxs[0] = max(start[0], end[0]) + boxmaxs[0] + 1;
+               segmentmaxs[1] = max(start[1], end[1]) + boxmaxs[1] + 1;
+               segmentmaxs[2] = max(start[2], end[2]) + boxmaxs[2] + 1;
+               VectorAdd(start, boxmins, boxstartmins);
+               VectorAdd(start, boxmaxs, boxstartmaxs);
+               VectorAdd(end, boxmins, boxendmins);
+               VectorAdd(end, boxmaxs, boxendmaxs);
+               thisbrush_start = Collision_BrushForBox(&identitymatrix, boxstartmins, boxstartmaxs, 0, 0, NULL);
+               thisbrush_end = Collision_BrushForBox(&identitymatrix, boxendmins, boxendmaxs, 0, 0, NULL);
+               if (model->brush.submodel)
                {
-                       // go down both sides
-                       Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
-                       node = node->children[1];
+                       for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
+                               if (brush->colbrushf)
+                                       Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, brush->colbrushf, brush->colbrushf);
+                       if (mod_q3bsp_curves_collisions.integer)
+                               for (i = 0, surface = model->data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
+                                       if (surface->num_collisiontriangles)
+                                               Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
                }
+               else
+                       Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model, model->brush.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
        }
-       // if this is a leaf with a pvs, accumulate the pvs bits
-       if (((q3mleaf_t *)node)->clusterindex >= 0)
-       {
-               pvs = model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
-               for (i = 0;i < pvsbytes;i++)
-                       pvsbuffer[i] |= pvs[i];
-       }
-       return;
-}
-
-//Calculates a PVS that is the inclusive or of all leafs within radius pixels
-//of the given point.
-static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
-{
-       int bytes = model->brushq3.num_pvschainlength;
-       bytes = min(bytes, pvsbufferlength);
-       memset(pvsbuffer, 0, bytes);
-       Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq3.data_nodes);
-       return bytes;
 }
 
-
 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
 {
        int supercontents = 0;
-       if (nativecontents & Q2CONTENTS_SOLID)
+       if (nativecontents & CONTENTSQ3_SOLID)
                supercontents |= SUPERCONTENTS_SOLID;
-       if (nativecontents & Q2CONTENTS_WATER)
+       if (nativecontents & CONTENTSQ3_WATER)
                supercontents |= SUPERCONTENTS_WATER;
-       if (nativecontents & Q2CONTENTS_SLIME)
+       if (nativecontents & CONTENTSQ3_SLIME)
                supercontents |= SUPERCONTENTS_SLIME;
-       if (nativecontents & Q2CONTENTS_LAVA)
+       if (nativecontents & CONTENTSQ3_LAVA)
                supercontents |= SUPERCONTENTS_LAVA;
+       if (nativecontents & CONTENTSQ3_BODY)
+               supercontents |= SUPERCONTENTS_BODY;
+       if (nativecontents & CONTENTSQ3_CORPSE)
+               supercontents |= SUPERCONTENTS_CORPSE;
+       if (nativecontents & CONTENTSQ3_NODROP)
+               supercontents |= SUPERCONTENTS_NODROP;
+       if (nativecontents & CONTENTSQ3_PLAYERCLIP)
+               supercontents |= SUPERCONTENTS_PLAYERCLIP;
+       if (nativecontents & CONTENTSQ3_MONSTERCLIP)
+               supercontents |= SUPERCONTENTS_MONSTERCLIP;
+       if (nativecontents & CONTENTSQ3_DONOTENTER)
+               supercontents |= SUPERCONTENTS_DONOTENTER;
        return supercontents;
 }
 
@@ -4422,29 +5407,50 @@ static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int superco
 {
        int nativecontents = 0;
        if (supercontents & SUPERCONTENTS_SOLID)
-               nativecontents |= Q2CONTENTS_SOLID;
+               nativecontents |= CONTENTSQ3_SOLID;
        if (supercontents & SUPERCONTENTS_WATER)
-               nativecontents |= Q2CONTENTS_WATER;
+               nativecontents |= CONTENTSQ3_WATER;
        if (supercontents & SUPERCONTENTS_SLIME)
-               nativecontents |= Q2CONTENTS_SLIME;
+               nativecontents |= CONTENTSQ3_SLIME;
        if (supercontents & SUPERCONTENTS_LAVA)
-               nativecontents |= Q2CONTENTS_LAVA;
+               nativecontents |= CONTENTSQ3_LAVA;
+       if (supercontents & SUPERCONTENTS_BODY)
+               nativecontents |= CONTENTSQ3_BODY;
+       if (supercontents & SUPERCONTENTS_CORPSE)
+               nativecontents |= CONTENTSQ3_CORPSE;
+       if (supercontents & SUPERCONTENTS_NODROP)
+               nativecontents |= CONTENTSQ3_NODROP;
+       if (supercontents & SUPERCONTENTS_PLAYERCLIP)
+               nativecontents |= CONTENTSQ3_PLAYERCLIP;
+       if (supercontents & SUPERCONTENTS_MONSTERCLIP)
+               nativecontents |= CONTENTSQ3_MONSTERCLIP;
+       if (supercontents & SUPERCONTENTS_DONOTENTER)
+               nativecontents |= CONTENTSQ3_DONOTENTER;
        return nativecontents;
 }
 
-//extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
-extern void R_Q3BSP_Draw(struct entity_render_s *ent);
-//extern void R_Q3BSP_DrawFakeShadow(struct entity_render_s *ent);
-extern void R_Q3BSP_DrawShadowVolume(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius);
-extern void R_Q3BSP_DrawLight(struct entity_render_s *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz);
-void Mod_Q3BSP_Load(model_t *mod, void *buffer)
+void Mod_Q3BSP_RecursiveFindNumLeafs(mnode_t *node)
 {
-       int i;
+       int numleafs;
+       while (node->plane)
+       {
+               Mod_Q3BSP_RecursiveFindNumLeafs(node->children[0]);
+               node = node->children[1];
+       }
+       numleafs = ((mleaf_t *)node - loadmodel->brush.data_leafs) + 1;
+       if (loadmodel->brush.num_leafs < numleafs)
+               loadmodel->brush.num_leafs = numleafs;
+}
+
+void Mod_Q3BSP_Load(model_t *mod, void *buffer, void *bufferend)
+{
+       int i, j, numshadowmeshtriangles;
        q3dheader_t *header;
        float corner[3], yawradius, modelradius;
+       msurface_t *surface;
 
        mod->type = mod_brushq3;
-       mod->numframes = 1;
+       mod->numframes = 2; // although alternate textures are not supported it is annoying to complain about no such frame 1
        mod->numskins = 1;
 
        header = (q3dheader_t *)buffer;
@@ -4452,32 +5458,55 @@ void Mod_Q3BSP_Load(model_t *mod, void *buffer)
        i = LittleLong(header->version);
        if (i != Q3BSPVERSION)
                Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
+       mod->brush.ishlbsp = false;
+       mod->brush.ismcbsp = false;
        if (loadmodel->isworldmodel)
        {
-               Cvar_SetValue("halflifebsp", false);
-               // until we get a texture for it...
-               R_ResetQuakeSky();
+               Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
+               Cvar_SetValue("mcbsp", mod->brush.ismcbsp);
        }
 
+       mod->soundfromcenter = true;
+       mod->TraceBox = Mod_Q3BSP_TraceBox;
        mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
        mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
-       mod->brush.GetPVS = Mod_Q3BSP_GetPVS;
-       mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
-       mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
+       mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
+       mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
+       mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
+       mod->brush.BoxTouchingLeafPVS = Mod_Q1BSP_BoxTouchingLeafPVS;
+       mod->brush.BoxTouchingVisibleLeafs = Mod_Q1BSP_BoxTouchingVisibleLeafs;
+       mod->brush.FindBoxClusters = Mod_Q1BSP_FindBoxClusters;
        mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
-       mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
-       mod->brush.TraceBox = Mod_Q3BSP_TraceBox;
-       //mod->DrawSky = R_Q3BSP_DrawSky;
-       mod->Draw = R_Q3BSP_Draw;
-       //mod->DrawFakeShadow = R_Q3BSP_DrawFakeShadow;
-       mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume;
-       mod->DrawLight = R_Q3BSP_DrawLight;
+       mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
+       mod->brush.PointInLeaf = Mod_Q1BSP_PointInLeaf;
+       mod->Draw = R_Q1BSP_Draw;
+       mod->GetLightInfo = R_Q1BSP_GetLightInfo;
+       mod->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
+       mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
+       mod->DrawLight = R_Q1BSP_DrawLight;
 
-       mod_base = (qbyte *)header;
+       mod_base = (unsigned char *)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->brush.qw_md4sum = 0;
+       mod->brush.qw_md4sum2 = 0;
+       for (i = 0;i < Q3HEADER_LUMPS;i++)
+       {
+               if (i == Q3LUMP_ENTITIES)
+                       continue;
+               mod->brush.qw_md4sum ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen);
+               if (i == Q3LUMP_PVS || i == Q3LUMP_LEAFS || i == Q3LUMP_NODES)
+                       continue;
+               mod->brush.qw_md4sum2 ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen);
+       }
 
        Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
        Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
@@ -4498,15 +5527,43 @@ void Mod_Q3BSP_Load(model_t *mod, void *buffer)
        Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
        loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
 
-       for (i = 0;i < loadmodel->brushq3.num_models;i++)
+       // the MakePortals code works fine on the q3bsp data as well
+       Mod_Q1BSP_MakePortals();
+
+       // make a single combined shadow mesh to allow optimized shadow volume creation
+       numshadowmeshtriangles = 0;
+       for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
        {
-               if (i == 0)
-                       mod = loadmodel;
-               else
+               surface->num_firstshadowmeshtriangle = numshadowmeshtriangles;
+               numshadowmeshtriangles += surface->num_triangles;
+       }
+       loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true);
+       for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
+               if (surface->num_triangles > 0)
+                       Mod_ShadowMesh_AddMesh(loadmodel->mempool, loadmodel->brush.shadowmesh, NULL, NULL, NULL, loadmodel->surfmesh.data_vertex3f, NULL, NULL, NULL, NULL, surface->num_triangles, (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
+       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->brush.num_leafs = 0;
+       Mod_Q3BSP_RecursiveFindNumLeafs(loadmodel->brush.data_nodes);
+
+       if (loadmodel->isworldmodel)
+       {
+               // clear out any stale submodels or worldmodels lying around
+               // if we did this clear before now, an error might abort loading and
+               // leave things in a bad state
+               Mod_RemoveStaleWorldModels(loadmodel);
+       }
+
+       mod = loadmodel;
+       for (i = 0;i < loadmodel->brush.numsubmodels;i++)
+       {
+               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
@@ -4517,12 +5574,28 @@ void Mod_Q3BSP_Load(model_t *mod, void *buffer)
                        // textures and memory belong to the main model
                        mod->texturepool = NULL;
                        mod->mempool = NULL;
+                       mod->brush.GetPVS = NULL;
+                       mod->brush.FatPVS = NULL;
+                       mod->brush.BoxTouchingPVS = NULL;
+                       mod->brush.BoxTouchingLeafPVS = NULL;
+                       mod->brush.BoxTouchingVisibleLeafs = NULL;
+                       mod->brush.FindBoxClusters = NULL;
+                       mod->brush.LightPoint = NULL;
+                       mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
                }
-               mod->brushq3.data_thismodel = loadmodel->brushq3.data_models + i;
-               mod->brushq3.submodel = i;
-
-               VectorCopy(mod->brushq3.data_thismodel->mins, mod->normalmins);
-               VectorCopy(mod->brushq3.data_thismodel->maxs, mod->normalmaxs);
+               mod->brush.submodel = i;
+
+               // make the model surface list (used by shadowing/lighting)
+               mod->firstmodelsurface = mod->brushq3.data_models[i].firstface;
+               mod->nummodelsurfaces = mod->brushq3.data_models[i].numfaces;
+               mod->firstmodelbrush = mod->brushq3.data_models[i].firstbrush;
+               mod->nummodelbrushes = mod->brushq3.data_models[i].numbrushes;
+               mod->surfacelist = (int *)Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->surfacelist));
+               for (j = 0;j < mod->nummodelsurfaces;j++)
+                       mod->surfacelist[j] = mod->firstmodelsurface + j;
+
+               VectorCopy(mod->brushq3.data_models[i].mins, mod->normalmins);
+               VectorCopy(mod->brushq3.data_models[i].maxs, mod->normalmaxs);
                corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
                corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
                corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
@@ -4536,22 +5609,28 @@ void Mod_Q3BSP_Load(model_t *mod, void *buffer)
                mod->yawmaxs[2] = mod->normalmaxs[2];
                mod->radius = modelradius;
                mod->radius2 = modelradius * modelradius;
+
+               for (j = 0;j < mod->nummodelsurfaces;j++)
+                       if (mod->data_surfaces[j + mod->firstmodelsurface].texture->surfaceflags & Q3SURFACEFLAG_SKY)
+                               break;
+               if (j < mod->nummodelsurfaces)
+                       mod->DrawSky = R_Q1BSP_DrawSky;
        }
 }
 
-void Mod_IBSP_Load(model_t *mod, void *buffer)
+void Mod_IBSP_Load(model_t *mod, void *buffer, void *bufferend)
 {
        int i = LittleLong(((int *)buffer)[1]);
        if (i == Q3BSPVERSION)
-               Mod_Q3BSP_Load(mod,buffer);
+               Mod_Q3BSP_Load(mod,buffer, bufferend);
        else if (i == Q2BSPVERSION)
-               Mod_Q2BSP_Load(mod,buffer);
+               Mod_Q2BSP_Load(mod,buffer, bufferend);
        else
-               Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
+               Host_Error("Mod_IBSP_Load: unknown/unsupported version %i", i);
 }
 
-void Mod_MAP_Load(model_t *mod, void *buffer)
+void Mod_MAP_Load(model_t *mod, void *buffer, void *bufferend)
 {
-       Host_Error("Mod_MAP_Load: not yet implemented\n");
+       Host_Error("Mod_MAP_Load: not yet implemented");
 }