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)"};
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);
//==================================================================================
-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;
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))
//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
{
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;
}
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;
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"};
/*
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)
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)
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");
mvertex_t *points;
vec3_t mins, maxs; // culling
mplane_t plane;
+ double tracetime; // refreshed to realtime by traceline tests
}
mportal_t;
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"};
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);
// 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;
// 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;
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"
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);
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"};
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);
#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;
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++)
{
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] =
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]);
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++;