vec3_t modellight_ambient;
vec3_t modellight_diffuse; // q3bsp
vec3_t modellight_lightdir; // q3bsp
+
+ // last time visible during trace culling
+ double last_trace_visibility;
}
entity_render_t;
cvar_t r_drawportals = {0, "r_drawportals", "0", "shows portals (separating polygons) in world interior in quake1 maps"};
cvar_t r_drawentities = {0, "r_drawentities","1", "draw entities (doors, players, projectiles, etc)"};
cvar_t r_drawviewmodel = {0, "r_drawviewmodel","1", "draw your weapon model"};
+cvar_t r_cullentities_trace = {0, "r_cullentities_trace", "1", "probabistically cull invisible entities"};
+cvar_t r_cullentities_trace_samples = {0, "r_cullentities_trace_samples", "2", "number of samples to test for entity culling"};
+cvar_t r_cullentities_trace_enlarge = {0, "r_cullentities_trace_enlarge", "0", "box enlargement for entity culling"};
+cvar_t r_cullentities_trace_delay = {0, "r_cullentities_trace_delay", "1", "number of seconds until the entity gets actually culled"};
cvar_t r_speeds = {0, "r_speeds","0", "displays rendering statistics and per-subsystem timings"};
cvar_t r_fullbright = {0, "r_fullbright","0", "make everything bright cheat (not allowed in multiplayer)"};
cvar_t r_wateralpha = {CVAR_SAVE, "r_wateralpha","1", "opacity of water polygons"};
Cvar_RegisterVariable(&r_showdisabledepthtest);
Cvar_RegisterVariable(&r_drawportals);
Cvar_RegisterVariable(&r_drawentities);
+ Cvar_RegisterVariable(&r_cullentities_trace);
+ Cvar_RegisterVariable(&r_cullentities_trace_samples);
+ Cvar_RegisterVariable(&r_cullentities_trace_enlarge);
+ Cvar_RegisterVariable(&r_cullentities_trace_delay);
Cvar_RegisterVariable(&r_drawviewmodel);
Cvar_RegisterVariable(&r_speeds);
Cvar_RegisterVariable(&r_fullbrights);
ent = r_refdef.entities[i];
r_viewcache.entityvisible[i] = !(ent->flags & renderimask) && !R_CullBox(ent->mins, ent->maxs) && ((ent->effects & EF_NODEPTHTEST) || r_refdef.worldmodel->brush.BoxTouchingVisibleLeafs(r_refdef.worldmodel, r_viewcache.world_leafvisible, ent->mins, ent->maxs));
}
+ if(r_cullentities_trace.integer)
+ {
+ for (i = 0;i < r_refdef.numentities;i++)
+ {
+ ent = r_refdef.entities[i];
+ if(r_viewcache.entityvisible[i] && !(ent->effects & EF_NODEPTHTEST) && !(ent->model && (ent->model->name[0] == '*')))
+ {
+ if(Mod_CanSeeBox_Trace(r_cullentities_trace_samples.integer, r_cullentities_trace_enlarge.value, r_refdef.worldmodel, r_view.origin, ent->mins, ent->maxs))
+ ent->last_trace_visibility = realtime;
+ if(ent->last_trace_visibility < realtime - r_cullentities_trace_delay.value)
+ r_viewcache.entityvisible[i] = 0;
+ }
+ }
+ }
}
else
{
Host_Error("Mod_MAP_Load: not yet implemented");
}
+qboolean Mod_CanSeeBox_Trace(int numsamples, float t, model_t *model, vec3_t eye, vec3_t minsX, vec3_t maxsX)
+{
+ // we already have done PVS culling at this point...
+ // so we don't need to do it again.
+
+ int i;
+ vec3_t testorigin, mins, maxs;
+
+ testorigin[0] = (minsX[0] + maxsX[0]) * 0.5;
+ testorigin[1] = (minsX[1] + maxsX[1]) * 0.5;
+ testorigin[2] = (minsX[2] + maxsX[2]) * 0.5;
+
+ if(model->brush.TraceLineOfSight(model, eye, testorigin))
+ return 1;
+
+ // expand the box a little
+ mins[0] = (t+1) * minsX[0] - t * maxsX[0];
+ maxs[0] = (t+1) * maxsX[0] - t * minsX[0];
+ mins[1] = (t+1) * minsX[1] - t * maxsX[1];
+ maxs[1] = (t+1) * maxsX[1] - t * minsX[1];
+ mins[2] = (t+1) * minsX[2] - t * maxsX[2];
+ maxs[2] = (t+1) * maxsX[2] - t * minsX[2];
+
+ for(i = 0; i != numsamples; ++i)
+ {
+ testorigin[0] = lhrandom(mins[0], maxs[0]);
+ testorigin[1] = lhrandom(mins[1], maxs[1]);
+ testorigin[2] = lhrandom(mins[2], maxs[2]);
+
+ if(model->brush.TraceLineOfSight(model, eye, testorigin))
+ return 1;
+ }
+
+ return 0;
+}
+
void Mod_IDSP_Load(model_t *mod, void *buffer, void *bufferend);
void Mod_IDS2_Load(model_t *mod, void *buffer, void *bufferend);
+// utility
+qboolean Mod_CanSeeBox_Trace(int numsamples, float t, model_t *model, vec3_t eye, vec3_t minsX, vec3_t maxsX);
+
#endif // MODEL_SHARED_H
static cvar_t sv_cullentities_pvs = {0, "sv_cullentities_pvs", "1", "fast but loose culling of hidden entities"}; // fast but loose
static cvar_t sv_cullentities_trace = {0, "sv_cullentities_trace", "0", "somewhat slow but very tight culling of hidden entities, minimizes network traffic and makes wallhack cheats useless"}; // tends to get false negatives, uses a timeout to keep entities visible a short time after becoming hidden
+static cvar_t sv_cullentities_trace_samples = {0, "sv_cullentities_trace_samples", "1", "number of samples to test for entity culling"};
+static cvar_t sv_cullentities_trace_samples_extra = {0, "sv_cullentities_trace_samples", "2", "number of samples to test for entity culling when the entity affects its surroundings by e.g. dlight"};
+static cvar_t sv_cullentities_trace_enlarge = {0, "sv_cullentities_trace_enlarge", "0", "box enlargement for entity culling"};
+static cvar_t sv_cullentities_trace_delay = {0, "sv_cullentities_trace_delay", "1", "number of seconds until the entity gets actually culled"};
static cvar_t sv_cullentities_nevercullbmodels = {0, "sv_cullentities_nevercullbmodels", "0", "if enabled the clients are always notified of moving doors and lifts and other submodels of world (warning: eats a lot of network bandwidth on some levels!)"};
static cvar_t sv_cullentities_stats = {0, "sv_cullentities_stats", "0", "displays stats on network entities culled by various methods for each client"};
static cvar_t sv_entpatch = {0, "sv_entpatch", "1", "enables loading of .ent files to override entities in the bsp (for example Threewave CTF server pack contains .ent patch files enabling play of CTF on id1 maps)"};
Cvar_RegisterVariable (&sv_nostep);
Cvar_RegisterVariable (&sv_cullentities_pvs);
Cvar_RegisterVariable (&sv_cullentities_trace);
+ Cvar_RegisterVariable (&sv_cullentities_trace_samples);
+ Cvar_RegisterVariable (&sv_cullentities_trace_samples_extra);
+ Cvar_RegisterVariable (&sv_cullentities_trace_enlarge);
+ Cvar_RegisterVariable (&sv_cullentities_trace_delay);
Cvar_RegisterVariable (&sv_cullentities_nevercullbmodels);
Cvar_RegisterVariable (&sv_cullentities_stats);
Cvar_RegisterVariable (&sv_entpatch);
void SV_MarkWriteEntityStateToClient(entity_state_t *s)
{
int isbmodel;
- vec3_t testorigin;
model_t *model;
prvm_edict_t *ed;
if (sententitiesconsideration[s->number] == sententitiesmark)
// or not seen by random tracelines
if (sv_cullentities_trace.integer && !isbmodel)
{
- // LordHavoc: test center first
- testorigin[0] = (ed->priv.server->cullmins[0] + ed->priv.server->cullmaxs[0]) * 0.5f;
- testorigin[1] = (ed->priv.server->cullmins[1] + ed->priv.server->cullmaxs[1]) * 0.5f;
- testorigin[2] = (ed->priv.server->cullmins[2] + ed->priv.server->cullmaxs[2]) * 0.5f;
- if (sv.worldmodel->brush.TraceLineOfSight(sv.worldmodel, sv_writeentitiestoclient_testeye, testorigin))
- sv_writeentitiestoclient_client->visibletime[s->number] = realtime + 1;
- else
- {
- // LordHavoc: test random offsets, to maximize chance of detection
- testorigin[0] = lhrandom(ed->priv.server->cullmins[0], ed->priv.server->cullmaxs[0]);
- testorigin[1] = lhrandom(ed->priv.server->cullmins[1], ed->priv.server->cullmaxs[1]);
- testorigin[2] = lhrandom(ed->priv.server->cullmins[2], ed->priv.server->cullmaxs[2]);
- if (sv.worldmodel->brush.TraceLineOfSight(sv.worldmodel, sv_writeentitiestoclient_testeye, testorigin))
- sv_writeentitiestoclient_client->visibletime[s->number] = realtime + 1;
- else
- {
- if (s->specialvisibilityradius)
- {
- // LordHavoc: test random offsets, to maximize chance of detection
- testorigin[0] = lhrandom(ed->priv.server->cullmins[0], ed->priv.server->cullmaxs[0]);
- testorigin[1] = lhrandom(ed->priv.server->cullmins[1], ed->priv.server->cullmaxs[1]);
- testorigin[2] = lhrandom(ed->priv.server->cullmins[2], ed->priv.server->cullmaxs[2]);
- if (sv.worldmodel->brush.TraceLineOfSight(sv.worldmodel, sv_writeentitiestoclient_testeye, testorigin))
- sv_writeentitiestoclient_client->visibletime[s->number] = realtime + 1;
- }
- }
- }
+ if(Mod_CanSeeBox_Trace(s->specialvisibilityradius ? sv_cullentities_trace_samples_extra.integer : sv_cullentities_trace_samples.integer, sv_cullentities_trace_enlarge.value, sv.worldmodel, sv_writeentitiestoclient_testeye, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
+ sv_writeentitiestoclient_client->visibletime[s->number] = realtime + sv_cullentities_trace_delay.value;
if (realtime > sv_writeentitiestoclient_client->visibletime[s->number])
{
sv_writeentitiestoclient_culled_trace++;