patch from div0 that adds clientside culling of entities by TraceLineOfSight (like...
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Fri, 23 Feb 2007 09:36:47 +0000 (09:36 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Fri, 23 Feb 2007 09:36:47 +0000 (09:36 +0000)
git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@6904 d7cf8633-e32d-0410-b094-e92efae38249

client.h
gl_rmain.c
model_brush.c
model_shared.h
sv_main.c

index 60d605d..72ed2c7 100644 (file)
--- 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;
 
index c5d0c0d..095a5f9 100644 (file)
@@ -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
        {
index c1795f3..7bf40f5 100644 (file)
@@ -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;
+}
+
index 405d291..0dd2fb3 100644 (file)
@@ -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
 
index 0a429f0..bef7c41 100644 (file)
--- 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++;