]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - model_brush.c
change q1bsp lightmap to vertex color conversion to scale colors
[xonotic/darkplaces.git] / model_brush.c
index 70acef81d982f2d0546d977246c0ba7f6e8bb228..d41a589110b1754c0baca5fcc55fa516102a3fe4 100644 (file)
@@ -27,6 +27,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 
 //cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128", "how large water polygons should be (smaller values produce more polygons which give better warping effects)"};
+cvar_t mod_bsp_portalize = {0, "mod_bsp_portalize", "0", "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 - all of which are off by default"};
 cvar_t r_novis = {0, "r_novis", "0", "draws whole level, see also sv_cullentities_pvs 0"};
 cvar_t r_nosurftextures = {0, "r_nosurftextures", "0", "pretends there was no texture lump found in the q1bsp/hlbsp loading (useful for debugging this rare case)"};
 cvar_t r_subdivisions_tolerance = {0, "r_subdivisions_tolerance", "4", "maximum error tolerance on curve subdivision for rendering purposes (in other words, the curves will be given as many polygons as necessary to represent curves at this quality)"};
@@ -46,6 +47,8 @@ cvar_t mod_q3bsp_lightmapmergepower = {CVAR_SAVE, "mod_q3bsp_lightmapmergepower"
 cvar_t mod_q3bsp_nolightmaps = {CVAR_SAVE, "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 = {0, "mod_q3bsp_tracelineofsight_brushes", "0", "enables culling of entities behind detail brushes, curves, etc"};
 cvar_t mod_q3shader_default_offsetmapping = {CVAR_SAVE, "mod_q3shader_default_offsetmapping", "1", "use offsetmapping by default on all surfaces"};
+cvar_t mod_q3shader_default_polygonfactor = {0, "mod_q3shader_default_polygonfactor", "0", "biases depth values of 'polygonoffset' shaders to prevent z-fighting artifacts"};
+cvar_t mod_q3shader_default_polygonoffset = {0, "mod_q3shader_default_polygonoffset", "-2", "biases depth values of 'polygonoffset' shaders to prevent z-fighting artifacts"};
 
 cvar_t mod_q1bsp_polygoncollisions = {0, "mod_q1bsp_polygoncollisions", "0", "disables use of precomputed cliphulls and instead collides with polygons (uses Bounding Interval Hierarchy optimizations)"};
 cvar_t mod_collision_bih = {0, "mod_collision_bih", "1", "enables use of generated Bounding Interval Hierarchy tree instead of compiled bsp tree in collision code"};
@@ -60,6 +63,7 @@ static texture_t mod_q1bsp_texture_water;
 void Mod_BrushInit(void)
 {
 //     Cvar_RegisterVariable(&r_subdivide_size);
+       Cvar_RegisterVariable(&mod_bsp_portalize);
        Cvar_RegisterVariable(&r_novis);
        Cvar_RegisterVariable(&r_nosurftextures);
        Cvar_RegisterVariable(&r_subdivisions_tolerance);
@@ -79,6 +83,8 @@ void Mod_BrushInit(void)
        Cvar_RegisterVariable(&mod_q3bsp_nolightmaps);
        Cvar_RegisterVariable(&mod_q3bsp_tracelineofsight_brushes);
        Cvar_RegisterVariable(&mod_q3shader_default_offsetmapping);
+       Cvar_RegisterVariable(&mod_q3shader_default_polygonfactor);
+       Cvar_RegisterVariable(&mod_q3shader_default_polygonoffset);
        Cvar_RegisterVariable(&mod_q1bsp_polygoncollisions);
        Cvar_RegisterVariable(&mod_collision_bih);
        Cvar_RegisterVariable(&mod_recalculatenodeboxes);
@@ -2339,8 +2345,8 @@ static void Mod_Q1BSP_LoadFaces(lump_t *l)
                }
 
                // 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, true);
-               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, true);
+               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
@@ -2413,6 +2419,7 @@ static void Mod_Q1BSP_LoadFaces(lump_t *l)
                        // lightmap is needed on this surface (rather than duplicating the
                        // logic above)
                        loadmodel->brushq1.lightmapupdateflags[surfacenum] = true;
+                       loadmodel->lit = true;
                }
        }
 
@@ -3433,8 +3440,8 @@ static int Mod_Q1BSP_CreateShadowMesh(dp_model_t *mod)
        for (j = 0, surface = mod->data_surfaces;j < mod->num_surfaces;j++, surface++)
                if (surface->num_triangles > 0)
                        Mod_ShadowMesh_AddMesh(mod->mempool, mod->brush.shadowmesh, NULL, NULL, NULL, mod->surfmesh.data_vertex3f, NULL, NULL, NULL, NULL, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
-       mod->brush.shadowmesh = Mod_ShadowMesh_Finish(mod->mempool, mod->brush.shadowmesh, false, true, false);
-       if (mod->brush.shadowmesh)
+       mod->brush.shadowmesh = Mod_ShadowMesh_Finish(mod->mempool, mod->brush.shadowmesh, false, r_enableshadowvolumes.integer != 0, false);
+       if (mod->brush.shadowmesh && mod->brush.shadowmesh->neighbor3i)
                Mod_BuildTriangleNeighbors(mod->brush.shadowmesh->neighbor3i, mod->brush.shadowmesh->element3i, mod->brush.shadowmesh->numtriangles);
 
        return numshadowmeshtriangles;
@@ -3447,7 +3454,6 @@ void Mod_Q1BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
        dmodel_t *bm;
        float dist, modelyawradius, modelradius;
        msurface_t *surface;
-       int numshadowmeshtriangles;
        hullinfo_t hullinfo;
        int totalstylesurfaces, totalstyles, stylecounts[256], remapstyles[256];
        model_brush_lightstyleinfo_t styleinfo[256];
@@ -3569,13 +3575,14 @@ void Mod_Q1BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
        mod->brushq1.num_compressedpvs = 0;
 
        Mod_Q1BSP_MakeHull0();
-       Mod_Q1BSP_MakePortals();
+       if (mod_bsp_portalize.integer)
+               Mod_Q1BSP_MakePortals();
 
        mod->numframes = 2;             // regular and alternate animation
        mod->numskins = 1;
 
        // make a single combined shadow mesh to allow optimized shadow volume creation
-       numshadowmeshtriangles = Mod_Q1BSP_CreateShadowMesh(loadmodel);
+       Mod_Q1BSP_CreateShadowMesh(loadmodel);
 
        if (loadmodel->brush.numsubmodels)
                loadmodel->brush.submodels = (dp_model_t **)Mem_Alloc(loadmodel->mempool, loadmodel->brush.numsubmodels * sizeof(dp_model_t *));
@@ -3751,15 +3758,17 @@ void Mod_Q1BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
                }
                //mod->brushq1.num_visleafs = bm->visleafs;
 
+               // build a Bounding Interval Hierarchy for culling triangles in light rendering
+               Mod_MakeCollisionBIH(mod, true, &mod->render_bih);
+
                if (mod_q1bsp_polygoncollisions.integer)
                {
-                       Mod_MakeCollisionBIH(mod, true, &mod->collision_bih);
+                       mod->collision_bih = mod->render_bih;
                        // point traces and contents checks still use the bsp tree
                        mod->TraceLine = Mod_CollisionBIH_TraceLine;
                        mod->TraceBox = Mod_CollisionBIH_TraceBox;
+                       mod->TraceBrush = Mod_CollisionBIH_TraceBrush;
                }
-               else
-                       Mod_MakeCollisionBIH(mod, true, &mod->render_bih);
 
                // generate VBOs and other shared data before cloning submodels
                if (i == 0)
@@ -4506,6 +4515,8 @@ static void Mod_Q3BSP_LoadVertices(lump_t *l)
                loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
                loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
                loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
+               if(in->color4ub[0] != 255 || in->color4ub[1] != 255 || in->color4ub[2] != 255)
+                       loadmodel->lit = true;
        }
 }
 
@@ -4546,12 +4557,32 @@ static void Mod_Q3BSP_LoadTriangles(lump_t *l)
 static void Mod_Q3BSP_LoadLightmaps(lump_t *l, lump_t *faceslump)
 {
        q3dlightmap_t *input_pointer;
-       int i, j, k, count, power, power2, endlightmap, mergewidth, mergeheight;
+       int i;
+       int j;
+       int k;
+       int count;
+       int powerx;
+       int powery;
+       int powerxy;
+       int powerdxy;
+       int endlightmap;
+       int mergegoal;
+       int lightmapindex;
+       int realcount;
+       int realindex;
+       int mergedwidth;
+       int mergedheight;
+       int mergedcolumns;
+       int mergedrows;
+       int mergedrowsxcolumns;
+       int size;
+       int bytesperpixel;
+       int rgbmap[3];
        unsigned char *c;
-
-       unsigned char *convertedpixels;
+       unsigned char *mergedpixels;
+       unsigned char *mergeddeluxepixels;
+       unsigned char *mergebuf;
        char mapname[MAX_QPATH];
-       int size, bytesperpixel, rgbmap[3];
        qboolean external;
        unsigned char *inpixels[10000]; // max count q3map2 can output (it uses 4 digits)
 
@@ -4623,7 +4654,6 @@ static void Mod_Q3BSP_LoadLightmaps(lump_t *l, lump_t *faceslump)
                }
        }
 
-       convertedpixels = (unsigned char *) Mem_Alloc(tempmempool, size*size*4);
        loadmodel->brushq3.lightmapsize = size;
        loadmodel->brushq3.num_originallightmaps = count;
 
@@ -4693,18 +4723,42 @@ static void Mod_Q3BSP_LoadLightmaps(lump_t *l, lump_t *faceslump)
 
        // figure out what the most reasonable merge power is within limits
 
-       loadmodel->brushq3.num_lightmapmergepower = 0;
-
-       for(i = 0; (128 << i) < size; ++i)
-               ;
-       // i is now 0 for 128, 1 for 256, etc
-
-       for (power = 1;power + i <= mod_q3bsp_lightmapmergepower.integer && (size << power) <= (int)vid.maxtexturesize_2d && (1 << (power * 2)) < 4 * (count >> (loadmodel->brushq3.deluxemapping ? 1 : 0)); power++)
-               loadmodel->brushq3.num_lightmapmergepower = power;
-
-       loadmodel->brushq3.num_lightmapmerge = 1 << loadmodel->brushq3.num_lightmapmergepower;
-
-       loadmodel->brushq3.num_mergedlightmaps = ((count >> (loadmodel->brushq3.deluxemapping ? 1 : 0)) + (1 << (loadmodel->brushq3.num_lightmapmergepower * 2)) - 1) >> (loadmodel->brushq3.num_lightmapmergepower * 2);
+       // find the appropriate NxN dimensions to merge to, to avoid wasted space
+       realcount = count >> (int)loadmodel->brushq3.deluxemapping;
+
+       // figure out how big the merged texture has to be
+       mergegoal = 128<<bound(0, mod_q3bsp_lightmapmergepower.integer, 6);
+       mergegoal = bound(size, mergegoal, (int)vid.maxtexturesize_2d);
+       while (mergegoal > size && mergegoal * mergegoal / 4 >= size * size * realcount)
+               mergegoal /= 2;
+       mergedwidth = mergegoal;
+       mergedheight = mergegoal;
+       // choose non-square size (2x1 aspect) if only half the space is used;
+       // this really only happens when the entire set fits in one texture, if
+       // there are multiple textures, we don't worry about shrinking the last
+       // one to fit, because the driver prefers the same texture size on
+       // consecutive draw calls...
+       if (mergedwidth * mergedheight / 2 >= size*size*realcount)
+               mergedheight /= 2;
+
+       loadmodel->brushq3.num_lightmapmergedwidthpower = 0;
+       loadmodel->brushq3.num_lightmapmergedheightpower = 0;
+       while (mergedwidth > size<<loadmodel->brushq3.num_lightmapmergedwidthpower)
+               loadmodel->brushq3.num_lightmapmergedwidthpower++;
+       while (mergedheight > size<<loadmodel->brushq3.num_lightmapmergedheightpower)
+               loadmodel->brushq3.num_lightmapmergedheightpower++;
+       loadmodel->brushq3.num_lightmapmergedwidthheightdeluxepower = loadmodel->brushq3.num_lightmapmergedwidthpower + loadmodel->brushq3.num_lightmapmergedheightpower + (loadmodel->brushq3.deluxemapping ? 1 : 0);
+
+       powerx = loadmodel->brushq3.num_lightmapmergedwidthpower;
+       powery = loadmodel->brushq3.num_lightmapmergedheightpower;
+       powerxy = powerx+powery;
+       powerdxy = loadmodel->brushq3.deluxemapping + powerxy;
+
+       mergedcolumns = 1 << powerx;
+       mergedrows = 1 << powery;
+       mergedrowsxcolumns = 1 << powerxy;
+
+       loadmodel->brushq3.num_mergedlightmaps = (realcount + (1 << powerxy) - 1) >> powerxy;
        loadmodel->brushq3.data_lightmaps = (rtexture_t **)Mem_Alloc(loadmodel->mempool, loadmodel->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
        if (loadmodel->brushq3.deluxemapping)
                loadmodel->brushq3.data_deluxemaps = (rtexture_t **)Mem_Alloc(loadmodel->mempool, loadmodel->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
@@ -4713,60 +4767,43 @@ static void Mod_Q3BSP_LoadLightmaps(lump_t *l, lump_t *faceslump)
        if (loadmodel->texturepool == NULL && cls.state != ca_dedicated)
                loadmodel->texturepool = R_AllocTexturePool();
 
-       power = loadmodel->brushq3.num_lightmapmergepower;
-       power2 = power * 2;
+       mergedpixels = (unsigned char *) Mem_Alloc(tempmempool, mergedwidth * mergedheight * 4);
+       mergeddeluxepixels = loadmodel->brushq3.deluxemapping ? (unsigned char *) Mem_Alloc(tempmempool, mergedwidth * mergedheight * 4) : NULL;
        for (i = 0;i < count;i++)
        {
                // figure out which merged lightmap texture this fits into
-               int lightmapindex = i >> (loadmodel->brushq3.deluxemapping + power2);
-               for (k = 0;k < size*size;k++)
+               realindex = i >> (int)loadmodel->brushq3.deluxemapping;
+               lightmapindex = i >> powerdxy;
+
+               // choose the destination address
+               mergebuf = (loadmodel->brushq3.deluxemapping && (i & 1)) ? mergeddeluxepixels : mergedpixels;
+               mergebuf += 4 * (realindex & (mergedcolumns-1))*size + 4 * ((realindex >> powerx) & (mergedrows-1))*mergedwidth*size;
+               if ((i & 1) == 0 || !loadmodel->brushq3.deluxemapping)
+                       Con_Printf("copying original lightmap %i (%ix%i) to %i (at %i,%i)\n", i, size, size, lightmapindex, (realindex & (mergedcolumns-1))*size, ((realindex >> powerx) & (mergedrows-1))*size);
+
+               // convert pixels from RGB or BGRA while copying them into the destination rectangle
+               for (j = 0;j < size;j++)
+               for (k = 0;k < size;k++)
                {
-                       convertedpixels[k*4+0] = inpixels[i][k*bytesperpixel+rgbmap[0]];
-                       convertedpixels[k*4+1] = inpixels[i][k*bytesperpixel+rgbmap[1]];
-                       convertedpixels[k*4+2] = inpixels[i][k*bytesperpixel+rgbmap[2]];
-                       convertedpixels[k*4+3] = 255;
+                       mergebuf[(j*mergedwidth+k)*4+0] = inpixels[i][(j*size+k)*bytesperpixel+rgbmap[0]];
+                       mergebuf[(j*mergedwidth+k)*4+1] = inpixels[i][(j*size+k)*bytesperpixel+rgbmap[1]];
+                       mergebuf[(j*mergedwidth+k)*4+2] = inpixels[i][(j*size+k)*bytesperpixel+rgbmap[2]];
+                       mergebuf[(j*mergedwidth+k)*4+3] = 255;
                }
-               if (loadmodel->brushq3.num_lightmapmergepower > 0)
-               {
-                       // if the lightmap has not been allocated yet, create it
-                       if (!loadmodel->brushq3.data_lightmaps[lightmapindex])
-                       {
-                               // create a lightmap only as large as necessary to hold the
-                               // remaining size*size blocks
-                               // if there are multiple merged lightmap textures then they will
-                               // all be full size except the last one which may be smaller
-                               // because it only needs to the remaining blocks, and it will often
-                               // be odd sizes like 2048x512 due to only being 25% full or so.
-                               j = (count >> (loadmodel->brushq3.deluxemapping ? 1 : 0)) - (lightmapindex << power2);
-                               for (mergewidth = 1;mergewidth < j && mergewidth < (1 << power);mergewidth *= 2)
-                                       ;
-                               for (mergeheight = 1;mergewidth*mergeheight < j && mergeheight < (1 << power);mergeheight *= 2)
-                                       ;
-                               if (developer_loading.integer)
-                                       Con_Printf("lightmap merge texture #%i is %ix%i (%i of %i used)\n", lightmapindex, mergewidth*size, mergeheight*size, min(j, mergewidth*mergeheight), mergewidth*mergeheight);
-                               loadmodel->brushq3.data_lightmaps[lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", lightmapindex), mergewidth * size, mergeheight * size, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | (gl_texturecompression_q3bsplightmaps.integer ? TEXF_COMPRESS : TEXF_ALLOWUPDATES), -1, NULL);
-                               if (loadmodel->brushq3.data_deluxemaps)
-                                       loadmodel->brushq3.data_deluxemaps[lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("deluxemap%04i", lightmapindex), mergewidth * size, mergeheight * size, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | (gl_texturecompression_q3bspdeluxemaps.integer ? TEXF_COMPRESS : TEXF_ALLOWUPDATES), -1, NULL);
-                       }
-                       mergewidth = R_TextureWidth(loadmodel->brushq3.data_lightmaps[lightmapindex]) / size;
-                       mergeheight = R_TextureHeight(loadmodel->brushq3.data_lightmaps[lightmapindex]) / size;
-                       j = (i >> (loadmodel->brushq3.deluxemapping ? 1 : 0)) & ((1 << power2) - 1);
-                       if (loadmodel->brushq3.deluxemapping && (i & 1))
-                               R_UpdateTexture(loadmodel->brushq3.data_deluxemaps[lightmapindex], convertedpixels, (j % mergewidth) * size, (j / mergewidth) * size, size, size);
-                       else
-                               R_UpdateTexture(loadmodel->brushq3.data_lightmaps [lightmapindex], convertedpixels, (j % mergewidth) * size, (j / mergewidth) * size, size, size);
-               }
-               else
+
+               // upload texture if this was the last tile being written to the texture
+               if (((realindex + 1) & (mergedrowsxcolumns - 1)) == 0 || (realindex + 1) == realcount)
                {
-                       // figure out which merged lightmap texture this fits into
                        if (loadmodel->brushq3.deluxemapping && (i & 1))
-                               loadmodel->brushq3.data_deluxemaps[lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("deluxemap%04i", lightmapindex), size, size, convertedpixels, TEXTYPE_BGRA, TEXF_FORCELINEAR | (gl_texturecompression_q3bspdeluxemaps.integer ? TEXF_COMPRESS : 0), -1, NULL);
+                               loadmodel->brushq3.data_deluxemaps[lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("deluxemap%04i", lightmapindex), mergedwidth, mergedheight, mergeddeluxepixels, TEXTYPE_BGRA, TEXF_FORCELINEAR | (gl_texturecompression_q3bspdeluxemaps.integer ? TEXF_COMPRESS : 0), -1, NULL);
                        else
-                               loadmodel->brushq3.data_lightmaps [lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", lightmapindex), size, size, convertedpixels, TEXTYPE_BGRA, TEXF_FORCELINEAR | (gl_texturecompression_q3bsplightmaps.integer ? TEXF_COMPRESS : 0), -1, NULL);
+                               loadmodel->brushq3.data_lightmaps [lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", lightmapindex), mergedwidth, mergedheight, mergedpixels, TEXTYPE_BGRA, TEXF_FORCELINEAR | (gl_texturecompression_q3bsplightmaps.integer ? TEXF_COMPRESS : 0), -1, NULL);
                }
        }
 
-       Mem_Free(convertedpixels);
+       if (mergeddeluxepixels)
+               Mem_Free(mergeddeluxepixels);
+       Mem_Free(mergedpixels);
        if(external)
        {
                for(i = 0; i < count; ++i)
@@ -4928,9 +4965,10 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                        }
                        else
                        {
-                               out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n >> (loadmodel->brushq3.num_lightmapmergepower * 2 + loadmodel->brushq3.deluxemapping)];
+                               out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n >> loadmodel->brushq3.num_lightmapmergedwidthheightdeluxepower];
                                if (loadmodel->brushq3.deluxemapping)
-                                       out->deluxemaptexture = loadmodel->brushq3.data_deluxemaps[n >> (loadmodel->brushq3.num_lightmapmergepower * 2 + loadmodel->brushq3.deluxemapping)];
+                                       out->deluxemaptexture = loadmodel->brushq3.data_deluxemaps[n >> loadmodel->brushq3.num_lightmapmergedwidthheightdeluxepower];
+                               loadmodel->lit = true;
                        }
                }
 
@@ -5285,13 +5323,20 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                if(out->num_vertices && out->num_triangles)
                        continue;
                if(out->num_vertices == 0)
-                       Con_Printf("Mod_Q3BSP_LoadFaces: surface %d has no vertices, ignoring\n", i);
-               if(out->num_triangles == 0)
-                       Con_Printf("Mod_Q3BSP_LoadFaces: surface %d has no triangles, ignoring\n", i);
+               {
+                       Con_Printf("Mod_Q3BSP_LoadFaces: surface %d (texture %s) has no vertices, ignoring\n", i, out->texture ? out->texture->name : "(none)");
+                       if(out->num_triangles == 0)
+                               Con_Printf("Mod_Q3BSP_LoadFaces: surface %d (texture %s) has no triangles, ignoring\n", i, out->texture ? out->texture->name : "(none)");
+               }
+               else if(out->num_triangles == 0)
+                       Con_Printf("Mod_Q3BSP_LoadFaces: surface %d (texture %s, near %f %f %f) has no triangles, ignoring\n", i, out->texture ? out->texture->name : "(none)",
+                                       (loadmodel->surfmesh.data_vertex3f + 3 * out->num_firstvertex)[0 * 3 + 0],
+                                       (loadmodel->surfmesh.data_vertex3f + 3 * out->num_firstvertex)[1 * 3 + 0],
+                                       (loadmodel->surfmesh.data_vertex3f + 3 * out->num_firstvertex)[2 * 3 + 0]);
        }
 
        // for per pixel lighting
-       Mod_BuildTextureVectorsFromNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_texcoordtexture2f, loadmodel->surfmesh.data_normal3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_svector3f, loadmodel->surfmesh.data_tvector3f, true);
+       Mod_BuildTextureVectorsFromNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_texcoordtexture2f, loadmodel->surfmesh.data_normal3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_svector3f, loadmodel->surfmesh.data_tvector3f, r_smoothnormals_areaweighting.integer != 0);
 
        // generate ushort elements array if possible
        if (loadmodel->surfmesh.data_element3s)
@@ -5583,7 +5628,10 @@ static void Mod_Q3BSP_LightPoint(dp_model_t *model, const vec3_t p, vec3_t ambie
        q3dlightgrid_t *a, *s;
 
        // scale lighting by lightstyle[0] so that darkmode in dpmod works properly
-       stylescale = r_refdef.scene.rtlightstylevalue[0];
+       if (vid.renderpath == RENDERPATH_GL20)
+               stylescale = 1; // added while render
+       else
+               stylescale = r_refdef.scene.rtlightstylevalue[0];
 
        if (!model->brushq3.num_lightgrid)
        {
@@ -6162,10 +6210,7 @@ void Mod_CollisionBIH_TraceBox(dp_model_t *model, const frameblend_t *frameblend
                if (VectorCompare(start, end))
                        Mod_CollisionBIH_TracePoint(model, frameblend, skeleton, trace, shiftstart, hitsupercontentsmask);
                else
-               {
                        Mod_CollisionBIH_TraceLine(model, frameblend, skeleton, trace, shiftstart, shiftend, hitsupercontentsmask);
-                       VectorSubtract(trace->endpos, boxmins, trace->endpos);
-               }
                return;
        }
 
@@ -6189,6 +6234,33 @@ void Mod_CollisionBIH_TraceBox(dp_model_t *model, const frameblend_t *frameblend
        Mod_CollisionBIH_TraceBrush_RecursiveBIHNode(trace, model, model->collision_bih.rootnode, &thisbrush_start.brush, &thisbrush_end.brush, segmentmins, segmentmaxs);
 }
 
+void Mod_CollisionBIH_TraceBrush(dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, trace_t *trace, colbrushf_t *start, colbrushf_t *end, int hitsupercontentsmask)
+{
+       float segmentmins[3], segmentmaxs[3];
+
+       if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(start->mins, start->maxs) && VectorCompare(end->mins, end->maxs))
+       {
+               if (VectorCompare(start->mins, end->mins))
+                       Mod_CollisionBIH_TracePoint(model, frameblend, skeleton, trace, start->mins, hitsupercontentsmask);
+               else
+                       Mod_CollisionBIH_TraceLine(model, frameblend, skeleton, trace, start->mins, end->mins, hitsupercontentsmask);
+               return;
+       }
+
+       // box trace, performed as brush trace
+       memset(trace, 0, sizeof(*trace));
+       trace->fraction = 1;
+       trace->realfraction = 1;
+       trace->hitsupercontentsmask = hitsupercontentsmask;
+       segmentmins[0] = min(start->mins[0], end->mins[0]);
+       segmentmins[1] = min(start->mins[1], end->mins[1]);
+       segmentmins[2] = min(start->mins[2], end->mins[2]);
+       segmentmaxs[0] = max(start->maxs[0], end->maxs[0]);
+       segmentmaxs[1] = max(start->maxs[1], end->maxs[1]);
+       segmentmaxs[2] = max(start->maxs[2], end->maxs[2]);
+       Mod_CollisionBIH_TraceBrush_RecursiveBIHNode(trace, model, model->collision_bih.rootnode, start, end, segmentmins, segmentmaxs);
+}
+
 int Mod_CollisionBIH_PointSuperContents(struct model_s *model, int frame, const vec3_t point)
 {
        trace_t trace;
@@ -6528,10 +6600,7 @@ static void Mod_Q3BSP_TraceBox(dp_model_t *model, const frameblend_t *frameblend
                if (VectorCompare(start, end))
                        Mod_Q3BSP_TracePoint(model, frameblend, skeleton, trace, shiftstart, hitsupercontentsmask);
                else
-               {
                        Mod_Q3BSP_TraceLine(model, frameblend, skeleton, trace, shiftstart, shiftend, hitsupercontentsmask);
-                       VectorSubtract(trace->endpos, boxmins, trace->endpos);
-               }
                return;
        }
 
@@ -6568,6 +6637,49 @@ static void Mod_Q3BSP_TraceBox(dp_model_t *model, const frameblend_t *frameblend
                Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model, model->brush.data_nodes, &thisbrush_start.brush, &thisbrush_end.brush, ++markframe, segmentmins, segmentmaxs);
 }
 
+void Mod_Q3BSP_TraceBrush(dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, trace_t *trace, colbrushf_t *start, colbrushf_t *end, int hitsupercontentsmask)
+{
+       float segmentmins[3], segmentmaxs[3];
+       int i;
+       msurface_t *surface;
+       q3mbrush_t *brush;
+
+       if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(start->mins, start->maxs) && VectorCompare(end->mins, end->maxs))
+       {
+               if (VectorCompare(start->mins, end->mins))
+                       Mod_Q3BSP_TracePoint(model, frameblend, skeleton, trace, start->mins, hitsupercontentsmask);
+               else
+                       Mod_Q3BSP_TraceLine(model, frameblend, skeleton, trace, start->mins, end->mins, hitsupercontentsmask);
+               return;
+       }
+
+       // box trace, performed as brush trace
+       memset(trace, 0, sizeof(*trace));
+       trace->fraction = 1;
+       trace->realfraction = 1;
+       trace->hitsupercontentsmask = hitsupercontentsmask;
+       segmentmins[0] = min(start->mins[0], end->mins[0]);
+       segmentmins[1] = min(start->mins[1], end->mins[1]);
+       segmentmins[2] = min(start->mins[2], end->mins[2]);
+       segmentmaxs[0] = max(start->maxs[0], end->maxs[0]);
+       segmentmaxs[1] = max(start->maxs[1], end->maxs[1]);
+       segmentmaxs[2] = max(start->maxs[2], end->maxs[2]);
+       if (mod_collision_bih.integer)
+               Mod_CollisionBIH_TraceBrush_RecursiveBIHNode(trace, model, model->collision_bih.rootnode, start, end, segmentmins, segmentmaxs);
+       else if (model->brush.submodel)
+       {
+               for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
+                       if (brush->colbrushf && BoxesOverlap(segmentmins, segmentmaxs, brush->colbrushf->mins, brush->colbrushf->maxs))
+                               Collision_TraceBrushBrushFloat(trace, start, end, brush->colbrushf, brush->colbrushf);
+               if (mod_q3bsp_curves_collisions.integer)
+                       for (i = 0, surface = model->data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
+                               if (surface->num_collisiontriangles && BoxesOverlap(segmentmins, segmentmaxs, surface->mins, surface->maxs))
+                                       Collision_TraceBrushTriangleMeshFloat(trace, start, end, surface->num_collisiontriangles, surface->deprecatedq3data_collisionelement3i, surface->deprecatedq3data_collisionvertex3f, surface->deprecatedq3num_collisionbboxstride, surface->deprecatedq3data_collisionbbox6f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
+       }
+       else
+               Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model, model->brush.data_nodes, start, end, ++markframe, segmentmins, segmentmaxs);
+}
+
 static int Mod_Q3BSP_PointSuperContents(struct model_s *model, int frame, const vec3_t point)
 {
        int i;
@@ -6816,7 +6928,7 @@ void Mod_Q3BSP_RecursiveFindNumLeafs(mnode_t *node)
 
 void Mod_Q3BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
 {
-       int i, j, numshadowmeshtriangles, lumps;
+       int i, j, lumps;
        q3dheader_t *header;
        float corner[3], yawradius, modelradius;
 
@@ -6836,6 +6948,7 @@ void Mod_Q3BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
        mod->soundfromcenter = true;
        mod->TraceBox = Mod_Q3BSP_TraceBox;
+       mod->TraceBrush = Mod_Q3BSP_TraceBrush;
        mod->TraceLine = Mod_Q3BSP_TraceLine;
        mod->TracePoint = Mod_Q3BSP_TracePoint;
        mod->PointSuperContents = Mod_Q3BSP_PointSuperContents;
@@ -6930,13 +7043,14 @@ void Mod_Q3BSP_Load(dp_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
-       Mod_Q1BSP_MakePortals();
+       if (mod_bsp_portalize.integer)
+               Mod_Q1BSP_MakePortals();
 
        // FIXME: shader alpha should replace r_wateralpha support in q3bsp
        loadmodel->brush.supportwateralpha = true;
 
        // make a single combined shadow mesh to allow optimized shadow volume creation
-       numshadowmeshtriangles = Mod_Q1BSP_CreateShadowMesh(loadmodel);
+       Mod_Q1BSP_CreateShadowMesh(loadmodel);
 
        loadmodel->brush.num_leafs = 0;
        Mod_Q3BSP_RecursiveFindNumLeafs(loadmodel->brush.data_nodes);
@@ -7133,6 +7247,7 @@ void Mod_OBJ_Load(dp_model_t *mod, void *buffer, void *bufferend)
        loadmodel->type = mod_obj;
        loadmodel->soundfromcenter = true;
        loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
+       loadmodel->TraceBrush = Mod_CollisionBIH_TraceBrush;
        loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
        loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
        loadmodel->PointSuperContents = Mod_CollisionBIH_PointSuperContents_Mesh;
@@ -7181,6 +7296,7 @@ void Mod_OBJ_Load(dp_model_t *mod, void *buffer, void *bufferend)
        // parse the OBJ text now
        for(;;)
        {
+               static char emptyarg[1] = "";
                if (!*text)
                        break;
                linenumber++;
@@ -7189,7 +7305,7 @@ void Mod_OBJ_Load(dp_model_t *mod, void *buffer, void *bufferend)
                        line[linelen] = text[linelen];
                line[linelen] = 0;
                for (argc = 0;argc < 4;argc++)
-                       argv[argc] = "";
+                       argv[argc] = emptyarg;
                argc = 0;
                s = line;
                while (*s == ' ' || *s == '\t')
@@ -7286,9 +7402,12 @@ void Mod_OBJ_Load(dp_model_t *mod, void *buffer, void *bufferend)
                                vcurrent.nextindex = -1;
                                vcurrent.textureindex = textureindex;
                                vcurrent.submodelindex = submodelindex;
-                               VectorCopy(v + 3*index1, vcurrent.v);
-                               Vector2Copy(vt + 2*index2, vcurrent.vt);
-                               VectorCopy(vn + 3*index3, vcurrent.vn);
+                               if (v && index1 >= 0 && index1 < numv)
+                                       VectorCopy(v + 3*index1, vcurrent.v);
+                               if (vt && index2 >= 0 && index2 < numvt)
+                                       Vector2Copy(vt + 2*index2, vcurrent.vt);
+                               if (vn && index3 >= 0 && index3 < numvn)
+                                       VectorCopy(vn + 3*index3, vcurrent.vn);
                                if (numtriangles == 0)
                                {
                                        VectorCopy(vcurrent.v, mins);
@@ -7460,13 +7579,14 @@ void Mod_OBJ_Load(dp_model_t *mod, void *buffer, void *bufferend)
        // allocate storage for final mesh data
        loadmodel->num_textures = numtextures * loadmodel->numskins;
        loadmodel->num_texturesperskin = numtextures;
-       data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(int) + loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t) + numtriangles * sizeof(int[3]) + (numvertices <= 65536 ? numtriangles * sizeof(unsigned short[3]) : 0) + numvertices * sizeof(float[14]) + loadmodel->brush.numsubmodels * sizeof(dp_model_t *));
+       data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(int) + loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t) + numtriangles * sizeof(int[3]) + (numvertices <= 65536 ? numtriangles * sizeof(unsigned short[3]) : 0) + (r_enableshadowvolumes.integer ? numtriangles * sizeof(int[3]) : 0) + numvertices * sizeof(float[14]) + loadmodel->brush.numsubmodels * sizeof(dp_model_t *));
        loadmodel->brush.submodels = (dp_model_t **)data;data += loadmodel->brush.numsubmodels * sizeof(dp_model_t *);
        loadmodel->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
        loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
        loadmodel->surfmesh.num_vertices = numvertices;
        loadmodel->surfmesh.num_triangles = numtriangles;
-       loadmodel->surfmesh.data_neighbor3i = (int *)data;data += numtriangles * sizeof(int[3]);
+       if (r_enableshadowvolumes.integer)
+               loadmodel->surfmesh.data_neighbor3i = (int *)data;data += numtriangles * sizeof(int[3]);
        loadmodel->surfmesh.data_vertex3f = (float *)data;data += numvertices * sizeof(float[3]);
        loadmodel->surfmesh.data_svector3f = (float *)data;data += numvertices * sizeof(float[3]);
        loadmodel->surfmesh.data_tvector3f = (float *)data;data += numvertices * sizeof(float[3]);
@@ -7510,9 +7630,10 @@ void Mod_OBJ_Load(dp_model_t *mod, void *buffer, void *bufferend)
        Mod_ValidateElements(loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles, 0, loadmodel->surfmesh.num_vertices, __FILE__, __LINE__);
        // generate normals if the file did not have them
        if (!VectorLength2(loadmodel->surfmesh.data_normal3f))
-               Mod_BuildNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_normal3f, true);
-       Mod_BuildTextureVectorsFromNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_texcoordtexture2f, loadmodel->surfmesh.data_normal3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_svector3f, loadmodel->surfmesh.data_tvector3f, true);
-       Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
+               Mod_BuildNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_normal3f, r_smoothnormals_areaweighting.integer != 0);
+       Mod_BuildTextureVectorsFromNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_texcoordtexture2f, loadmodel->surfmesh.data_normal3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_svector3f, loadmodel->surfmesh.data_tvector3f, r_smoothnormals_areaweighting.integer != 0);
+       if (loadmodel->surfmesh.data_neighbor3i)
+               Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
 
        // if this is a worldmodel and has no BSP tree, create a fake one for the purpose
        loadmodel->brush.num_visleafs = 1;
@@ -8412,14 +8533,15 @@ void Mod_OBJ_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
        loadmodel->num_surfaces = 1;
        loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
-       data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->numframes * sizeof(animscene_t) + loadmodel->numframes * sizeof(float[6]) + loadmodel->surfmesh.num_triangles * sizeof(int[3]) + loadmodel->surfmesh.num_triangles * sizeof(int[3]));
+       data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->numframes * sizeof(animscene_t) + loadmodel->numframes * sizeof(float[6]) + loadmodel->surfmesh.num_triangles * sizeof(int[3]) + (r_enableshadowvolume.integer ? loadmodel->surfmesh.num_triangles * sizeof(int[3]) : 0));
        loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
        loadmodel->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
        loadmodel->sortedmodelsurfaces[0] = 0;
        loadmodel->animscenes = (animscene_t *)data;data += loadmodel->numframes * sizeof(animscene_t);
        loadmodel->surfmesh.data_morphmd2framesize6f = (float *)data;data += loadmodel->numframes * sizeof(float[6]);
        loadmodel->surfmesh.data_element3i = (int *)data;data += loadmodel->surfmesh.num_triangles * sizeof(int[3]);
-       loadmodel->surfmesh.data_neighbor3i = (int *)data;data += loadmodel->surfmesh.num_triangles * sizeof(int[3]);
+       if (r_enableshadowvolumes.integer)
+               loadmodel->surfmesh.data_neighbor3i = (int *)data;data += loadmodel->surfmesh.num_triangles * sizeof(int[3]);
 
        loadmodel->synctype = ST_RAND;
 
@@ -8561,7 +8683,8 @@ void Mod_OBJ_Load(dp_model_t *mod, void *buffer, void *bufferend)
        Mem_Free(vertremap);
 
        Mod_MakeSortedSurfaces(loadmodel);
-       Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
+       if (loadmodel->surfmesh.data_neighbor3i)
+               Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
        Mod_Alias_CalculateBoundingBox();
        Mod_Alias_MorphMesh_CompileFrames();