]> de.git.xonotic.org Git - xonotic/darkplaces.git/commitdiff
Added r_cullentities_trace_expand and pad cvars, these configure additional ways...
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Sat, 14 Apr 2018 05:20:44 +0000 (05:20 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Sat, 14 Apr 2018 05:20:44 +0000 (05:20 +0000)
Added r_vis_trace feature which allows you to turn on traceline-based culling of portals, and r_vis_trace_surfaces lets you cull surfaces by traceline (slow and probably pointless, but a curious feature).

Added r_shadow_culllights_trace_expand and pad cvars.

Added sv_cullentities_trace_expand to make SV_CanSeeBox be at feature parity with R_CanSeeBox.

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@12395 d7cf8633-e32d-0410-b094-e92efae38249

gl_rmain.c
gl_rsurf.c
model_brush.h
r_shadow.c
render.h
server.h
sv_main.c

index 4842ee0bf6df64bd18d2d8b982741381213ac774..386d12f35ca602a51b972e8a37801749183430e5 100644 (file)
@@ -114,6 +114,8 @@ cvar_t r_cullentities_trace_entityocclusion = { 0, "r_cullentities_trace_entityo
 cvar_t r_cullentities_trace_samples = {0, "r_cullentities_trace_samples", "2", "number of samples to test for entity culling (in addition to center sample)"};
 cvar_t r_cullentities_trace_tempentitysamples = {0, "r_cullentities_trace_tempentitysamples", "-1", "number of samples to test for entity culling of temp entities (including all CSQC entities), -1 disables trace culling on these entities to prevent flicker (pvs still applies)"};
 cvar_t r_cullentities_trace_enlarge = {0, "r_cullentities_trace_enlarge", "0", "box enlargement for entity culling"};
+cvar_t r_cullentities_trace_expand = {0, "r_cullentities_trace_expand", "0", "box expanded by this many units for entity culling"};
+cvar_t r_cullentities_trace_pad = {0, "r_cullentities_trace_pad", "8", "accept traces that hit within this many units of the box"};
 cvar_t r_cullentities_trace_delay = {0, "r_cullentities_trace_delay", "1", "number of seconds until the entity gets actually culled"};
 cvar_t r_cullentities_trace_eyejitter = {0, "r_cullentities_trace_eyejitter", "16", "randomly offset rays from the eye by this much to reduce the odds of flickering"};
 cvar_t r_sortentities = {0, "r_sortentities", "0", "sort entities before drawing (might be faster)"};
@@ -4266,6 +4268,8 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_cullentities_trace_samples);
        Cvar_RegisterVariable(&r_cullentities_trace_tempentitysamples);
        Cvar_RegisterVariable(&r_cullentities_trace_enlarge);
+       Cvar_RegisterVariable(&r_cullentities_trace_expand);
+       Cvar_RegisterVariable(&r_cullentities_trace_pad);
        Cvar_RegisterVariable(&r_cullentities_trace_delay);
        Cvar_RegisterVariable(&r_cullentities_trace_eyejitter);
        Cvar_RegisterVariable(&r_sortentities);
@@ -5023,11 +5027,12 @@ void R_AnimCache_CacheVisibleEntities(void)
 
 //==================================================================================
 
-qboolean R_CanSeeBox(int numsamples, vec_t eyejitter, vec_t entboxenlarge, vec3_t eye, vec3_t entboxmins, vec3_t entboxmaxs)
+qboolean R_CanSeeBox(int numsamples, vec_t eyejitter, vec_t entboxenlarge, vec_t entboxexpand, vec_t pad, vec3_t eye, vec3_t entboxmins, vec3_t entboxmaxs)
 {
        int i;
        vec3_t eyemins, eyemaxs;
        vec3_t boxmins, boxmaxs;
+       vec3_t padmins, padmaxs;
        vec3_t start;
        vec3_t end;
        dp_model_t *model = r_refdef.scene.worldmodel;
@@ -5062,12 +5067,19 @@ qboolean R_CanSeeBox(int numsamples, vec_t eyejitter, vec_t entboxenlarge, vec3_
        eyemins[2] = eye[2] - eyejitter;
        eyemaxs[2] = eye[2] + eyejitter;
        // expand the box a little
-       boxmins[0] = (entboxenlarge + 1) * entboxmins[0] - entboxenlarge * entboxmaxs[0];
-       boxmaxs[0] = (entboxenlarge + 1) * entboxmaxs[0] - entboxenlarge * entboxmins[0];
-       boxmins[1] = (entboxenlarge + 1) * entboxmins[1] - entboxenlarge * entboxmaxs[1];
-       boxmaxs[1] = (entboxenlarge + 1) * entboxmaxs[1] - entboxenlarge * entboxmins[1];
-       boxmins[2] = (entboxenlarge + 1) * entboxmins[2] - entboxenlarge * entboxmaxs[2];
-       boxmaxs[2] = (entboxenlarge + 1) * entboxmaxs[2] - entboxenlarge * entboxmins[2];
+       boxmins[0] = (entboxenlarge + 1) * entboxmins[0] - entboxenlarge * entboxmaxs[0] - entboxexpand;
+       boxmaxs[0] = (entboxenlarge + 1) * entboxmaxs[0] - entboxenlarge * entboxmins[0] + entboxexpand;
+       boxmins[1] = (entboxenlarge + 1) * entboxmins[1] - entboxenlarge * entboxmaxs[1] - entboxexpand;
+       boxmaxs[1] = (entboxenlarge + 1) * entboxmaxs[1] - entboxenlarge * entboxmins[1] + entboxexpand;
+       boxmins[2] = (entboxenlarge + 1) * entboxmins[2] - entboxenlarge * entboxmaxs[2] - entboxexpand;
+       boxmaxs[2] = (entboxenlarge + 1) * entboxmaxs[2] - entboxenlarge * entboxmins[2] + entboxexpand;
+       // make an even larger box for the acceptable area
+       padmins[0] = boxmins[0] - pad;
+       padmaxs[0] = boxmaxs[0] + pad;
+       padmins[1] = boxmins[1] - pad;
+       padmaxs[1] = boxmaxs[1] + pad;
+       padmins[2] = boxmins[2] - pad;
+       padmaxs[2] = boxmaxs[2] + pad;
 
        // return true if eye overlaps enlarged box
        if (BoxesOverlap(boxmins, boxmaxs, eyemins, eyemaxs))
@@ -5085,11 +5097,11 @@ qboolean R_CanSeeBox(int numsamples, vec_t eyejitter, vec_t entboxenlarge, vec3_
                        //trace_t trace = CL_TraceLine(start, end, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, SUPERCONTENTS_SKY, 0.0f, true, false, NULL, true, true);
                        trace_t trace = CL_Cache_TraceLineSurfaces(start, end, MOVE_NORMAL, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT);
                        // not picky - if the trace ended anywhere in the box we're good
-                       if (BoxesOverlap(trace.endpos, trace.endpos, boxmins, boxmaxs))
+                       if (BoxesOverlap(trace.endpos, trace.endpos, padmins, padmaxs))
                                return true;
                }
        }
-       else if (model->brush.TraceLineOfSight(model, start, end, boxmins, boxmaxs))
+       else if (model->brush.TraceLineOfSight(model, start, end, padmins, padmaxs))
                return true;
 
        // try various random positions
@@ -5101,10 +5113,10 @@ qboolean R_CanSeeBox(int numsamples, vec_t eyejitter, vec_t entboxenlarge, vec3_
                {
                        trace_t trace = CL_Cache_TraceLineSurfaces(start, end, MOVE_NORMAL, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT);
                        // not picky - if the trace ended anywhere in the box we're good
-                       if (BoxesOverlap(trace.endpos, trace.endpos, boxmins, boxmaxs))
+                       if (BoxesOverlap(trace.endpos, trace.endpos, padmins, padmaxs))
                                return true;
                }
-               else if (model->brush.TraceLineOfSight(model, start, end, boxmins, boxmaxs))
+               else if (model->brush.TraceLineOfSight(model, start, end, padmins, padmaxs))
                        return true;
        }
 
@@ -5163,7 +5175,7 @@ static void R_View_UpdateEntityVisible (void)
                        if (!(ent->flags & (RENDER_VIEWMODEL | RENDER_WORLDOBJECT | RENDER_NODEPTHTEST)) && !(ent->model && (ent->model->name[0] == '*')))
                        {
                                samples = ent->last_trace_visibility == 0 ? r_cullentities_trace_tempentitysamples.integer : r_cullentities_trace_samples.integer;
-                               if (R_CanSeeBox(samples, r_cullentities_trace_eyejitter.value, r_cullentities_trace_enlarge.value, r_refdef.view.origin, ent->mins, ent->maxs))
+                               if (R_CanSeeBox(samples, r_cullentities_trace_eyejitter.value, r_cullentities_trace_enlarge.value, r_cullentities_trace_expand.value, r_cullentities_trace_pad.value, r_refdef.view.origin, ent->mins, ent->maxs))
                                        ent->last_trace_visibility = realtime;
                                if (ent->last_trace_visibility < realtime - r_cullentities_trace_delay.value)
                                        r_refdef.viewcache.entityvisible[i] = 0;
index 130e7453a073e6478827a4c059f799244ded216d..d0477c3d12b1f3026fd4c5fe721755a4e29909db 100644 (file)
@@ -30,6 +30,14 @@ 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"};
 
 /*
@@ -418,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)
@@ -568,6 +582,13 @@ void R_View_WorldVisibility(qboolean forcenovis)
                                        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;
@@ -576,7 +597,7 @@ void R_View_WorldVisibility(qboolean forcenovis)
                }
        }
 
-        R_View_WorldVisibility_CullSurfaces();
+       R_View_WorldVisibility_CullSurfaces();
 }
 
 void R_Q1BSP_DrawSky(entity_render_t *ent)
@@ -1633,6 +1654,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");
index 3b1d61624f9228a08946f70eac6e4d45c5f3ddc0..9eae6abb9022ce2b19ef2907672da2067dd2be7f 100644 (file)
@@ -219,6 +219,7 @@ typedef struct mportal_s
        mvertex_t *points;
        vec3_t mins, maxs; // culling
        mplane_t plane;
+       double tracetime; // refreshed to realtime by traceline tests
 }
 mportal_t;
 
index c7ecb5a19ca8db0d93c268bc50c104aab9922d49..b880ab00795cd6a7c271ca664345cc68a890afda 100644 (file)
@@ -340,7 +340,9 @@ cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1", "use 3D voxel texture
 cvar_t r_shadow_culllights_pvs = {CVAR_SAVE, "r_shadow_culllights_pvs", "1", "check if light overlaps any visible bsp leafs when determining if the light is visible"};
 cvar_t r_shadow_culllights_trace = {CVAR_SAVE, "r_shadow_culllights_trace", "1", "use raytraces from the eye to random places within light bounds to determine if the light is visible"};
 cvar_t r_shadow_culllights_trace_eyejitter = {CVAR_SAVE, "r_shadow_culllights_trace_eyejitter", "16", "offset eye location randomly by this much"};
-cvar_t r_shadow_culllights_trace_enlarge = {CVAR_SAVE, "r_shadow_culllights_trace_enlarge", "0.1", "make light bounds bigger by *1.0+enlarge"};
+cvar_t r_shadow_culllights_trace_enlarge = {CVAR_SAVE, "r_shadow_culllights_trace_enlarge", "0", "make light bounds bigger by *(1.0+enlarge)"};
+cvar_t r_shadow_culllights_trace_expand = {CVAR_SAVE, "r_shadow_culllights_trace_expand", "8", "make light bounds bigger by this many units"};
+cvar_t r_shadow_culllights_trace_pad = {CVAR_SAVE, "r_shadow_culllights_trace_expand", "8", "accept traces that hit within this many units of the light bounds"};
 cvar_t r_shadow_culllights_trace_samples = {CVAR_SAVE, "r_shadow_culllights_trace_samples", "16", "use this many traces to random positions (in addition to center trace)"};
 cvar_t r_shadow_culllights_trace_tempsamples = {CVAR_SAVE, "r_shadow_culllights_trace_tempsamples", "16", "use this many traces if the light was created by csqc (no inter-frame caching), -1 disables the check (to avoid flicker entirely)"};
 cvar_t r_shadow_culllights_trace_delay = {CVAR_SAVE, "r_shadow_culllights_trace_delay", "1", "light will be considered visible for this many seconds after any trace connects"};
@@ -818,6 +820,8 @@ void R_Shadow_Init(void)
        Cvar_RegisterVariable(&r_shadow_culllights_trace);
        Cvar_RegisterVariable(&r_shadow_culllights_trace_eyejitter);
        Cvar_RegisterVariable(&r_shadow_culllights_trace_enlarge);
+       Cvar_RegisterVariable(&r_shadow_culllights_trace_expand);
+       Cvar_RegisterVariable(&r_shadow_culllights_trace_pad);
        Cvar_RegisterVariable(&r_shadow_culllights_trace_samples);
        Cvar_RegisterVariable(&r_shadow_culllights_trace_tempsamples);
        Cvar_RegisterVariable(&r_shadow_culllights_trace_delay);
@@ -2874,7 +2878,7 @@ static void R_Shadow_BounceGrid_AssignPhotons(r_shadow_bouncegrid_settings_t *se
                        // is probably fine (and they use the same timer)
                        if (r_shadow_culllights_trace.integer)
                        {
-                               if (rtlight->trace_timer != realtime && R_CanSeeBox(rtlight->trace_timer == 0 ? r_shadow_culllights_trace_tempsamples.integer : r_shadow_culllights_trace_samples.integer, r_shadow_culllights_trace_eyejitter.value, r_shadow_culllights_trace_enlarge.value, r_refdef.view.origin, rtlight->cullmins, rtlight->cullmaxs))
+                               if (rtlight->trace_timer != realtime && R_CanSeeBox(rtlight->trace_timer == 0 ? r_shadow_culllights_trace_tempsamples.integer : r_shadow_culllights_trace_samples.integer, r_shadow_culllights_trace_eyejitter.value, r_shadow_culllights_trace_enlarge.value, r_shadow_culllights_trace_expand.value, r_shadow_culllights_trace_pad.value, r_refdef.view.origin, rtlight->cullmins, rtlight->cullmaxs))
                                        rtlight->trace_timer = realtime;
                                if (realtime - rtlight->trace_timer > r_shadow_culllights_trace_delay.value)
                                        return;
@@ -4707,7 +4711,7 @@ static void R_Shadow_PrepareLight(rtlight_t *rtlight)
        // skip if the light box is not visible to traceline
        if (r_shadow_culllights_trace.integer)
        {
-               if (rtlight->trace_timer != realtime && R_CanSeeBox(rtlight->trace_timer == 0 ? r_shadow_culllights_trace_tempsamples.integer : r_shadow_culllights_trace_samples.integer, r_shadow_culllights_trace_eyejitter.value, r_shadow_culllights_trace_enlarge.value, r_refdef.view.origin, rtlight->cullmins, rtlight->cullmaxs))
+               if (rtlight->trace_timer != realtime && R_CanSeeBox(rtlight->trace_timer == 0 ? r_shadow_culllights_trace_tempsamples.integer : r_shadow_culllights_trace_samples.integer, r_shadow_culllights_trace_eyejitter.value, r_shadow_culllights_trace_enlarge.value, r_shadow_culllights_trace_expand.value, r_shadow_culllights_trace_pad.value, r_refdef.view.origin, rtlight->cullmins, rtlight->cullmaxs))
                        rtlight->trace_timer = realtime;
                if (realtime - rtlight->trace_timer > r_shadow_culllights_trace_delay.value)
                        return;
index 5ae6304a413140df310d948c93b58b7ff7d153dc..df88749a259f6c2fe1b04ea25b6ddb96a9e7e53a 100644 (file)
--- a/render.h
+++ b/render.h
@@ -166,7 +166,7 @@ void R_DrawExplosions(void);
 
 int R_CullBox(const vec3_t mins, const vec3_t maxs);
 int R_CullBoxCustomPlanes(const vec3_t mins, const vec3_t maxs, int numplanes, const mplane_t *planes);
-qboolean R_CanSeeBox(int numsamples, vec_t eyejitter, vec_t entboxenlarge, vec3_t eye, vec3_t entboxmins, vec3_t entboxmaxs);
+qboolean R_CanSeeBox(int numsamples, vec_t eyejitter, vec_t entboxenlarge, vec_t entboxexpand, vec_t pad, vec3_t eye, vec3_t entboxmins, vec3_t entboxmaxs);
 
 #include "r_modules.h"
 
index bfd20ecb973251b262b483b552eba10dd030550b..bd35e7bdbb29ae6bc486ca48ccae23a953fdcc23 100644 (file)
--- a/server.h
+++ b/server.h
@@ -582,7 +582,7 @@ trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_
 trace_t SV_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask);
 int SV_EntitiesInBox(const vec3_t mins, const vec3_t maxs, int maxedicts, prvm_edict_t **resultedicts);
 
-qboolean SV_CanSeeBox(int numsamples, vec_t eyejitter, vec_t enlarge, vec3_t eye, vec3_t entboxmins, vec3_t entboxmaxs);
+qboolean SV_CanSeeBox(int numsamples, vec_t eyejitter, vec_t enlarge, vec_t entboxexpand, vec3_t eye, vec3_t entboxmins, vec3_t entboxmaxs);
 
 int SV_PointSuperContents(const vec3_t point);
 
index d724aa1f3ad3bbd42a268c123061975722bf830e..f315a3a76a9627ea665604a1e3483454a5d7c6e7 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -81,6 +81,7 @@ cvar_t sv_cullentities_trace = {0, "sv_cullentities_trace", "0", "somewhat slow
 cvar_t sv_cullentities_trace_delay = {0, "sv_cullentities_trace_delay", "1", "number of seconds until the entity gets actually culled"};
 cvar_t sv_cullentities_trace_delay_players = {0, "sv_cullentities_trace_delay_players", "0.2", "number of seconds until the entity gets actually culled if it is a player entity"};
 cvar_t sv_cullentities_trace_enlarge = {0, "sv_cullentities_trace_enlarge", "0", "box enlargement for entity culling"};
+cvar_t sv_cullentities_trace_expand = { 0, "sv_cullentities_trace_expand", "0", "box is expanded by this many units for entity culling" };
 cvar_t sv_cullentities_trace_eyejitter = {0, "sv_cullentities_trace_eyejitter", "16", "jitter the eye by this much for each trace"};
 cvar_t sv_cullentities_trace_prediction = {0, "sv_cullentities_trace_prediction", "1", "also trace from the predicted player position"};
 cvar_t sv_cullentities_trace_prediction_time = {0, "sv_cullentities_trace_prediction_time", "0.2", "how many seconds of prediction to use"};
@@ -494,6 +495,7 @@ void SV_Init (void)
        Cvar_RegisterVariable (&sv_cullentities_trace_delay);
        Cvar_RegisterVariable (&sv_cullentities_trace_delay_players);
        Cvar_RegisterVariable (&sv_cullentities_trace_enlarge);
+       Cvar_RegisterVariable (&sv_cullentities_trace_expand);
        Cvar_RegisterVariable (&sv_cullentities_trace_eyejitter);
        Cvar_RegisterVariable (&sv_cullentities_trace_entityocclusion);
        Cvar_RegisterVariable (&sv_cullentities_trace_prediction);
@@ -1487,7 +1489,7 @@ static void SV_PrepareEntitiesForSending(void)
 
 #define MAX_LINEOFSIGHTTRACES 64
 
-qboolean SV_CanSeeBox(int numtraces, vec_t eyejitter, vec_t enlarge, vec3_t eye, vec3_t entboxmins, vec3_t entboxmaxs)
+qboolean SV_CanSeeBox(int numtraces, vec_t eyejitter, vec_t enlarge, vec_t entboxexpand, vec3_t eye, vec3_t entboxmins, vec3_t entboxmaxs)
 {
        prvm_prog_t *prog = SVVM_prog;
        float pitchsign;
@@ -1519,12 +1521,12 @@ qboolean SV_CanSeeBox(int numtraces, vec_t eyejitter, vec_t enlarge, vec3_t eye,
        eyemins[2] = eye[2] - eyejitter;
        eyemaxs[2] = eye[2] + eyejitter;
        // expand the box a little
-       boxmins[0] = (enlarge+1) * entboxmins[0] - enlarge * entboxmaxs[0];
-       boxmaxs[0] = (enlarge+1) * entboxmaxs[0] - enlarge * entboxmins[0];
-       boxmins[1] = (enlarge+1) * entboxmins[1] - enlarge * entboxmaxs[1];
-       boxmaxs[1] = (enlarge+1) * entboxmaxs[1] - enlarge * entboxmins[1];
-       boxmins[2] = (enlarge+1) * entboxmins[2] - enlarge * entboxmaxs[2];
-       boxmaxs[2] = (enlarge+1) * entboxmaxs[2] - enlarge * entboxmins[2];
+       boxmins[0] = (enlarge+1) * entboxmins[0] - enlarge * entboxmaxs[0] - entboxexpand;
+       boxmaxs[0] = (enlarge+1) * entboxmaxs[0] - enlarge * entboxmins[0] + entboxexpand;
+       boxmins[1] = (enlarge+1) * entboxmins[1] - enlarge * entboxmaxs[1] - entboxexpand;
+       boxmaxs[1] = (enlarge+1) * entboxmaxs[1] - enlarge * entboxmins[1] + entboxexpand;
+       boxmins[2] = (enlarge+1) * entboxmins[2] - enlarge * entboxmaxs[2] - entboxexpand;
+       boxmaxs[2] = (enlarge+1) * entboxmaxs[2] - enlarge * entboxmins[2] + entboxexpand;
 
        VectorMAM(0.5f, boxmins, 0.5f, boxmaxs, endpoints[0]);
        for (traceindex = 1;traceindex < numtraces;traceindex++)
@@ -1729,7 +1731,7 @@ static void SV_MarkWriteEntityStateToClient(entity_state_t *s)
                                {
                                        int eyeindex;
                                        for (eyeindex = 0;eyeindex < sv.writeentitiestoclient_numeyes;eyeindex++)
-                                               if(SV_CanSeeBox(samples, sv_cullentities_trace_eyejitter.value, enlarge, sv.writeentitiestoclient_eyes[eyeindex], ed->priv.server->cullmins, ed->priv.server->cullmaxs))
+                                               if(SV_CanSeeBox(samples, sv_cullentities_trace_eyejitter.value, enlarge, sv_cullentities_trace_expand.value, sv.writeentitiestoclient_eyes[eyeindex], ed->priv.server->cullmins, ed->priv.server->cullmaxs))
                                                        break;
                                        if(eyeindex < sv.writeentitiestoclient_numeyes)
                                                svs.clients[sv.writeentitiestoclient_clientnumber].visibletime[s->number] =
@@ -1812,7 +1814,7 @@ static void SV_AddCameraEyes(void)
                for(k = 0; k < sv.writeentitiestoclient_numeyes; ++k)
                if(eye_levels[k] <= MAX_EYE_RECURSION)
                {
-                       if(SV_CanSeeBox(sv_cullentities_trace_samples.integer, sv_cullentities_trace_eyejitter.value, sv_cullentities_trace_enlarge.value, sv.writeentitiestoclient_eyes[k], mi, ma))
+                       if(SV_CanSeeBox(sv_cullentities_trace_samples.integer, sv_cullentities_trace_eyejitter.value, sv_cullentities_trace_enlarge.value, sv_cullentities_trace_expand.value, sv.writeentitiestoclient_eyes[k], mi, ma))
                        {
                                eye_levels[sv.writeentitiestoclient_numeyes] = eye_levels[k] + 1;
                                VectorCopy(camera_origins[j], sv.writeentitiestoclient_eyes[sv.writeentitiestoclient_numeyes]);
@@ -1873,7 +1875,7 @@ static void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, size
                vec_t predtime = bound(0, host_client->ping, sv_cullentities_trace_prediction_time.value);
                vec3_t predeye;
                VectorMA(eye, predtime, PRVM_serveredictvector(camera, velocity), predeye);
-               if (SV_CanSeeBox(1, 0, 0, eye, predeye, predeye))
+               if (SV_CanSeeBox(1, 0, 0, 0, eye, predeye, predeye))
                {
                        VectorCopy(predeye, sv.writeentitiestoclient_eyes[sv.writeentitiestoclient_numeyes]);
                        sv.writeentitiestoclient_numeyes++;