]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_rsurf.c
Fix a crash on Doombringer duel5.bsp where one of the lights has more than 32768...
[xonotic/darkplaces.git] / gl_rsurf.c
index f392c5f837b2c920731786fbe31f004c1ec12531..3051aa0bf96ceedd9cac4ad1bd1bc3e9c1797ede 100644 (file)
@@ -30,9 +30,15 @@ cvar_t r_lockpvs = {0, "r_lockpvs", "0", "disables pvs switching, allows you to
 cvar_t r_lockvisibility = {0, "r_lockvisibility", "0", "disables visibility updates, allows you to walk around and inspect what is visible from a given viewpoint in the map (anything offscreen at the moment this is enabled will not be drawn)"};
 cvar_t r_useportalculling = {0, "r_useportalculling", "2", "improve framerate with r_novis 1 by using portal culling - still not as good as compiled visibility data in the map, but it helps (a value of 2 forces use of this even with vis data, which improves framerates in maps without too much complexity, but hurts in extremely complex maps, which is why 2 is not the default mode)"};
 cvar_t r_usesurfaceculling = {0, "r_usesurfaceculling", "1", "skip off-screen surfaces (1 = cull surfaces if the map is likely to benefit, 2 = always cull surfaces)"};
+cvar_t r_vis_trace = {0, "r_vis_trace", "0", "test if each portal or leaf is visible using tracelines"};
+cvar_t r_vis_trace_samples = {0, "r_vis_trace_samples", "1", "use this many randomly positioned tracelines each frame to refresh the visible timer"};
+cvar_t r_vis_trace_delay = {0, "r_vis_trace_delay", "1", "keep a portal visible for this many seconds"};
+cvar_t r_vis_trace_eyejitter = {0, "r_vis_trace_eyejitter", "8", "use a random offset of this much on the start of each traceline"};
+cvar_t r_vis_trace_enlarge = {0, "r_vis_trace_enlarge", "0", "make portal bounds bigger for tests by (1+this)*size"};
+cvar_t r_vis_trace_expand = {0, "r_vis_trace_expand", "0", "make portal bounds bigger for tests by this many units"};
+cvar_t r_vis_trace_pad = {0, "r_vis_trace_pad", "8", "accept traces that hit within this many units of the portal"};
+cvar_t r_vis_trace_surfaces = {0, "r_vis_trace_surfaces", "0", "also use tracelines to cull surfaces"};
 cvar_t r_q3bsp_renderskydepth = {0, "r_q3bsp_renderskydepth", "0", "draws sky depth masking in q3 maps (as in q1 maps), this means for example that sky polygons can hide other things"};
-extern cvar_t vid_sRGB;
-extern cvar_t vid_sRGB_fallback;
 
 /*
 ===============
@@ -55,8 +61,8 @@ void R_BuildLightMap (const entity_render_t *ent, msurface_t *surface)
        size = smax*tmax;
        size3 = size*3;
 
-       r_refdef.stats.lightmapupdatepixels += size;
-       r_refdef.stats.lightmapupdates++;
+       r_refdef.stats[r_stat_lightmapupdatepixels] += size;
+       r_refdef.stats[r_stat_lightmapupdates]++;
 
        if (cl.buildlightmapmemorysize < size*sizeof(int[3]))
        {
@@ -395,7 +401,7 @@ void R_DrawPortals(void)
                                                VectorAdd(center, portal->points[i].position, center);
                                        f = ixtable[portal->numpoints];
                                        VectorScale(center, f, center);
-                                       R_MeshQueue_AddTransparent(center, R_DrawPortal_Callback, (entity_render_t *)portal, leafnum, rsurface.rtlight);
+                                       R_MeshQueue_AddTransparent(TRANSPARENTSORT_DISTANCE, center, R_DrawPortal_Callback, (entity_render_t *)portal, leafnum, rsurface.rtlight);
                                }
                        }
                }
@@ -420,9 +426,15 @@ static void R_View_WorldVisibility_CullSurfaces(void)
        surfaceindexend = surfaceindexstart + model->nummodelsurfaces;
        surfaces = model->data_surfaces;
        surfacevisible = r_refdef.viewcache.world_surfacevisible;
-       for (surfaceindex = surfaceindexstart;surfaceindex < surfaceindexend;surfaceindex++)
-               if (surfacevisible[surfaceindex] && R_CullBox(surfaces[surfaceindex].mins, surfaces[surfaceindex].maxs))
-                       surfacevisible[surfaceindex] = 0;
+       for (surfaceindex = surfaceindexstart; surfaceindex < surfaceindexend; surfaceindex++)
+       {
+               if (surfacevisible[surfaceindex])
+               {
+                       if (R_CullBox(surfaces[surfaceindex].mins, surfaces[surfaceindex].maxs)
+                        || (r_vis_trace_surfaces.integer && !R_CanSeeBox(r_vis_trace_samples.integer, r_vis_trace_eyejitter.value, r_vis_trace_enlarge.value, r_vis_trace_expand.value, r_vis_trace_pad.value, r_refdef.view.origin, surfaces[surfaceindex].mins, surfaces[surfaceindex].maxs)))
+                               surfacevisible[surfaceindex] = 0;
+               }
+       }
 }
 
 void R_View_WorldVisibility(qboolean forcenovis)
@@ -448,7 +460,7 @@ void R_View_WorldVisibility(qboolean forcenovis)
                        // if leaf is in current pvs and on the screen, mark its surfaces
                        if (CHECKPVSBIT(r_refdef.viewcache.world_pvsbits, leaf->clusterindex) && !R_CullBox(leaf->mins, leaf->maxs))
                        {
-                               r_refdef.stats.world_leafs++;
+                               r_refdef.stats[r_stat_world_leafs]++;
                                r_refdef.viewcache.world_leafvisible[j] = true;
                                if (leaf->numleafsurfaces)
                                        for (i = 0, mark = leaf->firstleafsurface;i < leaf->numleafsurfaces;i++, mark++)
@@ -475,7 +487,7 @@ void R_View_WorldVisibility(qboolean forcenovis)
 
                // if floating around in the void (no pvs data available, and no
                // portals available), simply use all on-screen leafs.
-               if (!viewleaf || viewleaf->clusterindex < 0 || forcenovis || r_trippy.integer)
+               if (!viewleaf || viewleaf->clusterindex < 0 || forcenovis || !r_refdef.view.usevieworiginculling)
                {
                        // no visibility method: (used when floating around in the void)
                        // simply cull each leaf to the frustum (view pyramid)
@@ -488,7 +500,7 @@ void R_View_WorldVisibility(qboolean forcenovis)
                                // if leaf is in current pvs and on the screen, mark its surfaces
                                if (!R_CullBox(leaf->mins, leaf->maxs))
                                {
-                                       r_refdef.stats.world_leafs++;
+                                       r_refdef.stats[r_stat_world_leafs]++;
                                        r_refdef.viewcache.world_leafvisible[j] = true;
                                        if (leaf->numleafsurfaces)
                                                for (i = 0, mark = leaf->firstleafsurface;i < leaf->numleafsurfaces;i++, mark++)
@@ -511,7 +523,7 @@ void R_View_WorldVisibility(qboolean forcenovis)
                                // if leaf is in current pvs and on the screen, mark its surfaces
                                if (CHECKPVSBIT(r_refdef.viewcache.world_pvsbits, leaf->clusterindex) && !R_CullBox(leaf->mins, leaf->maxs))
                                {
-                                       r_refdef.stats.world_leafs++;
+                                       r_refdef.stats[r_stat_world_leafs]++;
                                        r_refdef.viewcache.world_leafvisible[j] = true;
                                        if (leaf->numleafsurfaces)
                                                for (i = 0, mark = leaf->firstleafsurface;i < leaf->numleafsurfaces;i++, mark++)
@@ -526,6 +538,8 @@ void R_View_WorldVisibility(qboolean forcenovis)
                        int leafstackpos;
                        mportal_t *p;
                        mleaf_t *leafstack[8192];
+                       vec3_t cullmins, cullmaxs;
+                       float cullbias = r_nearclip.value * 2.0f; // the nearclip plane can easily end up culling portals in certain perfectly-aligned views, causing view blackouts
                        // simple-frustum portal method:
                        // follows portals leading outward from viewleaf, does not venture
                        // offscreen or into leafs that are not visible, faster than
@@ -542,7 +556,7 @@ void R_View_WorldVisibility(qboolean forcenovis)
                                        continue;
                                if (leaf->clusterindex < 0)
                                        continue;
-                               r_refdef.stats.world_leafs++;
+                               r_refdef.stats[r_stat_world_leafs]++;
                                r_refdef.viewcache.world_leafvisible[leaf - model->brush.data_leafs] = true;
                                // mark any surfaces bounding this leaf
                                if (leaf->numleafsurfaces)
@@ -550,36 +564,47 @@ void R_View_WorldVisibility(qboolean forcenovis)
                                                r_refdef.viewcache.world_surfacevisible[*mark] = true;
                                // follow portals into other leafs
                                // the checks are:
-                               // if viewer is behind portal (portal faces outward into the scene)
-                               // and the portal polygon's bounding box is on the screen
-                               // and the leaf has not been visited yet
+                               // the leaf has not been visited yet
                                // and the leaf is visible in the pvs
-                               // (the first two checks won't cause as many cache misses as the leaf checks)
+                               // the portal polygon's bounding box is on the screen
                                for (p = leaf->portals;p;p = p->next)
                                {
-                                       r_refdef.stats.world_portals++;
-                                       if (DotProduct(r_refdef.view.origin, p->plane.normal) < (p->plane.dist + 1)
-                                        && !r_refdef.viewcache.world_leafvisible[p->past - model->brush.data_leafs]
-                                        && CHECKPVSBIT(r_refdef.viewcache.world_pvsbits, p->past->clusterindex)
-                                        && !R_CullBox(p->mins, p->maxs)
-                                        && leafstackpos < (int)(sizeof(leafstack) / sizeof(leafstack[0])))
-                                               leafstack[leafstackpos++] = p->past;
+                                       r_refdef.stats[r_stat_world_portals]++;
+                                       if (r_refdef.viewcache.world_leafvisible[p->past - model->brush.data_leafs])
+                                               continue;
+                                       if (!CHECKPVSBIT(r_refdef.viewcache.world_pvsbits, p->past->clusterindex))
+                                               continue;
+                                       cullmins[0] = p->mins[0] - cullbias;
+                                       cullmins[1] = p->mins[1] - cullbias;
+                                       cullmins[2] = p->mins[2] - cullbias;
+                                       cullmaxs[0] = p->maxs[0] + cullbias;
+                                       cullmaxs[1] = p->maxs[1] + cullbias;
+                                       cullmaxs[2] = p->maxs[2] + cullbias;
+                                       if (R_CullBox(cullmins, cullmaxs))
+                                               continue;
+                                       if (r_vis_trace.integer)
+                                       {
+                                               if (p->tracetime != realtime && R_CanSeeBox(r_vis_trace_samples.value, r_vis_trace_eyejitter.value, r_vis_trace_enlarge.value, r_vis_trace_expand.value, r_vis_trace_pad.value, r_refdef.view.origin, cullmins, cullmaxs))
+                                                       p->tracetime = realtime;
+                                               if (realtime - p->tracetime > r_vis_trace_delay.value)
+                                                       continue;
+                                       }
+                                       if (leafstackpos >= (int)(sizeof(leafstack) / sizeof(leafstack[0])))
+                                               break;
+                                       leafstack[leafstackpos++] = p->past;
                                }
                        }
                }
        }
 
-        R_View_WorldVisibility_CullSurfaces();
+       R_View_WorldVisibility_CullSurfaces();
 }
 
 void R_Q1BSP_DrawSky(entity_render_t *ent)
 {
        if (ent->model == NULL)
                return;
-       if (ent == r_refdef.scene.worldentity)
-               R_DrawWorldSurfaces(true, true, false, false, false);
-       else
-               R_DrawModelSurfaces(ent, true, true, false, false, false);
+       R_DrawModelSurfaces(ent, true, true, false, false, false);
 }
 
 void R_Q1BSP_DrawAddWaterPlanes(entity_render_t *ent)
@@ -590,10 +615,7 @@ void R_Q1BSP_DrawAddWaterPlanes(entity_render_t *ent)
        if (model == NULL)
                return;
 
-       if (ent == r_refdef.scene.worldentity)
-               RSurf_ActiveWorldEntity();
-       else
-               RSurf_ActiveModelEntity(ent, true, false, false);
+       RSurf_ActiveModelEntity(ent, true, false, false);
 
        surfaces = model->data_surfaces;
        flagsmask = MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA;
@@ -622,7 +644,7 @@ void R_Q1BSP_DrawAddWaterPlanes(entity_render_t *ent)
                                R_Water_AddWaterPlane(surfaces + j, n);
                }
        }
-       rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
+       rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveModelEntity
 }
 
 void R_Q1BSP_Draw(entity_render_t *ent)
@@ -630,16 +652,13 @@ void R_Q1BSP_Draw(entity_render_t *ent)
        dp_model_t *model = ent->model;
        if (model == NULL)
                return;
-       if (ent == r_refdef.scene.worldentity)
-               R_DrawWorldSurfaces(false, true, false, false, false);
-       else
-               R_DrawModelSurfaces(ent, false, true, false, false, false);
+       R_DrawModelSurfaces(ent, false, true, false, false, false);
 }
 
 void R_Q1BSP_DrawDepth(entity_render_t *ent)
 {
        dp_model_t *model = ent->model;
-       if (model == NULL)
+       if (model == NULL || model->surfmesh.isanimated)
                return;
        GL_ColorMask(0,0,0,0);
        GL_Color(1,1,1,1);
@@ -647,11 +666,7 @@ void R_Q1BSP_DrawDepth(entity_render_t *ent)
        GL_BlendFunc(GL_ONE, GL_ZERO);
        GL_DepthMask(true);
 //     R_Mesh_ResetTextureState();
-       R_SetupShader_DepthOrShadow(false, false);
-       if (ent == r_refdef.scene.worldentity)
-               R_DrawWorldSurfaces(false, false, true, false, false);
-       else
-               R_DrawModelSurfaces(ent, false, false, true, false, false);
+       R_DrawModelSurfaces(ent, false, false, true, false, false);
        GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
 }
 
@@ -659,10 +674,7 @@ void R_Q1BSP_DrawDebug(entity_render_t *ent)
 {
        if (ent->model == NULL)
                return;
-       if (ent == r_refdef.scene.worldentity)
-               R_DrawWorldSurfaces(false, false, false, true, false);
-       else
-               R_DrawModelSurfaces(ent, false, false, false, true, false);
+       R_DrawModelSurfaces(ent, false, false, false, true, false);
 }
 
 void R_Q1BSP_DrawPrepass(entity_render_t *ent)
@@ -670,10 +682,7 @@ void R_Q1BSP_DrawPrepass(entity_render_t *ent)
        dp_model_t *model = ent->model;
        if (model == NULL)
                return;
-       if (ent == r_refdef.scene.worldentity)
-               R_DrawWorldSurfaces(false, true, false, false, true);
-       else
-               R_DrawModelSurfaces(ent, false, true, false, false, true);
+       R_DrawModelSurfaces(ent, false, true, false, false, true);
 }
 
 typedef struct r_q1bsp_getlightinfo_s
@@ -698,6 +707,8 @@ typedef struct r_q1bsp_getlightinfo_s
        const unsigned char *pvs;
        qboolean svbsp_active;
        qboolean svbsp_insertoccluder;
+       qboolean noocclusion; // avoids PVS culling
+       qboolean frontsidecasting; // casts shadows from surfaces facing the light (otherwise ones facing away)
        int numfrustumplanes;
        const mplane_t *frustumplanes;
 }
@@ -728,7 +739,8 @@ static void R_Q1BSP_RecursiveGetLightInfo_BSP(r_q1bsp_getlightinfo_t *info, qboo
        const vec_t *v[3];
        float v2[3][3];
        qboolean insidebox;
-       qboolean frontsidecasting = r_shadow_frontsidecasting.integer != 0;
+       qboolean noocclusion = info->noocclusion;
+       qboolean frontsidecasting = info->frontsidecasting;
        qboolean svbspactive = info->svbsp_active;
        qboolean svbspinsertoccluder = info->svbsp_insertoccluder;
        const int *leafsurfaceindices;
@@ -769,13 +781,13 @@ static void R_Q1BSP_RecursiveGetLightInfo_BSP(r_q1bsp_getlightinfo_t *info, qboo
                                        // recurse front side first because the svbsp building prefers it
                                        if (info->relativelightorigin[plane->type] >= plane->dist)
                                        {
-                                               if (nodestackpos < GETLIGHTINFO_MAXNODESTACK)
+                                               if (nodestackpos < GETLIGHTINFO_MAXNODESTACK-1)
                                                        nodestack[nodestackpos++] = node->children[0];
                                                nodestack[nodestackpos++] = node->children[1];
                                        }
                                        else
                                        {
-                                               if (nodestackpos < GETLIGHTINFO_MAXNODESTACK)
+                                               if (nodestackpos < GETLIGHTINFO_MAXNODESTACK-1)
                                                        nodestack[nodestackpos++] = node->children[1];
                                                nodestack[nodestackpos++] = node->children[0];
                                        }
@@ -799,13 +811,13 @@ static void R_Q1BSP_RecursiveGetLightInfo_BSP(r_q1bsp_getlightinfo_t *info, qboo
                                        // recurse front side first because the svbsp building prefers it
                                        if (PlaneDist(info->relativelightorigin, plane) >= 0)
                                        {
-                                               if (nodestackpos < GETLIGHTINFO_MAXNODESTACK)
+                                               if (nodestackpos < GETLIGHTINFO_MAXNODESTACK-1)
                                                        nodestack[nodestackpos++] = node->children[0];
                                                nodestack[nodestackpos++] = node->children[1];
                                        }
                                        else
                                        {
-                                               if (nodestackpos < GETLIGHTINFO_MAXNODESTACK)
+                                               if (nodestackpos < GETLIGHTINFO_MAXNODESTACK-1)
                                                        nodestack[nodestackpos++] = node->children[1];
                                                nodestack[nodestackpos++] = node->children[0];
                                        }
@@ -818,7 +830,7 @@ static void R_Q1BSP_RecursiveGetLightInfo_BSP(r_q1bsp_getlightinfo_t *info, qboo
                        // leaf
                        leaf = (mleaf_t *)node;
 #if 1
-                       if (r_shadow_frontsidecasting.integer && info->pvs != NULL && !CHECKPVSBIT(info->pvs, leaf->clusterindex))
+                       if (!info->noocclusion && info->pvs != NULL && !CHECKPVSBIT(info->pvs, leaf->clusterindex))
                                continue;
 #endif
 #if 1
@@ -896,7 +908,7 @@ static void R_Q1BSP_RecursiveGetLightInfo_BSP(r_q1bsp_getlightinfo_t *info, qboo
                                                VectorCopy(v[0], v2[0]);
                                                VectorCopy(v[1], v2[1]);
                                                VectorCopy(v[2], v2[2]);
-                                               if (insidebox || TriangleOverlapsBox(v2[0], v2[1], v2[2], info->lightmins, info->lightmaxs))
+                                               if (insidebox || TriangleBBoxOverlapsBox(v2[0], v2[1], v2[2], info->lightmins, info->lightmaxs))
                                                        SVBSP_AddPolygon(&r_svbsp, 3, v2[0], true, NULL, NULL, 0);
                                        }
                                }
@@ -924,7 +936,7 @@ static void R_Q1BSP_RecursiveGetLightInfo_BSP(r_q1bsp_getlightinfo_t *info, qboo
                                                VectorCopy(v[0], v2[0]);
                                                VectorCopy(v[1], v2[1]);
                                                VectorCopy(v[2], v2[2]);
-                                               if (!insidebox && !TriangleOverlapsBox(v2[0], v2[1], v2[2], info->lightmins, info->lightmaxs))
+                                               if (!insidebox && !TriangleBBoxOverlapsBox(v2[0], v2[1], v2[2], info->lightmins, info->lightmaxs))
                                                        continue;
                                                if (svbspactive && !(SVBSP_AddPolygon(&r_svbsp, 3, v2[0], false, NULL, NULL, 0) & 2))
                                                        continue;
@@ -935,7 +947,7 @@ static void R_Q1BSP_RecursiveGetLightInfo_BSP(r_q1bsp_getlightinfo_t *info, qboo
                                                addedtris = true;
                                                if (castshadow)
                                                {
-                                                       if (currentmaterialflags & MATERIALFLAG_NOCULLFACE)
+                                                       if (noocclusion || (currentmaterialflags & MATERIALFLAG_NOCULLFACE))
                                                        {
                                                                // if the material is double sided we
                                                                // can't cull by direction
@@ -977,6 +989,8 @@ static void R_Q1BSP_RecursiveGetLightInfo_BIH(r_q1bsp_getlightinfo_t *info, cons
        int nodeleafindex;
        int currentmaterialflags;
        qboolean castshadow;
+       qboolean noocclusion = info->noocclusion;
+       qboolean frontsidecasting = info->frontsidecasting;
        msurface_t *surface;
        const int *e;
        const vec_t *v[3];
@@ -1036,13 +1050,13 @@ static void R_Q1BSP_RecursiveGetLightInfo_BIH(r_q1bsp_getlightinfo_t *info, cons
                                SETPVSBIT(info->outlighttrispvs, t);
                                if (castshadow)
                                {
-                                       if (currentmaterialflags & MATERIALFLAG_NOCULLFACE)
+                                       if (noocclusion || (currentmaterialflags & MATERIALFLAG_NOCULLFACE))
                                        {
                                                // if the material is double sided we
                                                // can't cull by direction
                                                SETPVSBIT(info->outshadowtrispvs, t);
                                        }
-                                       else if (r_shadow_frontsidecasting.integer)
+                                       else if (frontsidecasting)
                                        {
                                                // front side casting occludes backfaces,
                                                // so they are completely useless as both
@@ -1078,7 +1092,7 @@ static void R_Q1BSP_RecursiveGetLightInfo_BIH(r_q1bsp_getlightinfo_t *info, cons
 #endif
                        if (info->lightmins[axis] <= node->backmax)
                        {
-                               if (info->lightmaxs[axis] >= node->frontmin && nodestackpos < GETLIGHTINFO_MAXNODESTACK)
+                               if (info->lightmaxs[axis] >= node->frontmin && nodestackpos < GETLIGHTINFO_MAXNODESTACK-1)
                                        nodestack[nodestackpos++] = node->front;
                                nodestack[nodestackpos++] = node->back;
                                continue;
@@ -1199,9 +1213,11 @@ static int R_Q1BSP_GetLightInfo_comparefunc(const void *ap, const void *bp)
 
 extern cvar_t r_shadow_sortsurfaces;
 
-void R_Q1BSP_GetLightInfo(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, vec3_t outmins, vec3_t outmaxs, int *outleaflist, unsigned char *outleafpvs, int *outnumleafspointer, int *outsurfacelist, unsigned char *outsurfacepvs, int *outnumsurfacespointer, unsigned char *outshadowtrispvs, unsigned char *outlighttrispvs, unsigned char *visitingleafpvs, int numfrustumplanes, const mplane_t *frustumplanes)
+void R_Q1BSP_GetLightInfo(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, vec3_t outmins, vec3_t outmaxs, int *outleaflist, unsigned char *outleafpvs, int *outnumleafspointer, int *outsurfacelist, unsigned char *outsurfacepvs, int *outnumsurfacespointer, unsigned char *outshadowtrispvs, unsigned char *outlighttrispvs, unsigned char *visitingleafpvs, int numfrustumplanes, const mplane_t *frustumplanes, qboolean noocclusion)
 {
        r_q1bsp_getlightinfo_t info;
+       info.frontsidecasting = r_shadow_frontsidecasting.integer != 0;
+       info.noocclusion = noocclusion || !info.frontsidecasting;
        VectorCopy(relativelightorigin, info.relativelightorigin);
        info.lightradius = lightradius;
        info.lightmins[0] = info.relativelightorigin[0] - info.lightradius;
@@ -1240,18 +1256,18 @@ void R_Q1BSP_GetLightInfo(entity_render_t *ent, vec3_t relativelightorigin, floa
        else
                memset(outshadowtrispvs, 0, (info.model->surfmesh.num_triangles + 7) >> 3);
        memset(outlighttrispvs, 0, (info.model->surfmesh.num_triangles + 7) >> 3);
-       if (info.model->brush.GetPVS && r_shadow_frontsidecasting.integer)
+       if (info.model->brush.GetPVS && !info.noocclusion)
                info.pvs = info.model->brush.GetPVS(info.model, info.relativelightorigin);
        else
                info.pvs = NULL;
-       RSurf_ActiveWorldEntity();
+       RSurf_ActiveModelEntity(r_refdef.scene.worldentity, false, false, false);
 
-       if (r_shadow_frontsidecasting.integer && r_shadow_compilingrtlight && r_shadow_realtime_world_compileportalculling.integer && info.model->brush.data_portals)
+       if (!info.noocclusion && r_shadow_compilingrtlight && r_shadow_realtime_world_compileportalculling.integer && info.model->brush.data_portals)
        {
                // use portal recursion for exact light volume culling, and exact surface checking
                Portal_Visibility(info.model, info.relativelightorigin, info.outleaflist, info.outleafpvs, &info.outnumleafs, info.outsurfacelist, info.outsurfacepvs, &info.outnumsurfaces, NULL, 0, true, info.lightmins, info.lightmaxs, info.outmins, info.outmaxs, info.outshadowtrispvs, info.outlighttrispvs, info.visitingleafpvs);
        }
-       else if (r_shadow_frontsidecasting.integer && r_shadow_realtime_dlight_portalculling.integer && info.model->brush.data_portals)
+       else if (!info.noocclusion && r_shadow_realtime_dlight_portalculling.integer && info.model->brush.data_portals)
        {
                // use portal recursion for exact light volume culling, but not the expensive exact surface checking
                Portal_Visibility(info.model, info.relativelightorigin, info.outleaflist, info.outleafpvs, &info.outnumleafs, info.outsurfacelist, info.outsurfacepvs, &info.outnumsurfaces, NULL, 0, r_shadow_realtime_dlight_portalculling.integer >= 2, info.lightmins, info.lightmaxs, info.outmins, info.outmaxs, info.outshadowtrispvs, info.outlighttrispvs, info.visitingleafpvs);
@@ -1262,10 +1278,10 @@ void R_Q1BSP_GetLightInfo(entity_render_t *ent, vec3_t relativelightorigin, floa
                // optionally using svbsp for exact culling of compiled lights
                // (or if the user enables dlight svbsp culling, which is mostly for
                //  debugging not actual use)
-               R_Q1BSP_CallRecursiveGetLightInfo(&info, (r_shadow_compilingrtlight ? r_shadow_realtime_world_compilesvbsp.integer : r_shadow_realtime_dlight_svbspculling.integer) != 0);
+               R_Q1BSP_CallRecursiveGetLightInfo(&info, !info.noocclusion && (r_shadow_compilingrtlight ? r_shadow_realtime_world_compilesvbsp.integer : r_shadow_realtime_dlight_svbspculling.integer) != 0);
        }
 
-       rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
+       rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveModelEntity
 
        // limit combined leaf box to light boundaries
        outmins[0] = max(info.outmins[0] - 1, info.lightmins[0]);
@@ -1367,7 +1383,12 @@ void R_Q1BSP_CompileShadowMap(entity_render_t *ent, vec3_t relativelightorigin,
        int i;
        if (!model->brush.shadowmesh)
                return;
-       r_shadow_compilingrtlight->static_meshchain_shadow_shadowmap = Mod_ShadowMesh_Begin(r_main_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
+       // FIXME: the sidetotals code incorrectly assumes that static_meshchain is
+       // a single mesh - to prevent that from crashing (sideoffsets, sidetotals
+       // exceeding the number of triangles in a single mesh) we have to make sure
+       // that we make only a single mesh - so over-estimate the size of the mesh
+       // to match the model.
+       r_shadow_compilingrtlight->static_meshchain_shadow_shadowmap = Mod_ShadowMesh_Begin(r_main_mempool, model->surfmesh.num_vertices, model->surfmesh.num_triangles, NULL, NULL, NULL, false, false, true);
        R_Shadow_PrepareShadowSides(model->brush.shadowmesh->numtriangles);
        for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
        {
@@ -1406,8 +1427,8 @@ void R_Q1BSP_DrawShadowMap(int side, entity_render_t *ent, const vec3_t relative
                        continue;
                if (!BoxesOverlap(lightmins, lightmaxs, surface->mins, surface->maxs))
                        continue;
-               r_refdef.stats.lights_dynamicshadowtriangles += surface->num_triangles;
-               r_refdef.stats.lights_shadowtriangles += surface->num_triangles;
+               r_refdef.stats[r_stat_lights_dynamicshadowtriangles] += surface->num_triangles;
+               r_refdef.stats[r_stat_lights_shadowtriangles] += surface->num_triangles;
                batchsurfacelist[0] = surface;
                batchnumsurfaces = 1;
                while(++modelsurfacelistindex < modelnumsurfaces && batchnumsurfaces < RSURF_MAX_BATCHSURFACES)
@@ -1419,17 +1440,14 @@ void R_Q1BSP_DrawShadowMap(int side, entity_render_t *ent, const vec3_t relative
                                break;
                        if (!BoxesOverlap(lightmins, lightmaxs, surface->mins, surface->maxs))
                                continue;
-                       r_refdef.stats.lights_dynamicshadowtriangles += surface->num_triangles;
-                       r_refdef.stats.lights_shadowtriangles += surface->num_triangles;
+                       r_refdef.stats[r_stat_lights_dynamicshadowtriangles] += surface->num_triangles;
+                       r_refdef.stats[r_stat_lights_shadowtriangles] += surface->num_triangles;
                        batchsurfacelist[batchnumsurfaces++] = surface;
                }
                --modelsurfacelistindex;
                GL_CullFace(rsurface.texture->currentmaterialflags & MATERIALFLAG_NOCULLFACE ? GL_NONE : r_refdef.view.cullface_back);
-               RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX, batchnumsurfaces, batchsurfacelist);
-               if (rsurface.batchvertex3fbuffer)
-                       R_Mesh_PrepareVertices_Vertex3f(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchvertex3fbuffer);
-               else
-                       R_Mesh_PrepareVertices_Vertex3f(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchvertex3f_vertexbuffer);
+               RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ALLOWMULTIDRAW, batchnumsurfaces, batchsurfacelist);
+               R_Mesh_PrepareVertices_Vertex3f(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchvertex3f_vertexbuffer, rsurface.batchvertex3f_bufferoffset);
                RSurf_DrawBatch();
        }
        R_FrameData_ReturnToMark();
@@ -1446,7 +1464,7 @@ static void R_Q1BSP_DrawLight_TransparentCallback(const entity_render_t *ent, co
        // note: in practice this never actually receives batches
        R_Shadow_RenderMode_Begin();
        R_Shadow_RenderMode_ActiveLight(rtlight);
-       R_Shadow_RenderMode_Lighting(false, true, false);
+       R_Shadow_RenderMode_Lighting(false, true, rtlight->shadowmapatlassidesize != 0, (ent->flags & RENDER_NOSELFSHADOW) != 0);
        R_Shadow_SetupEntityLight(ent);
        for (i = 0;i < numsurfaces;i = j)
        {
@@ -1508,7 +1526,7 @@ void R_Q1BSP_DrawLight(entity_render_t *ent, int numsurfaces, const int *surface
                                ;
                        // now figure out what to do with this particular range of surfaces
                        // VorteX: added MATERIALFLAG_NORTLIGHT
-                       if ((rsurface.texture->currentmaterialflags & (MATERIALFLAG_WALL | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_NORTLIGHT)) != MATERIALFLAG_WALL)
+                       if ((rsurface.texture->currentmaterialflags & (MATERIALFLAG_WALL | MATERIALFLAG_NORTLIGHT)) != MATERIALFLAG_WALL)
                                continue;
                        if (r_fb.water.renderingscene && (rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA)))
                                continue;
@@ -1518,11 +1536,26 @@ void R_Q1BSP_DrawLight(entity_render_t *ent, int numsurfaces, const int *surface
                                for (l = k;l < kend;l++)
                                {
                                        surface = batchsurfacelist[l];
-                                       tempcenter[0] = (surface->mins[0] + surface->maxs[0]) * 0.5f;
-                                       tempcenter[1] = (surface->mins[1] + surface->maxs[1]) * 0.5f;
-                                       tempcenter[2] = (surface->mins[2] + surface->maxs[2]) * 0.5f;
+                                       if (r_transparent_sortsurfacesbynearest.integer)
+                                       {
+                                               tempcenter[0] = bound(surface->mins[0], rsurface.localvieworigin[0], surface->maxs[0]);
+                                               tempcenter[1] = bound(surface->mins[1], rsurface.localvieworigin[1], surface->maxs[1]);
+                                               tempcenter[2] = bound(surface->mins[2], rsurface.localvieworigin[2], surface->maxs[2]);
+                                       }
+                                       else
+                                       {
+                                               tempcenter[0] = (surface->mins[0] + surface->maxs[0]) * 0.5f;
+                                               tempcenter[1] = (surface->mins[1] + surface->maxs[1]) * 0.5f;
+                                               tempcenter[2] = (surface->mins[2] + surface->maxs[2]) * 0.5f;
+                                       }
                                        Matrix4x4_Transform(&rsurface.matrix, tempcenter, center);
-                                       R_MeshQueue_AddTransparent(rsurface.texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST ? r_refdef.view.origin : center, R_Q1BSP_DrawLight_TransparentCallback, ent, surface - rsurface.modelsurfaces, rsurface.rtlight);
+                                       if (ent->transparent_offset) // transparent offset
+                                       {
+                                               center[0] += r_refdef.view.forward[0]*ent->transparent_offset;
+                                               center[1] += r_refdef.view.forward[1]*ent->transparent_offset;
+                                               center[2] += r_refdef.view.forward[2]*ent->transparent_offset;
+                                       }
+                                       R_MeshQueue_AddTransparent((rsurface.entity->flags & RENDER_WORLDOBJECT) ? TRANSPARENTSORT_SKY : ((rsurface.texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST) ? TRANSPARENTSORT_HUD : rsurface.texture->transparentsort), center, R_Q1BSP_DrawLight_TransparentCallback, ent, surface - rsurface.modelsurfaces, rsurface.rtlight);
                                }
                                continue;
                        }
@@ -1570,10 +1603,9 @@ static void R_ReplaceWorldTexture (void)
        {
                if(/*t->width && !strcasecmp(t->name, r)*/ matchpattern( t->name, r, true ) )
                {
-                       if ((skinframe = R_SkinFrame_LoadExternal(newt, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PICMIP, true)))
+                       if ((skinframe = R_SkinFrame_LoadExternal(newt, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PICMIP, true, false)))
                        {
 //                             t->skinframes[0] = skinframe;
-                               t->currentskinframe = skinframe;
                                t->currentskinframe = skinframe;
                                Con_Printf("%s replaced with %s\n", r, newt);
                        }
@@ -1601,7 +1633,7 @@ static void R_ListWorldTextures (void)
 
        Con_Print("Worldmodel textures :\n");
        for(i=0,t=m->data_textures;i<m->num_textures;i++,t++)
-               if (t->numskinframes)
+               if (t->name[0] && strcasecmp(t->name, "NO TEXTURE FOUND"))
                        Con_Printf("%s\n", t->name);
 }
 
@@ -1627,6 +1659,14 @@ void GL_Surf_Init(void)
        Cvar_RegisterVariable(&r_lockvisibility);
        Cvar_RegisterVariable(&r_useportalculling);
        Cvar_RegisterVariable(&r_usesurfaceculling);
+       Cvar_RegisterVariable(&r_vis_trace);
+       Cvar_RegisterVariable(&r_vis_trace_samples);
+       Cvar_RegisterVariable(&r_vis_trace_delay);
+       Cvar_RegisterVariable(&r_vis_trace_eyejitter);
+       Cvar_RegisterVariable(&r_vis_trace_enlarge);
+       Cvar_RegisterVariable(&r_vis_trace_expand);
+       Cvar_RegisterVariable(&r_vis_trace_pad);
+       Cvar_RegisterVariable(&r_vis_trace_surfaces);
        Cvar_RegisterVariable(&r_q3bsp_renderskydepth);
 
        Cmd_AddCommand ("r_replacemaptexture", R_ReplaceWorldTexture, "override a map texture for testing purposes");