From bc75fd7a5ccfe403cea6d6fb71b7970f7959f661 Mon Sep 17 00:00:00 2001 From: havoc Date: Fri, 23 Feb 2007 09:36:47 +0000 Subject: [PATCH] patch from div0 that adds clientside culling of entities by TraceLineOfSight (like sv_cullentities_trace does), and merges the client and server code for this culling, this also adds traces for predicted player movement on the server to reduce the "items popping up" problem when running into a new room git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@6904 d7cf8633-e32d-0410-b094-e92efae38249 --- client.h | 3 +++ gl_rmain.c | 22 ++++++++++++++++++++++ model_brush.c | 36 ++++++++++++++++++++++++++++++++++++ model_shared.h | 3 +++ sv_main.c | 38 ++++++++++---------------------------- 5 files changed, 74 insertions(+), 28 deletions(-) diff --git a/client.h b/client.h index 60d605db..72ed2c74 100644 --- a/client.h +++ b/client.h @@ -269,6 +269,9 @@ typedef struct entity_render_s 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; diff --git a/gl_rmain.c b/gl_rmain.c index c5d0c0d3..095a5f9c 100644 --- a/gl_rmain.c +++ b/gl_rmain.c @@ -46,6 +46,10 @@ cvar_t r_showdisabledepthtest = {0, "r_showdisabledepthtest", "0", "disables dep 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"}; @@ -1132,6 +1136,10 @@ void GL_Main_Init(void) 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); @@ -1317,6 +1325,20 @@ static void R_View_UpdateEntityVisible (void) 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 { diff --git a/model_brush.c b/model_brush.c index c1795f3c..7bf40f52 100644 --- a/model_brush.c +++ b/model_brush.c @@ -6083,3 +6083,39 @@ void Mod_MAP_Load(model_t *mod, void *buffer, void *bufferend) 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; +} + diff --git a/model_shared.h b/model_shared.h index 405d291c..0dd2fb33 100644 --- a/model_shared.h +++ b/model_shared.h @@ -718,5 +718,8 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend); 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 diff --git a/sv_main.c b/sv_main.c index 0a429f09..bef7c415 100644 --- a/sv_main.c +++ b/sv_main.c @@ -46,6 +46,10 @@ extern cvar_t sv_random_seed; 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)"}; @@ -127,6 +131,10 @@ void SV_Init (void) 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); @@ -807,7 +815,6 @@ static client_t *sv_writeentitiestoclient_client; void SV_MarkWriteEntityStateToClient(entity_state_t *s) { int isbmodel; - vec3_t testorigin; model_t *model; prvm_edict_t *ed; if (sententitiesconsideration[s->number] == sententitiesmark) @@ -892,33 +899,8 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s) // 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++; -- 2.39.2