]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - model_brush.c
CONTRIBUTING: Fix typos
[xonotic/darkplaces.git] / model_brush.c
index 6277339f5a8892edc4893dc4b1a8b151d7a7b3bc..19c17a40751d84e8a3ed358c50139f9f4544fdcb 100644 (file)
@@ -26,28 +26,21 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "wad.h"
 
 
+cvar_t r_trippy = {CF_CLIENT, "r_trippy", "0", "easter egg"};
 //cvar_t r_subdivide_size = {CF_CLIENT | CF_ARCHIVE, "r_subdivide_size", "128", "how large water polygons should be (smaller values produce more polygons which give better warping effects)"};
-cvar_t mod_bsp_portalize = {CF_CLIENT | CF_SERVER, "mod_bsp_portalize", "1", "enables portal generation from BSP tree (may take several seconds per map), used by r_drawportals, r_useportalculling, r_shadow_realtime_world_compileportalculling, sv_cullentities_portal"};
 cvar_t r_novis = {CF_CLIENT, "r_novis", "0", "draws whole level, see also sv_cullentities_pvs 0"};
 cvar_t r_nosurftextures = {CF_CLIENT, "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 = {CF_CLIENT, "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 = {CF_CLIENT, "r_subdivisions_mintess", "0", "minimum number of subdivisions (values above 0 will smooth curves that don't need it)"};
 cvar_t r_subdivisions_maxtess = {CF_CLIENT, "r_subdivisions_maxtess", "1024", "maximum number of subdivisions (prevents curves beyond a certain detail level, limits smoothing)"};
 cvar_t r_subdivisions_maxvertices = {CF_CLIENT, "r_subdivisions_maxvertices", "65536", "maximum vertices allowed per subdivided curve"};
-cvar_t r_subdivisions_collision_tolerance = {CF_CLIENT, "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 = {CF_CLIENT, "r_subdivisions_collision_mintess", "0", "minimum number of subdivisions (values above 0 will smooth curves that don't need it)"};
-cvar_t r_subdivisions_collision_maxtess = {CF_CLIENT, "r_subdivisions_collision_maxtess", "1024", "maximum number of subdivisions (prevents curves beyond a certain detail level, limits smoothing)"};
-cvar_t r_subdivisions_collision_maxvertices = {CF_CLIENT, "r_subdivisions_collision_maxvertices", "4225", "maximum vertices allowed per subdivided curve"};
-cvar_t r_trippy = {CF_CLIENT, "r_trippy", "0", "easter egg"};
-cvar_t r_fxaa = {CF_CLIENT | CF_ARCHIVE, "r_fxaa", "0", "fast approximate anti aliasing"};
-cvar_t mod_noshader_default_offsetmapping = {CF_CLIENT | CF_ARCHIVE, "mod_noshader_default_offsetmapping", "1", "use offsetmapping by default on all surfaces that are not using q3 shader files"};
-cvar_t mod_obj_orientation = {CF_CLIENT | CF_SERVER, "mod_obj_orientation", "1", "fix orientation of OBJ models to the usual conventions (if zero, use coordinates as is)"};
-cvar_t mod_q2bsp_littransparentsurfaces = {CF_CLIENT, "mod_q2bsp_littransparentsurfaces", "0", "allows lighting on rain in 3v3gloom3 and other cases of transparent surfaces that have lightmaps that were ignored by quake2"};
+cvar_t mod_q3bsp_curves_subdivisions_tolerance = {CF_CLIENT | CF_SERVER, "mod_q3bsp_curves_subdivisions_tolerance", "15", "maximum error tolerance on curve subdivision for collision purposes (usually a larger error tolerance than for rendering)"};
+cvar_t mod_q3bsp_curves_subdivisions_mintess = {CF_CLIENT | CF_SERVER, "mod_q3bsp_curves_subdivisions_mintess", "0", "minimum number of subdivisions for collision purposes (values above 0 will smooth curves that don't need it)"};
+cvar_t mod_q3bsp_curves_subdivisions_maxtess = {CF_CLIENT | CF_SERVER, "mod_q3bsp_curves_subdivisions_maxtess", "1024", "maximum number of subdivisions for collision purposes (prevents curves beyond a certain detail level, limits smoothing)"};
+cvar_t mod_q3bsp_curves_subdivisions_maxvertices = {CF_CLIENT | CF_SERVER, "mod_q3bsp_curves_subdivisions_maxvertices", "4225", "maximum vertices allowed per subdivided curve for collision purposes"};
 cvar_t mod_q3bsp_curves_collisions = {CF_CLIENT | CF_SERVER, "mod_q3bsp_curves_collisions", "1", "enables collisions with curves (SLOW)"};
-cvar_t mod_q3bsp_curves_collisions_stride = {CF_CLIENT | CF_SERVER, "mod_q3bsp_curves_collisions_stride", "16", "collisions against curves: optimize performance by doing a combined collision check for this triangle amount first (-1 avoids any box tests)"};
-cvar_t mod_q3bsp_curves_stride = {CF_CLIENT | CF_SERVER, "mod_q3bsp_curves_stride", "16", "particle effect collisions against curves: optimize performance by doing a combined collision check for this triangle amount first (-1 avoids any box tests)"};
 cvar_t mod_q3bsp_optimizedtraceline = {CF_CLIENT | CF_SERVER, "mod_q3bsp_optimizedtraceline", "1", "whether to use optimized traceline code for line traces (as opposed to tracebox code)"};
-cvar_t mod_q3bsp_debugtracebrush = {CF_CLIENT | CF_SERVER, "mod_q3bsp_debugtracebrush", "0", "selects different tracebrush bsp recursion algorithms (for debugging purposes only)"};
 cvar_t mod_q3bsp_lightmapmergepower = {CF_CLIENT | CF_ARCHIVE, "mod_q3bsp_lightmapmergepower", "4", "merges the quake3 128x128 lightmap textures into larger lightmap group textures to speed up rendering, 1 = 256x256, 2 = 512x512, 3 = 1024x1024, 4 = 2048x2048, 5 = 4096x4096, ..."};
 cvar_t mod_q3bsp_nolightmaps = {CF_CLIENT | CF_ARCHIVE, "mod_q3bsp_nolightmaps", "0", "do not load lightmaps in Q3BSP maps (to save video RAM, but be warned: it looks ugly)"};
 cvar_t mod_q3bsp_tracelineofsight_brushes = {CF_CLIENT | CF_SERVER, "mod_q3bsp_tracelineofsight_brushes", "0", "enables culling of entities behind detail brushes, curves, etc"};
@@ -55,6 +48,7 @@ cvar_t mod_q3bsp_sRGBlightmaps = {CF_CLIENT, "mod_q3bsp_sRGBlightmaps", "0", "tr
 cvar_t mod_q3bsp_lightgrid_texture = {CF_CLIENT, "mod_q3bsp_lightgrid_texture", "1", "directly apply the lightgrid as a global texture rather than only reading it at the entity origin"};
 cvar_t mod_q3bsp_lightgrid_world_surfaces = {CF_CLIENT, "mod_q3bsp_lightgrid_world_surfaces", "0", "apply lightgrid lighting to the world bsp geometry rather than using lightmaps (experimental/debug tool)"};
 cvar_t mod_q3bsp_lightgrid_bsp_surfaces = {CF_CLIENT, "mod_q3bsp_lightgrid_bsp_surfaces", "0", "apply lightgrid lighting to bsp models other than the world rather than using their lightmaps (experimental/debug tool)"};
+cvar_t mod_noshader_default_offsetmapping = {CF_CLIENT | CF_ARCHIVE, "mod_noshader_default_offsetmapping", "1", "use offsetmapping by default on all surfaces that are not using q3 shader files"};
 cvar_t mod_q3shader_default_offsetmapping = {CF_CLIENT | CF_ARCHIVE, "mod_q3shader_default_offsetmapping", "1", "use offsetmapping by default on all surfaces that are using q3 shader files"};
 cvar_t mod_q3shader_default_offsetmapping_scale = {CF_CLIENT | CF_ARCHIVE, "mod_q3shader_default_offsetmapping_scale", "1", "default scale used for offsetmapping"};
 cvar_t mod_q3shader_default_offsetmapping_bias = {CF_CLIENT | CF_ARCHIVE, "mod_q3shader_default_offsetmapping_bias", "0", "default bias used for offsetmapping"};
@@ -64,9 +58,17 @@ cvar_t mod_q3shader_default_refractive_index = {CF_CLIENT, "mod_q3shader_default
 cvar_t mod_q3shader_force_addalpha = {CF_CLIENT, "mod_q3shader_force_addalpha", "0", "treat GL_ONE GL_ONE (or add) blendfunc as GL_SRC_ALPHA GL_ONE for compatibility with older DarkPlaces releases"};
 cvar_t mod_q3shader_force_terrain_alphaflag = {CF_CLIENT, "mod_q3shader_force_terrain_alphaflag", "0", "for multilayered terrain shaders force TEXF_ALPHA flag on both layers"};
 
+cvar_t mod_q2bsp_littransparentsurfaces = {CF_CLIENT, "mod_q2bsp_littransparentsurfaces", "0", "allows lighting on rain in 3v3gloom3 and other cases of transparent surfaces that have lightmaps that were ignored by quake2"};
+
 cvar_t mod_q1bsp_polygoncollisions = {CF_CLIENT | CF_SERVER, "mod_q1bsp_polygoncollisions", "0", "disables use of precomputed cliphulls and instead collides with polygons (uses Bounding Interval Hierarchy optimizations)"};
+cvar_t mod_q1bsp_traceoutofsolid = {CF_SHARED, "mod_q1bsp_traceoutofsolid", "1", "enables tracebox to move an entity that's stuck in solid brushwork out to empty space, 1 matches FTEQW and QSS and is required by many community maps (items/monsters will be missing otherwise), 0 matches old versions of DP and the original Quake engine (if your map or QC needs 0 it's buggy)"};
+cvar_t mod_q1bsp_zero_hullsize_cutoff = {CF_CLIENT | CF_SERVER, "mod_q1bsp_zero_hullsize_cutoff", "3", "bboxes with an X dimension smaller than this will use the smallest cliphull (0x0x0) instead of being rounded up to the player cliphull (32x32x56) in Q1BSP, or crouching player (32x32x36) in HLBSP"};
+
+cvar_t mod_bsp_portalize = {CF_CLIENT, "mod_bsp_portalize", "0", "enables portal generation from BSP tree (takes a minute or more and GBs of memory when loading a complex map), used by r_drawportals, r_useportalculling, r_shadow_realtime_dlight_portalculling, r_shadow_realtime_world_compileportalculling"};
 cvar_t mod_recalculatenodeboxes = {CF_CLIENT | CF_SERVER, "mod_recalculatenodeboxes", "1", "enables use of generated node bounding boxes based on BSP tree portal reconstruction, rather than the node boxes supplied by the map compiler"};
 
+cvar_t mod_obj_orientation = {CF_CLIENT | CF_SERVER, "mod_obj_orientation", "1", "fix orientation of OBJ models to the usual conventions (if zero, use coordinates as is)"};
+
 static texture_t mod_q1bsp_texture_solid;
 static texture_t mod_q1bsp_texture_sky;
 static texture_t mod_q1bsp_texture_lava;
@@ -85,20 +87,16 @@ void Mod_BrushInit(void)
        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_subdivisions_tolerance);
+       Cvar_RegisterVariable(&mod_q3bsp_curves_subdivisions_mintess);
+       Cvar_RegisterVariable(&mod_q3bsp_curves_subdivisions_maxtess);
+       Cvar_RegisterVariable(&mod_q3bsp_curves_subdivisions_maxvertices);
        Cvar_RegisterVariable(&r_trippy);
-       Cvar_RegisterVariable(&r_fxaa);
        Cvar_RegisterVariable(&mod_noshader_default_offsetmapping);
        Cvar_RegisterVariable(&mod_obj_orientation);
        Cvar_RegisterVariable(&mod_q2bsp_littransparentsurfaces);
        Cvar_RegisterVariable(&mod_q3bsp_curves_collisions);
-       Cvar_RegisterVariable(&mod_q3bsp_curves_collisions_stride);
-       Cvar_RegisterVariable(&mod_q3bsp_curves_stride);
        Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline);
-       Cvar_RegisterVariable(&mod_q3bsp_debugtracebrush);
        Cvar_RegisterVariable(&mod_q3bsp_lightmapmergepower);
        Cvar_RegisterVariable(&mod_q3bsp_nolightmaps);
        Cvar_RegisterVariable(&mod_q3bsp_sRGBlightmaps);
@@ -115,6 +113,8 @@ void Mod_BrushInit(void)
        Cvar_RegisterVariable(&mod_q3shader_force_addalpha);
        Cvar_RegisterVariable(&mod_q3shader_force_terrain_alphaflag);
        Cvar_RegisterVariable(&mod_q1bsp_polygoncollisions);
+       Cvar_RegisterVariable(&mod_q1bsp_traceoutofsolid);
+       Cvar_RegisterVariable(&mod_q1bsp_zero_hullsize_cutoff);
        Cvar_RegisterVariable(&mod_recalculatenodeboxes);
 
        // these games were made for older DP engines and are no longer
@@ -123,27 +123,27 @@ void Mod_BrushInit(void)
                Cvar_SetQuick(&mod_q3shader_force_addalpha, "1");
 
        memset(&mod_q1bsp_texture_solid, 0, sizeof(mod_q1bsp_texture_solid));
-       strlcpy(mod_q1bsp_texture_solid.name, "solid" , sizeof(mod_q1bsp_texture_solid.name));
+       dp_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));
+       dp_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));
+       dp_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));
+       dp_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));
+       dp_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;
 }
@@ -471,7 +471,7 @@ typedef struct findnonsolidlocationinfo_s
 }
 findnonsolidlocationinfo_t;
 
-static void Mod_Q1BSP_FindNonSolidLocation_r_Triangle(findnonsolidlocationinfo_t *info, msurface_t *surface, int k)
+static void Mod_BSP_FindNonSolidLocation_r_Triangle(findnonsolidlocationinfo_t *info, msurface_t *surface, int k)
 {
        int i, *tri;
        float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3];
@@ -559,7 +559,7 @@ static void Mod_Q1BSP_FindNonSolidLocation_r_Triangle(findnonsolidlocationinfo_t
        }
 }
 
-static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
+static void Mod_BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
 {
        int surfacenum, k, *mark;
        msurface_t *surface;
@@ -572,26 +572,26 @@ static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *in
                {
                        for (k = 0;k < surface->num_triangles;k++)
                        {
-                               Mod_Q1BSP_FindNonSolidLocation_r_Triangle(info, surface, k);
+                               Mod_BSP_FindNonSolidLocation_r_Triangle(info, surface, k);
                        }
                }
        }
 }
 
-static void Mod_Q1BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node)
+static void Mod_BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node)
 {
        if (node->plane)
        {
                float f = PlaneDiff(info->center, node->plane);
                if (f >= -info->bestdist)
-                       Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[0]);
+                       Mod_BSP_FindNonSolidLocation_r(info, node->children[0]);
                if (f <= info->bestdist)
-                       Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[1]);
+                       Mod_BSP_FindNonSolidLocation_r(info, node->children[1]);
        }
        else
        {
                if (((mleaf_t *)node)->numleafsurfaces)
-                       Mod_Q1BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node);
+                       Mod_BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node);
        }
 }
 
@@ -620,7 +620,7 @@ static void Mod_BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t
                info.absmax[0] += info.radius + 1;
                info.absmax[1] += info.radius + 1;
                info.absmax[2] += info.radius + 1;
-               Mod_Q1BSP_FindNonSolidLocation_r(&info, model->brush.data_nodes + model->brushq1.hulls[0].firstclipnode);
+               Mod_BSP_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);
@@ -762,7 +762,7 @@ static int Mod_Q1BSP_RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int nu
                        // recurse both sides, front side first
                        ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[p1side], p1f, midf, p1, mid);
                        // if this side is not empty, return what it is (solid or done)
-                       if (ret != HULLCHECKSTATE_EMPTY)
+                       if (ret != HULLCHECKSTATE_EMPTY && (!t->trace->allsolid || !mod_q1bsp_traceoutofsolid.integer))
                                return ret;
 
                        ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[p2side], midf, p2f, mid, p2);
@@ -976,7 +976,7 @@ static void Mod_Q1BSP_TraceBox(struct model_s *model, const frameblend_t *frameb
        rhc.trace->fraction = 1;
        rhc.trace->allsolid = true;
        VectorSubtract(boxmaxs, boxmins, boxsize);
-       if (boxsize[0] < 3)
+       if (boxsize[0] < mod_q1bsp_zero_hullsize_cutoff.value)
                rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
        else if (model->brush.ishlbsp)
        {
@@ -1005,7 +1005,7 @@ static void Mod_Q1BSP_TraceBox(struct model_s *model, const frameblend_t *frameb
        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]);
+       Con_Printf("t(%f %f %f,%f %f %f,%li %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);
        {
 
@@ -1627,8 +1627,21 @@ static void Mod_Q1BSP_LoadSplitSky (unsigned char *src, int width, int height, i
                }
        }
 
-       loadmodel->brush.solidskyskinframe = R_SkinFrame_LoadInternalBGRA("sky_solidtexture", 0         , (unsigned char *) solidpixels, w, h, 0, 0, 0, vid.sRGB3D);
-       loadmodel->brush.alphaskyskinframe = R_SkinFrame_LoadInternalBGRA("sky_alphatexture", TEXF_ALPHA, (unsigned char *) alphapixels, w, h, 0, 0, 0, vid.sRGB3D);
+       // Load the solid and alpha parts of the sky texture as separate textures
+       loadmodel->brush.solidskyskinframe = R_SkinFrame_LoadInternalBGRA(
+               "sky_solidtexture",
+               0,
+               (unsigned char *) solidpixels,
+               w, h, w, h,
+               CRC_Block((unsigned char *) solidpixels, w*h*4),
+               vid.sRGB3D);
+       loadmodel->brush.alphaskyskinframe = R_SkinFrame_LoadInternalBGRA(
+               "sky_alphatexture",
+               TEXF_ALPHA,
+               (unsigned char *) alphapixels,
+               w, h, w, h,
+               CRC_Block((unsigned char *) alphapixels, w*h*4),
+               vid.sRGB3D);
        Mem_Free(solidpixels);
        Mem_Free(alphapixels);
 }
@@ -1685,6 +1698,10 @@ static void Mod_Q1BSP_LoadTextures(sizebuf_t *sb)
                        // pretty up the buffer (replacing any trailing garbage with 0)
                        for (j = (int)strlen(name); j < 16; j++)
                                name[j] = 0;
+                       // bones_was_here: force all names to lowercase (matching code below) so we don't crash on e2m9
+                       for (j = 0;name[j];j++)
+                               if (name[j] >= 'A' && name[j] <= 'Z')
+                                       name[j] += 'a' - 'A';
 
                        if (!strncmp(name, "sky", 3))
                                numsky++;
@@ -1712,7 +1729,7 @@ static void Mod_Q1BSP_LoadTextures(sizebuf_t *sb)
        skinframemissing = R_SkinFrame_LoadMissing();
        for (i = 0, tx = loadmodel->data_textures;i < loadmodel->num_textures;i++, tx++)
        {
-               strlcpy(tx->name, "NO TEXTURE FOUND", sizeof(tx->name));
+               dp_strlcpy(tx->name, "NO TEXTURE FOUND", sizeof(tx->name));
                tx->width = 16;
                tx->height = 16;
                tx->basealpha = 1.0f;
@@ -1830,7 +1847,7 @@ static void Mod_Q1BSP_LoadTextures(sizebuf_t *sb)
                // no luck with loading shaders or external textures - restore the in-progress texture loading
                loadmodel->data_textures[i] = backuptex;
 
-               strlcpy(tx->name, name, sizeof(tx->name));
+               dp_strlcpy(tx->name, name, sizeof(tx->name));
                tx->width = mtwidth;
                tx->height = mtheight;
                tx->basealpha = 1.0f;
@@ -2107,11 +2124,11 @@ static void Mod_Q1BSP_LoadLighting(sizebuf_t *sb)
        else // LadyHavoc: bsp version 29 (normal white lighting)
        {
                // LadyHavoc: hope is not lost yet, check for a .lit file to load
-               strlcpy (litfilename, loadmodel->name, sizeof (litfilename));
+               dp_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));
+               dp_strlcpy (dlitfilename, litfilename, sizeof (dlitfilename));
+               dp_strlcat (litfilename, ".lit", sizeof (litfilename));
+               dp_strlcat (dlitfilename, ".dlit", sizeof (dlitfilename));
                data = (unsigned char*) FS_LoadFile(litfilename, tempmempool, false, &filesize);
                if (data)
                {
@@ -2204,9 +2221,9 @@ static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
                if (com_token[0] == '}')
                        break; // end of worldspawn
                if (com_token[0] == '_')
-                       strlcpy(key, com_token + 1, sizeof(key));
+                       dp_strlcpy(key, com_token + 1, sizeof(key));
                else
-                       strlcpy(key, com_token, sizeof(key));
+                       dp_strlcpy(key, com_token, sizeof(key));
                while (key[strlen(key)-1] == ' ') // remove trailing spaces
                        key[strlen(key)-1] = 0;
                if (!COM_ParseToken_Simple(&data, false, false, true))
@@ -3375,15 +3392,15 @@ portal_t;
 
 static memexpandablearray_t portalarray;
 
-static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
+static void Mod_BSP_RecursiveRecalcNodeBBox(mnode_t *node)
 {
        // process only nodes (leafs already had their box calculated)
        if (!node->plane)
                return;
 
        // calculate children first
-       Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
-       Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
+       Mod_BSP_RecursiveRecalcNodeBBox(node->children[0]);
+       Mod_BSP_RecursiveRecalcNodeBBox(node->children[1]);
 
        // make combined bounding box from children
        node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
@@ -3517,7 +3534,7 @@ static void Mod_BSP_FinalizePortals(void)
        }
        // now recalculate the node bounding boxes from the leafs
        if (mod_recalculatenodeboxes.integer)
-               Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brush.data_nodes + loadmodel->brushq1.hulls[0].firstclipnode);
+               Mod_BSP_RecursiveRecalcNodeBBox(loadmodel->brush.data_nodes + loadmodel->brushq1.hulls[0].firstclipnode);
 }
 
 /*
@@ -3826,11 +3843,11 @@ static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, co
        const hull_t *hull;
 
        VectorSubtract(inmaxs, inmins, size);
-       if (cmodel->brush.ishlbsp)
+       if (size[0] < mod_q1bsp_zero_hullsize_cutoff.value)
+               hull = &cmodel->brushq1.hulls[0]; // 0x0x0
+       else if (cmodel->brush.ishlbsp)
        {
-               if (size[0] < 3)
-                       hull = &cmodel->brushq1.hulls[0]; // 0x0x0
-               else if (size[0] <= 32)
+               if (size[0] <= 32)
                {
                        if (size[2] < 54) // pick the nearest of 36 or 72
                                hull = &cmodel->brushq1.hulls[3]; // 32x32x36
@@ -3842,9 +3859,7 @@ static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, co
        }
        else
        {
-               if (size[0] < 3)
-                       hull = &cmodel->brushq1.hulls[0]; // 0x0x0
-               else if (size[0] <= 32)
+               if (size[0] <= 32)
                        hull = &cmodel->brushq1.hulls[1]; // 32x32x56
                else
                        hull = &cmodel->brushq1.hulls[2]; // 64x64x88
@@ -3887,7 +3902,8 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer, void *bufferend)
        hullinfo_t hullinfo;
        int totalstylesurfaces, totalstyles, stylecounts[256], remapstyles[256];
        model_brush_lightstyleinfo_t styleinfo[256];
-       unsigned char *datapointer;
+       int *datapointer;
+       model_brush_lightstyleinfo_t *lsidatapointer;
        sizebuf_t sb;
 
        MSG_InitReadBuffer(&sb, (unsigned char *)buffer, (unsigned char *)bufferend - (unsigned char *)buffer);
@@ -4008,7 +4024,7 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer, void *bufferend)
        mod->brushq1.num_compressedpvs = 0;
 
        Mod_Q1BSP_MakeHull0();
-       if (mod_bsp_portalize.integer)
+       if (mod_bsp_portalize.integer && cls.state != ca_dedicated)
                Mod_BSP_MakePortals();
 
        mod->numframes = 2;             // regular and alternate animation
@@ -4049,8 +4065,11 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer, void *bufferend)
                                totalstylesurfaces += stylecounts[k];
                }
        }
-       datapointer = (unsigned char *)Mem_Alloc(mod->mempool, mod->num_surfaces * sizeof(int) + totalstyles * sizeof(model_brush_lightstyleinfo_t) + totalstylesurfaces * sizeof(int *));
-       mod->modelsurfaces_sorted = (int*)datapointer;datapointer += mod->num_surfaces * sizeof(int);
+       // bones_was_here: using a separate allocation for model_brush_lightstyleinfo_t
+       // because on a 64-bit machine it no longer has the same alignment requirement as int.
+       lsidatapointer = Mem_AllocType(mod->mempool, model_brush_lightstyleinfo_t, totalstyles * sizeof(model_brush_lightstyleinfo_t));
+       datapointer = Mem_AllocType(mod->mempool, int, mod->num_surfaces * sizeof(int) + totalstylesurfaces * sizeof(int));
+       mod->modelsurfaces_sorted = datapointer;datapointer += mod->num_surfaces;
        for (i = 0;i < mod->brush.numsubmodels;i++)
        {
                // LadyHavoc: this code was originally at the end of this loop, but
@@ -4065,7 +4084,7 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer, void *bufferend)
                        // copy the base model to this one
                        *mod = *loadmodel;
                        // rename the clone back to its proper name
-                       strlcpy(mod->name, name, sizeof(mod->name));
+                       dp_strlcpy(mod->name, name, sizeof(mod->name));
                        mod->brush.parentmodel = loadmodel;
                        // textures and memory belong to the main model
                        mod->texturepool = NULL;
@@ -4142,7 +4161,7 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer, void *bufferend)
                                        styleinfo[mod->brushq1.num_lightstyles].style = k;
                                        styleinfo[mod->brushq1.num_lightstyles].value = 0;
                                        styleinfo[mod->brushq1.num_lightstyles].numsurfaces = 0;
-                                       styleinfo[mod->brushq1.num_lightstyles].surfacelist = (int *)datapointer;datapointer += stylecounts[k] * sizeof(int);
+                                       styleinfo[mod->brushq1.num_lightstyles].surfacelist = datapointer;datapointer += stylecounts[k];
                                        remapstyles[k] = mod->brushq1.num_lightstyles;
                                        mod->brushq1.num_lightstyles++;
                                }
@@ -4159,7 +4178,7 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer, void *bufferend)
                                        }
                                }
                        }
-                       mod->brushq1.data_lightstyleinfo = (model_brush_lightstyleinfo_t *)datapointer;datapointer += mod->brushq1.num_lightstyles * sizeof(model_brush_lightstyleinfo_t);
+                       mod->brushq1.data_lightstyleinfo = lsidatapointer;lsidatapointer += mod->brushq1.num_lightstyles;
                        memcpy(mod->brushq1.data_lightstyleinfo, styleinfo, mod->brushq1.num_lightstyles * sizeof(model_brush_lightstyleinfo_t));
                }
                else
@@ -4824,7 +4843,8 @@ static void Mod_Q2BSP_Load(model_t *mod, void *buffer, void *bufferend)
        msurface_t *surface;
        int totalstylesurfaces, totalstyles, stylecounts[256], remapstyles[256];
        model_brush_lightstyleinfo_t styleinfo[256];
-       unsigned char *datapointer;
+       int *datapointer;
+       model_brush_lightstyleinfo_t *lsidatapointer;
        sizebuf_t sb;
 
        MSG_InitReadBuffer(&sb, (unsigned char *)buffer, (unsigned char *)bufferend - (unsigned char *)buffer);
@@ -4939,7 +4959,7 @@ static void Mod_Q2BSP_Load(model_t *mod, void *buffer, void *bufferend)
        mod->brushq1.num_compressedpvs = 0;
 
        // the MakePortals code works fine on the q2bsp data as well
-       if (mod_bsp_portalize.integer)
+       if (mod_bsp_portalize.integer && cls.state != ca_dedicated)
                Mod_BSP_MakePortals();
 
        mod->numframes = 0;             // q2bsp animations are kind of special, frame is unbounded...
@@ -4966,8 +4986,11 @@ static void Mod_Q2BSP_Load(model_t *mod, void *buffer, void *bufferend)
                                totalstylesurfaces += stylecounts[k];
                }
        }
-       datapointer = (unsigned char *)Mem_Alloc(mod->mempool, mod->num_surfaces * sizeof(int) + totalstyles * sizeof(model_brush_lightstyleinfo_t) + totalstylesurfaces * sizeof(int *));
-       mod->modelsurfaces_sorted = (int*)datapointer; datapointer += mod->num_surfaces * sizeof(int);
+       // bones_was_here: using a separate allocation for model_brush_lightstyleinfo_t
+       // because on a 64-bit machine it no longer has the same alignment requirement as int.
+       lsidatapointer = Mem_AllocType(mod->mempool, model_brush_lightstyleinfo_t, totalstyles * sizeof(model_brush_lightstyleinfo_t));
+       datapointer = Mem_AllocType(mod->mempool, int, mod->num_surfaces * sizeof(int) + totalstylesurfaces * sizeof(int));
+       mod->modelsurfaces_sorted = datapointer; datapointer += mod->num_surfaces;
        // set up the world model, then on each submodel copy from the world model
        // and set up the submodel with the respective model info.
        mod = loadmodel;
@@ -4984,7 +5007,7 @@ static void Mod_Q2BSP_Load(model_t *mod, void *buffer, void *bufferend)
                        // copy the base model to this one
                        *mod = *loadmodel;
                        // rename the clone back to its proper name
-                       strlcpy(mod->name, name, sizeof(mod->name));
+                       dp_strlcpy(mod->name, name, sizeof(mod->name));
                        mod->brush.parentmodel = loadmodel;
                        // textures and memory belong to the main model
                        mod->texturepool = NULL;
@@ -5070,7 +5093,7 @@ static void Mod_Q2BSP_Load(model_t *mod, void *buffer, void *bufferend)
                                        styleinfo[mod->brushq1.num_lightstyles].style = k;
                                        styleinfo[mod->brushq1.num_lightstyles].value = 0;
                                        styleinfo[mod->brushq1.num_lightstyles].numsurfaces = 0;
-                                       styleinfo[mod->brushq1.num_lightstyles].surfacelist = (int *)datapointer;datapointer += stylecounts[k] * sizeof(int);
+                                       styleinfo[mod->brushq1.num_lightstyles].surfacelist = datapointer;datapointer += stylecounts[k];
                                        remapstyles[k] = mod->brushq1.num_lightstyles;
                                        mod->brushq1.num_lightstyles++;
                                }
@@ -5087,7 +5110,7 @@ static void Mod_Q2BSP_Load(model_t *mod, void *buffer, void *bufferend)
                                        }
                                }
                        }
-                       mod->brushq1.data_lightstyleinfo = (model_brush_lightstyleinfo_t *)datapointer;datapointer += mod->brushq1.num_lightstyles * sizeof(model_brush_lightstyleinfo_t);
+                       mod->brushq1.data_lightstyleinfo = lsidatapointer;lsidatapointer += mod->brushq1.num_lightstyles;
                        memcpy(mod->brushq1.data_lightstyleinfo, styleinfo, mod->brushq1.num_lightstyles * sizeof(model_brush_lightstyleinfo_t));
                }
                else
@@ -5143,14 +5166,14 @@ static void Mod_Q3BSP_LoadEntities(lump_t *l)
                        if (com_token[0] == '}')
                                break; // end of worldspawn
                        if (com_token[0] == '_')
-                               strlcpy(key, com_token + 1, sizeof(key));
+                               dp_strlcpy(key, com_token + 1, sizeof(key));
                        else
-                               strlcpy(key, com_token, sizeof(key));
+                               dp_strlcpy(key, com_token, sizeof(key));
                        while (key[strlen(key)-1] == ' ') // remove trailing spaces
                                key[strlen(key)-1] = 0;
                        if (!COM_ParseToken_Simple(&data, false, false, true))
                                break; // error
-                       strlcpy(value, com_token, sizeof(value));
+                       dp_strlcpy(value, com_token, sizeof(value));
                        if (!strcasecmp("gridsize", key)) // this one is case insensitive to 100% match q3map2
                        {
 #if _MSC_VER >= 1400
@@ -5368,7 +5391,7 @@ static void Mod_Q3BSP_LoadEffects(lump_t *l)
 
        for (i = 0;i < count;i++, in++, out++)
        {
-               strlcpy (out->shadername, in->shadername, sizeof (out->shadername));
+               dp_strlcpy (out->shadername, in->shadername, sizeof (out->shadername));
                n = LittleLong(in->brushindex);
                if (n >= loadmodel->brush.num_brushes)
                {
@@ -5893,7 +5916,7 @@ 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 (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))
+                       if (numvertices != (patchsize[0] * patchsize[1]) || patchsize[0] < 3 || patchsize[1] < 3 || !(patchsize[0] & 1) || !(patchsize[1] & 1) || patchsize[0] * patchsize[1] >= (cls.state == ca_dedicated ? mod_q3bsp_curves_subdivisions_maxvertices.integer : min(r_subdivisions_maxvertices.integer, mod_q3bsp_curves_subdivisions_maxvertices.integer)))
                        {
                                Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
                                continue;
@@ -5912,11 +5935,11 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
 
                        // lower quality collision patches! Same procedure as before, but different cvars
                        // convert patch to Q3FACETYPE_MESH
-                       cxtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value);
-                       cytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value);
+                       cxtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, mod_q3bsp_curves_subdivisions_tolerance.value);
+                       cytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, mod_q3bsp_curves_subdivisions_tolerance.value);
                        // bound to user settings
-                       cxtess = bound(r_subdivisions_collision_mintess.integer, cxtess, r_subdivisions_collision_maxtess.integer);
-                       cytess = bound(r_subdivisions_collision_mintess.integer, cytess, r_subdivisions_collision_maxtess.integer);
+                       cxtess = bound(mod_q3bsp_curves_subdivisions_mintess.integer, cxtess, mod_q3bsp_curves_subdivisions_maxtess.integer);
+                       cytess = bound(mod_q3bsp_curves_subdivisions_mintess.integer, cytess, mod_q3bsp_curves_subdivisions_maxtess.integer);
                        // bound to sanity settings
                        cxtess = bound(0, cxtess, 1024);
                        cytess = bound(0, cytess, 1024);
@@ -6563,7 +6586,7 @@ static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
                        lightgridmatrix[1][3] = -(loadmodel->brushq3.num_lightgrid_imins[1] - 0.5f) / texturesize[1];
                        lightgridmatrix[2][3] = -(loadmodel->brushq3.num_lightgrid_imins[2] - 1.5f) / texturesize[2];
                        lightgridmatrix[3][3] = 1;
-                       Matrix4x4_FromArrayDoubleD3D(&loadmodel->brushq3.lightgridworldtotexturematrix, lightgridmatrix[0]);
+                       Matrix4x4_FromArrayDoubleD3D(&loadmodel->brushq3.lightgridworldtotexturematrix, lightgridmatrix);
                        loadmodel->brushq3.lightgridtexture = R_LoadTexture3D(loadmodel->texturepool, "lightgrid", texturesize[0], texturesize[1], texturesize[2], texturergba, TEXTYPE_RGBA, TEXF_CLAMP, 0, NULL);
                        Mem_Free(texturergba);
                }
@@ -7520,7 +7543,7 @@ static void Mod_Q3BSP_Load(model_t *mod, void *buffer, void *bufferend)
        loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
 
        // the MakePortals code works fine on the q3bsp data as well
-       if (mod_bsp_portalize.integer)
+       if (mod_bsp_portalize.integer && cls.state != ca_dedicated)
                Mod_BSP_MakePortals();
 
        // FIXME: shader alpha should replace r_wateralpha support in q3bsp
@@ -7545,7 +7568,7 @@ static void Mod_Q3BSP_Load(model_t *mod, void *buffer, void *bufferend)
                        // copy the base model to this one
                        *mod = *loadmodel;
                        // rename the clone back to its proper name
-                       strlcpy(mod->name, name, sizeof(mod->name));
+                       dp_strlcpy(mod->name, name, sizeof(mod->name));
                        mod->brush.parentmodel = loadmodel;
                        // textures and memory belong to the main model
                        mod->texturepool = NULL;
@@ -7764,6 +7787,325 @@ static void Mod_VBSP_LoadPlanes(sizebuf_t *sb)
        }
 }
 
+static void Mod_VBSP_LoadTexinfo(sizebuf_t *sb)
+{
+       mtexinfo_t *out;
+       int i, j, k, count, miptex;
+       int structsize = 72;
+
+       if (sb->cursize % structsize)
+               Host_Error("Mod_VBSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
+       count = sb->cursize / structsize;
+       out = (mtexinfo_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+
+       loadmodel->brushq1.texinfo = out;
+       loadmodel->brushq1.numtexinfo = count;
+
+       for (i = 0;i < count;i++, out++)
+       {
+               for (k = 0;k < 2;k++)
+                       for (j = 0;j < 4;j++)
+                               out->vecs[k][j] = MSG_ReadLittleFloat(sb);
+
+               for (k = 0;k < 2;k++)
+                       for (j = 0;j < 4;j++)
+                               MSG_ReadLittleFloat(sb); // TODO lightmapVecs
+
+               out->q1flags = MSG_ReadLittleLong(sb);
+               miptex = MSG_ReadLittleLong(sb);
+
+               if (out->q1flags & TEX_SPECIAL)
+               {
+                       // if texture chosen is NULL or the shader needs a lightmap,
+                       // force to notexture water shader
+                       out->textureindex = loadmodel->num_textures - 1;
+               }
+               else
+               {
+                       // if texture chosen is NULL, force to notexture
+                       out->textureindex = loadmodel->num_textures - 2;
+               }
+               // see if the specified miptex is valid and try to use it instead
+               if (loadmodel->data_textures)
+               {
+                       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->textureindex = miptex;
+               }
+       }
+}
+
+static void Mod_VBSP_LoadFaces(sizebuf_t *sb)
+{
+       msurface_t *surface;
+       int i, j, count, surfacenum, planenum, smax, tmax, ssize, tsize, firstedge, numedges, totalverts, totaltris, lightmapnumber, lightmapsize, totallightmapsamples, lightmapoffset, texinfoindex;
+       float texmins[2], texmaxs[2], val;
+       rtexture_t *lightmaptexture, *deluxemaptexture;
+       char vabuf[1024];
+       int structsize =  56;
+
+       if (sb->cursize % structsize)
+               Host_Error("Mod_VBSP_LoadFaces: funny lump size in %s",loadmodel->name);
+       count = sb->cursize / structsize;
+       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;
+
+       loadmodel->brushq1.firstrender = true;
+       loadmodel->brushq1.lightmapupdateflags = (unsigned char *)Mem_Alloc(loadmodel->mempool, count*sizeof(unsigned char));
+
+       totalverts = 0;
+       totaltris = 0;
+       for (surfacenum = 0;surfacenum < count;surfacenum++)
+       {
+               numedges = BuffLittleShort(sb->data + structsize * surfacenum + 8);
+               totalverts += numedges;
+               totaltris += numedges - 2;
+       }
+
+       Mod_AllocSurfMesh(loadmodel->mempool, totalverts, totaltris, true, false);
+
+       lightmaptexture = NULL;
+       deluxemaptexture = r_texture_blanknormalmap;
+       lightmapnumber = 0;
+       lightmapsize = bound(256, gl_max_lightmapsize.integer, (int)vid.maxtexturesize_2d);
+       totallightmapsamples = 0;
+
+       totalverts = 0;
+       totaltris = 0;
+       for (surfacenum = 0, surface = loadmodel->data_surfaces;surfacenum < count;surfacenum++, surface++)
+       {
+               surface->lightmapinfo = loadmodel->data_surfaces_lightmapinfo + surfacenum;
+               planenum = (unsigned short)MSG_ReadLittleShort(sb);
+               /*side = */MSG_ReadLittleShort(sb); // TODO support onNode?
+               firstedge = MSG_ReadLittleLong(sb);
+               numedges = (unsigned short)MSG_ReadLittleShort(sb);
+               texinfoindex = (unsigned short)MSG_ReadLittleShort(sb);
+               MSG_ReadLittleLong(sb); // skipping over dispinfo and surfaceFogVolumeID, both short
+               for (i = 0;i < MAXLIGHTMAPS;i++)
+                       surface->lightmapinfo->styles[i] = MSG_ReadByte(sb);
+               lightmapoffset = MSG_ReadLittleLong(sb);
+
+               // FIXME: validate edges, texinfo, etc?
+               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_VBSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)", firstedge, numedges, loadmodel->brushq1.numsurfedges);
+               if ((unsigned int) texinfoindex >= (unsigned int) loadmodel->brushq1.numtexinfo)
+                       Host_Error("Mod_VBSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)", texinfoindex, loadmodel->brushq1.numtexinfo);
+               if ((unsigned int) planenum >= (unsigned int) loadmodel->brush.num_planes)
+                       Host_Error("Mod_VBSP_LoadFaces: invalid plane index %i (model has %i planes)", planenum, loadmodel->brush.num_planes);
+
+               surface->lightmapinfo->texinfo = loadmodel->brushq1.texinfo + texinfoindex;
+               surface->texture = loadmodel->data_textures + surface->lightmapinfo->texinfo->textureindex;
+
+               // Q2BSP doesn't use lightmaps on sky or warped surfaces (water), but still has a lightofs of 0
+               if (lightmapoffset == 0 && (surface->texture->q2flags & (Q2SURF_SKY | Q2SURF_WARP)))
+                       lightmapoffset = -1;
+
+               //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;
+                       // note: the q1bsp format does not allow a 0 surfedge (it would have no negative counterpart)
+                       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;
+               }
+
+               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;
+               }
+
+               // compile additional data about the surface geometry
+               Mod_BuildNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, loadmodel->surfmesh.data_vertex3f, (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle), loadmodel->surfmesh.data_normal3f, r_smoothnormals_areaweighting.integer != 0);
+               Mod_BuildTextureVectorsFromNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_texcoordtexture2f, loadmodel->surfmesh.data_normal3f, (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle), loadmodel->surfmesh.data_svector3f, loadmodel->surfmesh.data_tvector3f, r_smoothnormals_areaweighting.integer != 0);
+               BoxFromPoints(surface->mins, surface->maxs, surface->num_vertices, (loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex));
+
+               // 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];
+               }
+
+               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
+               surface->lightmaptexture = NULL;
+               surface->deluxemaptexture = r_texture_blanknormalmap;
+               if (lightmapoffset == -1)
+               {
+                       surface->lightmapinfo->samples = NULL;
+#if 1
+                       // give non-lightmapped water a 1x white lightmap
+                       if (!loadmodel->brush.isq2bsp && surface->texture->name[0] == '*' && (surface->lightmapinfo->texinfo->q1flags & 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);
+                       }
+#endif
+               }
+               else
+                       surface->lightmapinfo->samples = loadmodel->brushq1.lightdata + lightmapoffset;
+
+               // check if we should apply a lightmap to this
+               if (!(surface->lightmapinfo->texinfo->q1flags & TEX_SPECIAL) || surface->lightmapinfo->samples)
+               {
+                       if (ssize > 256 || tsize > 256)
+                               Host_Error("Bad surface extents");
+
+                       if (lightmapsize < ssize)
+                               lightmapsize = ssize;
+                       if (lightmapsize < tsize)
+                               lightmapsize = tsize;
+
+                       totallightmapsamples += ssize*tsize;
+
+                       // force lightmap upload on first time seeing the surface
+                       //
+                       // additionally this is used by the later code to see if a
+                       // lightmap is needed on this surface (rather than duplicating the
+                       // logic above)
+                       loadmodel->brushq1.lightmapupdateflags[surfacenum] = true;
+                       loadmodel->lit = true;
+               }
+       }
+
+       // small maps (such as ammo boxes especially) don't need big lightmap
+       // textures, so this code tries to guess a good size based on
+       // totallightmapsamples (size of the lightmaps lump basically), as well as
+       // trying to max out the size if there is a lot of lightmap data to store
+       // additionally, never choose a lightmapsize that is smaller than the
+       // largest surface encountered (as it would fail)
+       i = lightmapsize;
+       for (lightmapsize = 64; (lightmapsize < i) && (lightmapsize < bound(128, gl_max_lightmapsize.integer, (int)vid.maxtexturesize_2d)) && (totallightmapsamples > lightmapsize*lightmapsize); lightmapsize*=2)
+               ;
+
+       // now that we've decided the lightmap texture size, we can do the rest
+       if (cls.state != ca_dedicated)
+       {
+               int stainmapsize = 0;
+               mod_alloclightmap_state_t allocState;
+
+               Mod_AllocLightmap_Init(&allocState, loadmodel->mempool, lightmapsize, lightmapsize);
+               for (surfacenum = 0, surface = loadmodel->data_surfaces;surfacenum < count;surfacenum++, surface++)
+               {
+                       int iu, iv, lightmapx = 0, lightmapy = 0;
+                       float u, v, ubase, vbase, uscale, vscale;
+
+                       if (!loadmodel->brushq1.lightmapupdateflags[surfacenum])
+                               continue;
+
+                       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;
+                       stainmapsize += ssize * tsize * 3;
+
+                       if (!lightmaptexture || !Mod_AllocLightmap_Block(&allocState, ssize, tsize, &lightmapx, &lightmapy))
+                       {
+                               // allocate a texture pool if we need it
+                               if (loadmodel->texturepool == NULL)
+                                       loadmodel->texturepool = R_AllocTexturePool();
+                               // could not find room, make a new lightmap
+                               loadmodel->brushq3.num_mergedlightmaps = lightmapnumber + 1;
+                               loadmodel->brushq3.data_lightmaps = (rtexture_t **)Mem_Realloc(loadmodel->mempool, loadmodel->brushq3.data_lightmaps, loadmodel->brushq3.num_mergedlightmaps * sizeof(loadmodel->brushq3.data_lightmaps[0]));
+                               loadmodel->brushq3.data_deluxemaps = (rtexture_t **)Mem_Realloc(loadmodel->mempool, loadmodel->brushq3.data_deluxemaps, loadmodel->brushq3.num_mergedlightmaps * sizeof(loadmodel->brushq3.data_deluxemaps[0]));
+                               loadmodel->brushq3.data_lightmaps[lightmapnumber] = lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, va(vabuf, sizeof(vabuf), "lightmap%i", lightmapnumber), lightmapsize, lightmapsize, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_ALLOWUPDATES, -1, NULL);
+                               if (loadmodel->brushq1.nmaplightdata)
+                                       loadmodel->brushq3.data_deluxemaps[lightmapnumber] = deluxemaptexture = R_LoadTexture2D(loadmodel->texturepool, va(vabuf, sizeof(vabuf), "deluxemap%i", lightmapnumber), lightmapsize, lightmapsize, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_ALLOWUPDATES, -1, NULL);
+                               lightmapnumber++;
+                               Mod_AllocLightmap_Reset(&allocState);
+                               Mod_AllocLightmap_Block(&allocState, ssize, tsize, &lightmapx, &lightmapy);
+                       }
+                       surface->lightmaptexture = lightmaptexture;
+                       surface->deluxemaptexture = deluxemaptexture;
+                       surface->lightmapinfo->lightmaporigin[0] = lightmapx;
+                       surface->lightmapinfo->lightmaporigin[1] = lightmapy;
+
+                       uscale = 1.0f / (float)lightmapsize;
+                       vscale = 1.0f / (float)lightmapsize;
+                       ubase = lightmapx * uscale;
+                       vbase = lightmapy * vscale;
+
+                       for (i = 0;i < surface->num_vertices;i++)
+                       {
+                               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;
+                               // LadyHavoc: calc lightmap data offset for vertex lighting to use
+                               iu = (int) u;
+                               iv = (int) v;
+                               (loadmodel->surfmesh.data_lightmapoffsets + surface->num_firstvertex)[i] = (bound(0, iv, tmax) * ssize + bound(0, iu, smax)) * 3;
+                       }
+               }
+
+               if (cl_stainmaps.integer)
+               {
+                       // allocate stainmaps for permanent marks on walls and clear white
+                       unsigned char *stainsamples = NULL;
+                       stainsamples = (unsigned char *)Mem_Alloc(loadmodel->mempool, stainmapsize);
+                       memset(stainsamples, 255, stainmapsize);
+                       // assign pointers
+                       for (surfacenum = 0, surface = loadmodel->data_surfaces;surfacenum < count;surfacenum++, surface++)
+                       {
+                               if (!loadmodel->brushq1.lightmapupdateflags[surfacenum])
+                                       continue;
+                               ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
+                               tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
+                               surface->lightmapinfo->stainsamples = stainsamples;
+                               stainsamples += ssize * tsize * 3;
+                       }
+               }
+       }
+
+       // generate ushort elements array if possible
+       if (loadmodel->surfmesh.data_element3s)
+               for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
+                       loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
+}
+
 // Valve BSP loader
 // Cloudwalk: Wasn't sober when I wrote this. I screamed and ran away at the face loader
 void Mod_VBSP_Load(model_t *mod, void *buffer, void *bufferend)
@@ -7802,6 +8144,7 @@ void Mod_VBSP_Load(model_t *mod, void *buffer, void *bufferend)
                                Host_Error("Mod_VBSP_Load: %s has invalid lump %i (offset %i, size %i, file size %i)\n", mod->name, i, offset, size, (int)sb.cursize);
                        MSG_InitReadBuffer(&lumpsb[i], sb.data + offset, size);
                }
+
                MSG_ReadLittleLong(&sb); // TODO support revision field
 
                mod->soundfromcenter = true;
@@ -7845,10 +8188,10 @@ void Mod_VBSP_Load(model_t *mod, void *buffer, void *bufferend)
                Mod_VBSP_LoadTextures(&lumpsb[HL2LUMP_TEXDATA/*?*/]);
                //Mod_VBSP_LoadLighting(&lumpsb[HL2LUMP_LIGHTING]);
                Mod_VBSP_LoadPlanes(&lumpsb[HL2LUMP_PLANES]);
-               //Mod_VBSP_LoadTexinfo(&lumpsb[HL2LUMP_TEXINFO]);
+               Mod_VBSP_LoadTexinfo(&lumpsb[HL2LUMP_TEXINFO]);
 
                // AHHHHHHH
-               //Mod_VBSP_LoadFaces(&lumpsb[HL2LUMP_FACES]);
+               Mod_VBSP_LoadFaces(&lumpsb[HL2LUMP_FACES]);
        }
 }
 
@@ -8070,7 +8413,7 @@ void Mod_OBJ_Load(model_t *mod, void *buffer, void *bufferend)
                                        texturenames = (char *)Mem_Realloc(loadmodel->mempool, texturenames, maxtextures * MAX_QPATH);
                                }
                                textureindex = numtextures++;
-                               strlcpy(texturenames + textureindex*MAX_QPATH, loadmodel->name, MAX_QPATH);
+                               dp_strlcpy(texturenames + textureindex*MAX_QPATH, loadmodel->name, MAX_QPATH);
                        }
                        for (j = 1;j < argc;j++)
                        {
@@ -8163,7 +8506,7 @@ void Mod_OBJ_Load(model_t *mod, void *buffer, void *bufferend)
                                        texturenames = (char *)Mem_Realloc(loadmodel->mempool, texturenames, maxtextures * MAX_QPATH);
                                }
                                textureindex = numtextures++;
-                               strlcpy(texturenames + textureindex*MAX_QPATH, argv[1], MAX_QPATH);
+                               dp_strlcpy(texturenames + textureindex*MAX_QPATH, argv[1], MAX_QPATH);
                        }
                }
        }
@@ -8373,7 +8716,7 @@ void Mod_OBJ_Load(model_t *mod, void *buffer, void *bufferend)
                        // copy the base model to this one
                        *mod = *loadmodel;
                        // rename the clone back to its proper name
-                       strlcpy(mod->name, name, sizeof(mod->name));
+                       dp_strlcpy(mod->name, name, sizeof(mod->name));
                        mod->brush.parentmodel = loadmodel;
                        // textures and memory belong to the main model
                        mod->texturepool = NULL;