]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - model_brush.c
q3bsp loader no longer generates a fake lightgrid when one is missing, this saves...
[xonotic/darkplaces.git] / model_brush.c
index 89bae0de70d1eb19ea9c933a6d54f5f094539983..795742544cc4da77c4e10c6480767d8830505045 100644 (file)
@@ -26,8 +26,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "wad.h"
 
 
-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"};
@@ -46,7 +44,6 @@ cvar_t mod_q3bsp_curves_collisions = {0, "mod_q3bsp_curves_collisions", "1"};
 cvar_t mod_q3bsp_optimizedtraceline = {0, "mod_q3bsp_optimizedtraceline", "1"};
 cvar_t mod_q3bsp_debugtracebrush = {0, "mod_q3bsp_debugtracebrush", "0"};
 
-static void Mod_Q1BSP_Collision_Init (void);
 void Mod_BrushInit(void)
 {
 //     Cvar_RegisterVariable(&r_subdivide_size);
@@ -66,8 +63,6 @@ void Mod_BrushInit(void)
        Cvar_RegisterVariable(&mod_q3bsp_curves_collisions);
        Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline);
        Cvar_RegisterVariable(&mod_q3bsp_debugtracebrush);
-       memset(mod_q1bsp_novis, 0xff, sizeof(mod_q1bsp_novis));
-       Mod_Q1BSP_Collision_Init();
 }
 
 static mleaf_t *Mod_Q1BSP_PointInLeaf(model_t *model, const vec3_t p)
@@ -265,7 +260,7 @@ static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *in
        msurface_t *surface;
        for (surfacenum = 0, mark = leaf->firstleafsurface;surfacenum < leaf->numleafsurfaces;surfacenum++, mark++)
        {
-               surface = info->model->brush.data_surfaces + *mark;
+               surface = info->model->data_surfaces + *mark;
                if (surface->texture->supercontents & SUPERCONTENTS_SOLID)
                {
                        for (k = 0;k < surface->num_triangles;k++)
@@ -680,40 +675,6 @@ static void Mod_Q1BSP_TraceBox(struct model_s *model, int frame, trace_t *trace,
 #endif
 }
 
-static hull_t box_hull;
-static dclipnode_t box_clipnodes[6];
-static mplane_t box_planes[6];
-
-static void Mod_Q1BSP_Collision_Init (void)
-{
-       int             i;
-       int             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;
-       }
-}
-
 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)
 {
 #if 1
@@ -746,6 +707,9 @@ void Collision_ClipTrace_Box(trace_t *trace, const vec3_t cmins, const vec3_t cm
        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));
@@ -761,6 +725,36 @@ void Collision_ClipTrace_Box(trace_t *trace, const vec3_t cmins, const vec3_t cm
 #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;
@@ -779,7 +773,7 @@ void Collision_ClipTrace_Box(trace_t *trace, const vec3_t cmins, const vec3_t cm
 #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;
        float front, back;
@@ -823,7 +817,7 @@ loc0:
        }
 
        // go down front side
-       if (node->children[side]->plane && 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
        {
@@ -833,7 +827,7 @@ loc0:
                        int i, ds, dt;
                        msurface_t *surface;
 
-                       surface = r_refdef.worldmodel->brush.data_surfaces + node->firstsurface;
+                       surface = model->data_surfaces + node->firstsurface;
                        for (i = 0;i < node->numsurfaces;i++, surface++)
                        {
                                if (!(surface->texture->basematerialflags & MATERIALFLAG_WALL) || !surface->lightmapinfo->samples)
@@ -914,7 +908,7 @@ 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, model->brush.data_nodes + model->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);
 }
 
 static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *out, qbyte *outend)
@@ -1031,32 +1025,32 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l)
        qbyte *data, *mtdata;
        char name[256];
 
-       loadmodel->brush.data_textures = NULL;
+       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->brush.num_textures = m->nummiptex + 2;
+               loadmodel->num_textures = m->nummiptex + 2;
        }
        else
        {
                m = NULL;
-               loadmodel->brush.num_textures = 2;
+               loadmodel->num_textures = 2;
        }
 
-       loadmodel->brush.data_textures = Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_textures * sizeof(texture_t));
+       loadmodel->data_textures = Mem_Alloc(loadmodel->mempool, loadmodel->num_textures * sizeof(texture_t));
 
        // fill out all slots with notexture
-       for (i = 0, tx = loadmodel->brush.data_textures;i < loadmodel->brush.num_textures;i++, tx++)
+       for (i = 0, tx = loadmodel->data_textures;i < loadmodel->num_textures;i++, tx++)
        {
                strcpy(tx->name, "NO TEXTURE FOUND");
                tx->width = 16;
                tx->height = 16;
                tx->skin.base = r_texture_notexture;
                tx->basematerialflags = 0;
-               if (i == loadmodel->brush.num_textures - 1)
+               if (i == loadmodel->num_textures - 1)
                {
                        tx->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_LIGHTBOTHSIDES;
                        tx->supercontents = SUPERCONTENTS_WATER;
@@ -1110,7 +1104,7 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l)
                        if (name[j] >= 'A' && name[j] <= 'Z')
                                name[j] += 'a' - 'A';
 
-               tx = loadmodel->brush.data_textures + i;
+               tx = loadmodel->data_textures + i;
                strcpy(tx->name, name);
                tx->width = mtwidth;
                tx->height = mtheight;
@@ -1223,7 +1217,7 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l)
        // sequence the animations
        for (i = 0;i < m->nummiptex;i++)
        {
-               tx = loadmodel->brush.data_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])
@@ -1235,7 +1229,7 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l)
 
                for (j = i;j < m->nummiptex;j++)
                {
-                       tx2 = loadmodel->brush.data_textures + j;
+                       tx2 = loadmodel->data_textures + j;
                        if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
                                continue;
 
@@ -1343,7 +1337,7 @@ static void Mod_Q1BSP_LoadLighting(lump_t *l)
                data = (qbyte*) FS_LoadFile(litfilename, tempmempool, false);
                if (data)
                {
-                       if (fs_filesize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
+                       if (fs_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)
@@ -1365,7 +1359,7 @@ static void Mod_Q1BSP_LoadLighting(lump_t *l)
                                if (fs_filesize == 8)
                                        Con_Print("Empty .lit file, ignoring\n");
                                else
-                                       Con_Print("Corrupt .lit file (old version?), ignoring\n");
+                                       Con_Printf("Corrupt .lit file (file size %i bytes, should be %i bytes), ignoring\n", fs_filesize, 8 + l->filelen * 3);
                                Mem_Free(data);
                        }
                }
@@ -1633,25 +1627,25 @@ static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
                out->flags = LittleLong(in->flags);
 
                out->texture = NULL;
-               if (loadmodel->brush.data_textures)
+               if (loadmodel->data_textures)
                {
-                       if ((unsigned int) miptex >= (unsigned int) loadmodel->brush.num_textures)
-                               Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->brush.num_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->texture = loadmodel->brush.data_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->basematerialflags & MATERIALFLAG_WALL)
-                               out->texture = loadmodel->brush.data_textures + (loadmodel->brush.num_textures - 1);
+                               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->brush.data_textures + (loadmodel->brush.num_textures - 2);
+                               out->texture = loadmodel->data_textures + (loadmodel->num_textures - 2);
                }
        }
 }
@@ -1819,10 +1813,10 @@ static void Mod_Q1BSP_LoadFaces(lump_t *l)
        if (l->filelen % sizeof(*in))
                Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
        count = l->filelen / sizeof(*in);
-       loadmodel->brush.data_surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
-       loadmodel->brush.data_surfaces_lightmapinfo = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_lightmapinfo_t));
+       loadmodel->data_surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
+       loadmodel->data_surfaces_lightmapinfo = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_lightmapinfo_t));
 
-       loadmodel->brush.num_surfaces = count;
+       loadmodel->num_surfaces = count;
 
        totalverts = 0;
        totaltris = 0;
@@ -1841,9 +1835,9 @@ static void Mod_Q1BSP_LoadFaces(lump_t *l)
 
        totalverts = 0;
        totaltris = 0;
-       for (surfacenum = 0, in = (void *)(mod_base + l->fileofs), surface = loadmodel->brush.data_surfaces;surfacenum < count;surfacenum++, in++, surface++)
+       for (surfacenum = 0, in = (void *)(mod_base + l->fileofs), surface = loadmodel->data_surfaces;surfacenum < count;surfacenum++, in++, surface++)
        {
-               surface->lightmapinfo = loadmodel->brush.data_surfaces_lightmapinfo + surfacenum;
+               surface->lightmapinfo = loadmodel->data_surfaces_lightmapinfo + surfacenum;
 
                // FIXME: validate edges, texinfo, etc?
                firstedge = LittleLong(in->firstedge);
@@ -1901,7 +1895,7 @@ static void Mod_Q1BSP_LoadFaces(lump_t *l)
                }
 
                // compile additional data about the surface geometry
-               Mod_BuildTextureVectorsAndNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, surface->groupmesh->data_vertex3f, surface->groupmesh->data_texcoordtexture2f, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle), surface->groupmesh->data_svector3f, surface->groupmesh->data_tvector3f, surface->groupmesh->data_normal3f);
+               Mod_BuildTextureVectorsAndNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, surface->groupmesh->data_vertex3f, surface->groupmesh->data_texcoordtexture2f, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle), surface->groupmesh->data_svector3f, surface->groupmesh->data_tvector3f, surface->groupmesh->data_normal3f, true);
                BoxFromPoints(surface->mins, surface->maxs, surface->num_vertices, (surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex));
 
                // generate surface extents information
@@ -2243,7 +2237,7 @@ static void Mod_Q1BSP_LoadLeaffaces(lump_t *l)
        for (i = 0;i < loadmodel->brush.num_leafsurfaces;i++)
        {
                j = (unsigned) LittleShort(in[i]);
-               if (j >= loadmodel->brush.num_surfaces)
+               if (j >= loadmodel->num_surfaces)
                        Host_Error("Mod_Q1BSP_LoadLeaffaces: bad surface number");
                loadmodel->brush.data_leafsurfaces[i] = j;
        }
@@ -2782,7 +2776,7 @@ static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *mod
        memset(stylecounts, 0, sizeof(stylecounts));
        for (i = 0;i < model->nummodelsurfaces;i++)
        {
-               surface = model->brush.data_surfaces + model->firstmodelsurface + i;
+               surface = model->data_surfaces + model->firstmodelsurface + i;
                for (j = 0;j < MAXLIGHTMAPS;j++)
                        stylecounts[surface->lightmapinfo->styles[j]]++;
        }
@@ -2814,7 +2808,7 @@ static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *mod
        }
        for (i = 0;i < model->nummodelsurfaces;i++)
        {
-               surface = model->brush.data_surfaces + model->firstmodelsurface + i;
+               surface = model->data_surfaces + model->firstmodelsurface + i;
                for (j = 0;j < MAXLIGHTMAPS;j++)
                        if (surface->lightmapinfo->styles[j] != 255)
                                *model->brushq1.light_styleupdatechains[remapstyles[surface->lightmapinfo->styles[j]]]++ = surface;
@@ -3012,13 +3006,13 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer)
 
        // make a single combined shadow mesh to allow optimized shadow volume creation
        numshadowmeshtriangles = 0;
-       for (j = 0, surface = loadmodel->brush.data_surfaces;j < loadmodel->brush.num_surfaces;j++, surface++)
+       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->brush.data_surfaces;j < loadmodel->brush.num_surfaces;j++, surface++)
+       for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
                Mod_ShadowMesh_AddMesh(loadmodel->mempool, loadmodel->brush.shadowmesh, NULL, NULL, NULL, surface->groupmesh->data_vertex3f, NULL, NULL, NULL, NULL, surface->num_triangles, (surface->groupmesh->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);
@@ -3119,7 +3113,7 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer)
                        mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
                        modelyawradius = 0;
                        modelradius = 0;
-                       for (j = 0, surface = &mod->brush.data_surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surface++)
+                       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 (surface->texture->basematerialflags & MATERIALFLAG_SKY)
@@ -3164,7 +3158,7 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer)
        //Mod_Q1BSP_ProcessLightList();
 
        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->brush.num_surfaces, loadmodel->brush.num_nodes, loadmodel->brush.num_leafs, mod->brushq1.submodels[i].visleafs, loadmodel->brush.num_portals);
+               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)
@@ -3657,8 +3651,8 @@ static void Mod_Q3BSP_LoadTextures(lump_t *l)
        count = l->filelen / sizeof(*in);
        out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
 
-       loadmodel->brush.data_textures = out;
-       loadmodel->brush.num_textures = count;
+       loadmodel->data_textures = out;
+       loadmodel->num_textures = count;
 
        for (i = 0;i < count;i++, in++, out++)
        {
@@ -3848,7 +3842,7 @@ static void Mod_Q3BSP_LoadTextures(lump_t *l)
                                                }
                                                // add shader to list (shadername and flags)
                                                // actually here we just poke into the texture settings
-                                               for (j = 0, out = loadmodel->brush.data_textures;j < loadmodel->brush.num_textures;j++, out++)
+                                               for (j = 0, out = loadmodel->data_textures;j < loadmodel->num_textures;j++, out++)
                                                {
                                                        if (!strcasecmp(out->name, shadername))
                                                        {
@@ -3867,7 +3861,12 @@ static void Mod_Q3BSP_LoadTextures(lump_t *l)
                                                                        out->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_WATERALPHA;
                                                                else
                                                                        out->basematerialflags |= MATERIALFLAG_WALL;
-                                                               if (out->surfaceparms & Q3SURFACEPARM_TRANS)
+                                                               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;
@@ -3896,7 +3895,7 @@ parseerror:
        }
 
        c = 0;
-       for (j = 0, out = loadmodel->brush.data_textures;j < loadmodel->brush.num_textures;j++, out++)
+       for (j = 0, out = loadmodel->data_textures;j < loadmodel->num_textures;j++, out++)
        {
                if (out->surfaceparms == -1)
                {
@@ -3920,7 +3919,8 @@ parseerror:
                }
                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, 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, false, true))
-                               Con_Printf("%s: texture loading for shader \"%s\" failed (first layer \"%s\" not found either)\n", loadmodel->name, out->name, out->firstpasstexturename);
+                               if (cls.state != ca_dedicated)
+                                       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;
        }
@@ -3945,10 +3945,10 @@ static void Mod_Q3BSP_LoadPlanes(lump_t *l)
 
        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);
        }
 }
@@ -3975,9 +3975,9 @@ static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
                        Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brush.num_planes);
                out->plane = loadmodel->brush.data_planes + n;
                n = LittleLong(in->textureindex);
-               if (n < 0 || n >= loadmodel->brush.num_textures)
-                       Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brush.num_textures);
-               out->texture = loadmodel->brush.data_textures + n;
+               if (n < 0 || n >= loadmodel->num_textures)
+                       Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->num_textures);
+               out->texture = loadmodel->data_textures + n;
        }
 }
 
@@ -4009,9 +4009,9 @@ static void Mod_Q3BSP_LoadBrushes(lump_t *l)
                out->firstbrushside = loadmodel->brush.data_brushsides + n;
                out->numbrushsides = c;
                n = LittleLong(in->textureindex);
-               if (n < 0 || n >= loadmodel->brush.num_textures)
-                       Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brush.num_textures);
-               out->texture = loadmodel->brush.data_textures + n;
+               if (n < 0 || n >= loadmodel->num_textures)
+                       Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", 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)
@@ -4160,8 +4160,8 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
        count = l->filelen / sizeof(*in);
        out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
 
-       loadmodel->brush.data_surfaces = out;
-       loadmodel->brush.num_surfaces = count;
+       loadmodel->data_surfaces = out;
+       loadmodel->num_surfaces = count;
 
        i = 0;
        for (meshnum = 0;i < count;meshnum++)
@@ -4185,12 +4185,12 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                        }
 
                        n = LittleLong(in->textureindex);
-                       if (n < 0 || n >= loadmodel->brush.num_textures)
+                       if (n < 0 || n >= loadmodel->num_textures)
                        {
-                               Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brush.num_textures);
+                               Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->num_textures);
                                continue;
                        }
-                       out->texture = loadmodel->brush.data_textures + n;
+                       out->texture = loadmodel->data_textures + n;
                        n = LittleLong(in->effectindex);
                        if (n < -1 || n >= loadmodel->brushq3.num_effects)
                        {
@@ -4427,7 +4427,7 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                                Con_Print("\n");
                        }
                        // for per pixel lighting
-                       Mod_BuildTextureVectorsAndNormals(out->num_firstvertex, out->num_vertices, out->num_triangles, out->groupmesh->data_vertex3f, out->groupmesh->data_texcoordtexture2f, (out->groupmesh->data_element3i + 3 * out->num_firsttriangle), out->groupmesh->data_svector3f, out->groupmesh->data_tvector3f, out->groupmesh->data_normal3f);
+                       Mod_BuildTextureVectorsAndNormals(out->num_firstvertex, out->num_vertices, out->num_triangles, out->groupmesh->data_vertex3f, out->groupmesh->data_texcoordtexture2f, (out->groupmesh->data_element3i + 3 * out->num_firsttriangle), out->groupmesh->data_svector3f, out->groupmesh->data_tvector3f, out->groupmesh->data_normal3f, true);
                        // calculate a bounding box
                        VectorClear(out->mins);
                        VectorClear(out->maxs);
@@ -4504,8 +4504,8 @@ static void Mod_Q3BSP_LoadModels(lump_t *l)
                }
                n = LittleLong(in->firstface);
                c = LittleLong(in->numfaces);
-               if (n < 0 || n + c > loadmodel->brush.num_surfaces)
-                       Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brush.num_surfaces);
+               if (n < 0 || n + c > loadmodel->num_surfaces)
+                       Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->num_surfaces);
                out->firstface = n;
                out->numfaces = c;
                n = LittleLong(in->firstbrush);
@@ -4559,8 +4559,8 @@ static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
        for (i = 0;i < count;i++, in++, out++)
        {
                n = LittleLong(*in);
-               if (n < 0 || n >= loadmodel->brush.num_surfaces)
-                       Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brush.num_surfaces);
+               if (n < 0 || n >= loadmodel->num_surfaces)
+                       Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->num_surfaces);
                *out = n;
        }
 }
@@ -4680,40 +4680,22 @@ static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
        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];
+       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)\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
-       if (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));
-       else
-       {
-               // no data, fill with white
-               int i;
-               for (i = 0;i < count;i++)
-               {
-                       out[i].ambientrgb[0] = 128;
-                       out[i].ambientrgb[1] = 128;
-                       out[i].ambientrgb[2] = 128;
-                       out[i].diffusergb[0] = 0;
-                       out[i].diffusergb[1] = 0;
-                       out[i].diffusergb[2] = 0;
-                       out[i].diffusepitch = 0;
-                       out[i].diffuseyaw = 0;
-               }
        }
-
-       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]);
 }
 
 static void Mod_Q3BSP_LoadPVS(lump_t *l)
@@ -4759,7 +4741,6 @@ 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] = 1;
@@ -4904,7 +4885,7 @@ static void Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace_t *trace, model_t *model,
                // line trace the curves
                for (i = 0;i < leaf->numleafsurfaces;i++)
                {
-                       surface = model->brush.data_surfaces + leaf->firstleafsurface[i];
+                       surface = model->data_surfaces + leaf->firstleafsurface[i];
                        if (surface->num_collisiontriangles && surface->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, surface->mins, surface->maxs))
                        {
                                surface->collisionmarkframe = markframe;
@@ -5284,7 +5265,7 @@ static void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, model_t *model
        {
                for (i = 0;i < leaf->numleafsurfaces;i++)
                {
-                       surface = model->brush.data_surfaces + leaf->firstleafsurface[i];
+                       surface = model->data_surfaces + leaf->firstleafsurface[i];
                        if (surface->num_collisiontriangles && surface->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, surface->mins, surface->maxs))
                        {
                                surface->collisionmarkframe = markframe;
@@ -5338,7 +5319,7 @@ static void Mod_Q3BSP_TraceBox(model_t *model, int frame, trace_t *trace, const
                                        if (brush->colbrushf)
                                                Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, brush->colbrushf, brush->colbrushf);
                                if (mod_q3bsp_curves_collisions.integer)
-                                       for (i = 0, surface = model->brush.data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
+                                       for (i = 0, surface = model->data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
                                                if (surface->num_collisiontriangles)
                                                        Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, segmentmins, segmentmaxs);
                        }
@@ -5357,7 +5338,7 @@ static void Mod_Q3BSP_TraceBox(model_t *model, int frame, trace_t *trace, const
                                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->brush.data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
+                               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, segmentmins, segmentmaxs);
                }
@@ -5502,14 +5483,15 @@ void Mod_Q3BSP_Load(model_t *mod, void *buffer)
 
        // make a single combined shadow mesh to allow optimized shadow volume creation
        numshadowmeshtriangles = 0;
-       for (j = 0, surface = loadmodel->brush.data_surfaces;j < loadmodel->brush.num_surfaces;j++, surface++)
+       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->brush.data_surfaces;j < loadmodel->brush.num_surfaces;j++, surface++)
-               Mod_ShadowMesh_AddMesh(loadmodel->mempool, loadmodel->brush.shadowmesh, NULL, NULL, NULL, surface->groupmesh->data_vertex3f, NULL, NULL, NULL, NULL, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
+       for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
+               if (surface->groupmesh)
+                       Mod_ShadowMesh_AddMesh(loadmodel->mempool, loadmodel->brush.shadowmesh, NULL, NULL, NULL, surface->groupmesh->data_vertex3f, NULL, NULL, NULL, NULL, surface->num_triangles, (surface->groupmesh->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);
 
@@ -5579,7 +5561,7 @@ void Mod_Q3BSP_Load(model_t *mod, void *buffer)
                mod->radius2 = modelradius * modelradius;
 
                for (j = 0;j < mod->nummodelsurfaces;j++)
-                       if (mod->brush.data_surfaces[j + mod->firstmodelsurface].texture->surfaceflags & Q3SURFACEFLAG_SKY)
+                       if (mod->data_surfaces[j + mod->firstmodelsurface].texture->surfaceflags & Q3SURFACEFLAG_SKY)
                                break;
                if (j < mod->nummodelsurfaces)
                        mod->DrawSky = R_Q1BSP_DrawSky;