From 8ab339588b127e88e27bfa08f6eccbf9400b51f9 Mon Sep 17 00:00:00 2001 From: havoc Date: Sun, 11 Mar 2018 19:24:42 +0000 Subject: [PATCH] Rework R_CanSeeBox a bit to support an eyejitter feature, and make it available throughout the renderer. Make TraceLineOfSight check if the trace endpos is within a box, not just if fraction is 1 - this makes it much more likely to say an object is visible when the object overlaps walls and other obstacles as the rays only need to reach the box, it doesn't matter much where it goes inside the box. git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@12348 d7cf8633-e32d-0410-b094-e92efae38249 --- gl_rmain.c | 101 ++++++++++++++++++++++++++++++++++++------------- model_brush.c | 27 +++++++------ model_shared.h | 4 +- render.h | 1 + server.h | 2 +- snd_main.c | 2 +- sv_main.c | 44 ++++++++++++++++----- 7 files changed, 129 insertions(+), 52 deletions(-) diff --git a/gl_rmain.c b/gl_rmain.c index 6a316432..e7cfe472 100644 --- a/gl_rmain.c +++ b/gl_rmain.c @@ -28,6 +28,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "csprogs.h" #include "cl_video.h" #include "dpsoftrast.h" +#include "cl_collision.h" #ifdef SUPPORTD3D #include @@ -109,10 +110,12 @@ cvar_t r_drawworld = {0, "r_drawworld","1", "draw world (most static stuff)"}; cvar_t r_drawviewmodel = {0, "r_drawviewmodel","1", "draw your weapon model"}; cvar_t r_drawexteriormodel = {0, "r_drawexteriormodel","1", "draw your player model (e.g. in chase cam, reflections)"}; cvar_t r_cullentities_trace = {0, "r_cullentities_trace", "1", "probabistically cull invisible entities"}; +cvar_t r_cullentities_trace_entityocclusion = { 0, "r_cullentities_trace_entityocclusion", "1", "check for occluding entities such as doors, not just world hull" }; 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_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_t r_speeds = {0, "r_speeds","0", "displays rendering statistics and per-subsystem timings"}; cvar_t r_fullbright = {0, "r_fullbright","0", "makes map very bright and renders faster"}; @@ -4351,6 +4354,7 @@ void GL_Main_Init(void) Cvar_RegisterVariable(&r_draw2d); Cvar_RegisterVariable(&r_drawworld); Cvar_RegisterVariable(&r_cullentities_trace); + Cvar_RegisterVariable(&r_cullentities_trace_entityocclusion); Cvar_RegisterVariable(&r_cullentities_trace_samples); Cvar_RegisterVariable(&r_cullentities_trace_tempentitysamples); Cvar_RegisterVariable(&r_cullentities_trace_enlarge); @@ -5218,42 +5222,88 @@ static void R_View_UpdateEntityLighting (void) } } -#define MAX_LINEOFSIGHTTRACES 64 - -static qboolean R_CanSeeBox(int numsamples, vec_t enlarge, vec3_t eye, vec3_t entboxmins, vec3_t entboxmaxs) +qboolean R_CanSeeBox(int numsamples, vec_t eyejitter, vec_t entboxenlarge, vec3_t eye, vec3_t entboxmins, vec3_t entboxmaxs) { int i; + vec3_t eyemins, eyemaxs; vec3_t boxmins, boxmaxs; vec3_t start; vec3_t end; dp_model_t *model = r_refdef.scene.worldmodel; + static vec3_t positions[] = { + { 0.5f, 0.5f, 0.5f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f }, + { 0.0f, 1.0f, 0.0f }, + { 0.0f, 1.0f, 1.0f }, + { 1.0f, 0.0f, 0.0f }, + { 1.0f, 0.0f, 1.0f }, + { 1.0f, 1.0f, 0.0f }, + { 1.0f, 1.0f, 1.0f }, + }; + + // sample count can be set to -1 to skip this logic, for flicker-prone objects + if (numsamples < 0) + return true; + + // view origin is not used for culling in portal/reflection/refraction renders or isometric views + if (r_refdef.view.useclipplane || !r_refdef.view.useperspective || r_trippy.integer) + return true; - if (!model || !model->brush.TraceLineOfSight) + if (!r_cullentities_trace_entityocclusion.integer && (!model || !model->brush.TraceLineOfSight)) return true; + // expand the eye box a little + eyemins[0] = eye[0] - eyejitter; + eyemaxs[0] = eye[0] + eyejitter; + eyemins[1] = eye[1] - eyejitter; + eyemaxs[1] = eye[1] + eyejitter; + 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]; - - // return true if eye is inside enlarged box - if (BoxesOverlap(boxmins, boxmaxs, eye, eye)) + 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]; + + // return true if eye overlaps enlarged box + if (BoxesOverlap(boxmins, boxmaxs, eyemins, eyemaxs)) return true; - // try center - VectorCopy(eye, start); - VectorMAM(0.5f, boxmins, 0.5f, boxmaxs, end); - if (model->brush.TraceLineOfSight(model, start, end)) + // try specific positions in the box first - note that these can be cached + if (r_cullentities_trace_entityocclusion.integer) + { + for (i = 0; i < sizeof(positions) / sizeof(positions[0]); i++) + { + VectorCopy(eye, start); + end[0] = boxmins[0] + (boxmaxs[0] - boxmins[0]) * positions[i][0]; + end[1] = boxmins[1] + (boxmaxs[1] - boxmins[1]) * positions[i][1]; + end[2] = boxmins[2] + (boxmaxs[2] - boxmins[2]) * positions[i][2]; + //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_NOMONSTERS, SUPERCONTENTS_SOLID, SUPERCONTENTS_SKY); + // not picky - if the trace ended anywhere in the box we're good + if (BoxesOverlap(trace.endpos, trace.endpos, boxmins, boxmaxs)) + return true; + } + } + else if (model->brush.TraceLineOfSight(model, start, end, boxmins, boxmaxs)) return true; // try various random positions - for (i = 0;i < numsamples;i++) + for (i = 0; i < numsamples; i++) { + VectorSet(start, lhrandom(eyemins[0], eyemaxs[0]), lhrandom(eyemins[1], eyemaxs[1]), lhrandom(eyemins[2], eyemaxs[2])); VectorSet(end, lhrandom(boxmins[0], boxmaxs[0]), lhrandom(boxmins[1], boxmaxs[1]), lhrandom(boxmins[2], boxmaxs[2])); - if (model->brush.TraceLineOfSight(model, start, end)) + if (r_cullentities_trace_entityocclusion.integer) + { + trace_t trace = CL_TraceLine(start, end, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, SUPERCONTENTS_SKY, 0.0f, true, false, NULL, true, true); + // not picky - if the trace ended anywhere in the box we're good + if (BoxesOverlap(trace.endpos, trace.endpos, boxmins, boxmaxs)) + return true; + } + else if (model->brush.TraceLineOfSight(model, start, end, boxmins, boxmaxs)) return true; } @@ -5302,22 +5352,19 @@ static void R_View_UpdateEntityVisible (void) r_refdef.viewcache.entityvisible[i] = true; } } - if(r_cullentities_trace.integer && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.TraceLineOfSight && !r_refdef.view.useclipplane && !r_trippy.integer) - // sorry, this check doesn't work for portal/reflection/refraction renders as the view origin is not useful for culling + if (r_cullentities_trace.integer) { for (i = 0;i < r_refdef.scene.numentities;i++) { if (!r_refdef.viewcache.entityvisible[i]) continue; ent = r_refdef.scene.entities[i]; - if(!(ent->flags & (RENDER_VIEWMODEL | RENDER_WORLDOBJECT | RENDER_NODEPTHTEST)) && !(ent->model && (ent->model->name[0] == '*'))) + if (!(ent->flags & (RENDER_VIEWMODEL | RENDER_WORLDOBJECT | RENDER_NODEPTHTEST)) && !(ent->model && (ent->model->name[0] == '*'))) { - samples = ent->entitynumber ? r_cullentities_trace_samples.integer : r_cullentities_trace_tempentitysamples.integer; - if (samples < 0) - continue; // temp entities do pvs only - if(R_CanSeeBox(samples, r_cullentities_trace_enlarge.value, r_refdef.view.origin, ent->mins, ent->maxs)) + 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)) ent->last_trace_visibility = realtime; - if(ent->last_trace_visibility < realtime - r_cullentities_trace_delay.value) + if (ent->last_trace_visibility < realtime - r_cullentities_trace_delay.value) r_refdef.viewcache.entityvisible[i] = 0; } } diff --git a/model_brush.c b/model_brush.c index 37e2fd58..9c19c0b3 100644 --- a/model_brush.c +++ b/model_brush.c @@ -70,7 +70,7 @@ static texture_t mod_q1bsp_texture_lava; static texture_t mod_q1bsp_texture_slime; static texture_t mod_q1bsp_texture_water; -static qboolean Mod_Q3BSP_TraceLineOfSight(struct model_s *model, const vec3_t start, const vec3_t end); +static qboolean Mod_Q3BSP_TraceLineOfSight(struct model_s *model, const vec3_t start, const vec3_t end, const vec3_t acceptmins, const vec3_t acceptmaxs); void Mod_BrushInit(void) { @@ -1182,11 +1182,11 @@ void Collision_ClipTrace_Point(trace_t *trace, const vec3_t cmins, const vec3_t } } -static qboolean Mod_Q1BSP_TraceLineOfSight(struct model_s *model, const vec3_t start, const vec3_t end) +static qboolean Mod_Q1BSP_TraceLineOfSight(struct model_s *model, const vec3_t start, const vec3_t end, const vec3_t acceptmins, const vec3_t acceptmaxs) { trace_t trace; Mod_Q1BSP_TraceLine(model, NULL, NULL, &trace, start, end, SUPERCONTENTS_VISBLOCKERMASK, 0); - return trace.fraction == 1; + return trace.fraction == 1 || BoxesOverlap(trace.endpos, trace.endpos, acceptmins, acceptmaxs); } static int Mod_Q1BSP_LightPoint_RecursiveBSPNode(dp_model_t *model, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal, const mnode_t *node, float x, float y, float startz, float endz) @@ -6786,7 +6786,7 @@ static void Mod_Q3BSP_LightPoint(dp_model_t *model, const vec3_t p, vec3_t ambie //Con_Printf("result: ambient %f %f %f diffuse %f %f %f diffusenormal %f %f %f\n", ambientcolor[0], ambientcolor[1], ambientcolor[2], diffusecolor[0], diffusecolor[1], diffusecolor[2], diffusenormal[0], diffusenormal[1], diffusenormal[2]); } -static int Mod_Q3BSP_TraceLineOfSight_RecursiveNodeCheck(mnode_t *node, double p1[3], double p2[3]) +static int Mod_Q3BSP_TraceLineOfSight_RecursiveNodeCheck(mnode_t *node, double p1[3], double p2[3], double endpos[3]) { double t1, t2; double midf, mid[3]; @@ -6836,31 +6836,34 @@ static int Mod_Q3BSP_TraceLineOfSight_RecursiveNodeCheck(mnode_t *node, double p // or if start is solid and end is empty // as these degenerate cases usually indicate the eye is in solid and // should see the target point anyway - ret = Mod_Q3BSP_TraceLineOfSight_RecursiveNodeCheck(node->children[side ], p1, mid); + ret = Mod_Q3BSP_TraceLineOfSight_RecursiveNodeCheck(node->children[side ], p1, mid, endpos); if (ret != 0) return ret; - ret = Mod_Q3BSP_TraceLineOfSight_RecursiveNodeCheck(node->children[side ^ 1], mid, p2); + ret = Mod_Q3BSP_TraceLineOfSight_RecursiveNodeCheck(node->children[side ^ 1], mid, p2, endpos); if (ret != 1) return ret; + VectorCopy(mid, endpos); return 2; } return ((mleaf_t *)node)->clusterindex < 0; } -static qboolean Mod_Q3BSP_TraceLineOfSight(struct model_s *model, const vec3_t start, const vec3_t end) +static qboolean Mod_Q3BSP_TraceLineOfSight(struct model_s *model, const vec3_t start, const vec3_t end, const vec3_t acceptmins, const vec3_t acceptmaxs) { if (model->brush.submodel || mod_q3bsp_tracelineofsight_brushes.integer) { trace_t trace; model->TraceLine(model, NULL, NULL, &trace, start, end, SUPERCONTENTS_VISBLOCKERMASK, 0); - return trace.fraction == 1; + return trace.fraction == 1 || BoxesOverlap(trace.endpos, trace.endpos, acceptmins, acceptmaxs); } else { - double tracestart[3], traceend[3]; + double tracestart[3], traceend[3], traceendpos[3]; VectorCopy(start, tracestart); VectorCopy(end, traceend); - return !Mod_Q3BSP_TraceLineOfSight_RecursiveNodeCheck(model->brush.data_nodes, tracestart, traceend); + VectorCopy(end, traceendpos); + Mod_Q3BSP_TraceLineOfSight_RecursiveNodeCheck(model->brush.data_nodes, tracestart, traceend, traceendpos); + return BoxesOverlap(traceendpos, traceendpos, acceptmins, acceptmaxs); } } @@ -7230,11 +7233,11 @@ int Mod_CollisionBIH_PointSuperContents(struct model_s *model, int frame, const return trace.startsupercontents; } -qboolean Mod_CollisionBIH_TraceLineOfSight(struct model_s *model, const vec3_t start, const vec3_t end) +qboolean Mod_CollisionBIH_TraceLineOfSight(struct model_s *model, const vec3_t start, const vec3_t end, const vec3_t acceptmins, const vec3_t acceptmaxs) { trace_t trace; Mod_CollisionBIH_TraceLine(model, NULL, NULL, &trace, start, end, SUPERCONTENTS_VISBLOCKERMASK, 0); - return trace.fraction == 1; + return trace.fraction == 1 || BoxesOverlap(trace.endpos, trace.endpos, acceptmins, acceptmaxs); } void Mod_CollisionBIH_TracePoint_Mesh(dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, trace_t *trace, const vec3_t start, int hitsupercontentsmask, int skipsupercontentsmask) diff --git a/model_shared.h b/model_shared.h index e7e40039..1c186782 100644 --- a/model_shared.h +++ b/model_shared.h @@ -859,7 +859,7 @@ typedef struct model_brush_s void (*AmbientSoundLevelsForPoint)(struct model_s *model, const vec3_t p, unsigned char *out, int outsize); void (*RoundUpToHullSize)(struct model_s *cmodel, const vec3_t inmins, const vec3_t inmaxs, vec3_t outmins, vec3_t outmaxs); // trace a line of sight through this model (returns false if the line if sight is definitely blocked) - qboolean (*TraceLineOfSight)(struct model_s *model, const vec3_t start, const vec3_t end); + qboolean (*TraceLineOfSight)(struct model_s *model, const vec3_t start, const vec3_t end, const vec3_t acceptmins, const vec3_t acceptmaxs); char skybox[MAX_QPATH]; @@ -1240,7 +1240,7 @@ void Mod_CollisionBIH_TraceLine(dp_model_t *model, const struct frameblend_s *fr void Mod_CollisionBIH_TraceBox(dp_model_t *model, const struct frameblend_s *frameblend, const skeleton_t *skeleton, struct trace_s *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask, int skipsupercontentsmask); void Mod_CollisionBIH_TraceBrush(dp_model_t *model, const struct frameblend_s *frameblend, const skeleton_t *skeleton, struct trace_s *trace, struct colbrushf_s *start, struct colbrushf_s *end, int hitsupercontentsmask, int skipsupercontentsmask); void Mod_CollisionBIH_TracePoint_Mesh(dp_model_t *model, const struct frameblend_s *frameblend, const skeleton_t *skeleton, struct trace_s *trace, const vec3_t start, int hitsupercontentsmask, int skipsupercontentsmask); -qboolean Mod_CollisionBIH_TraceLineOfSight(struct model_s *model, const vec3_t start, const vec3_t end); +qboolean Mod_CollisionBIH_TraceLineOfSight(struct model_s *model, const vec3_t start, const vec3_t end, const vec3_t acceptmins, const vec3_t acceptmaxs); int Mod_CollisionBIH_PointSuperContents(struct model_s *model, int frame, const vec3_t point); int Mod_CollisionBIH_PointSuperContents_Mesh(struct model_s *model, int frame, const vec3_t point); bih_t *Mod_MakeCollisionBIH(dp_model_t *model, qboolean userendersurfaces, bih_t *out); diff --git a/render.h b/render.h index 09782f54..cba80598 100644 --- a/render.h +++ b/render.h @@ -162,6 +162,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); #include "r_modules.h" diff --git a/server.h b/server.h index e0721ca9..57eef855 100644 --- 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 SV_EntitiesInBox(const vec3_t mins, const vec3_t maxs, int maxedicts, prvm_edict_t **resultedicts); -qboolean SV_CanSeeBox(int numsamples, vec_t enlarge, vec3_t eye, vec3_t entboxmins, vec3_t entboxmaxs); +qboolean SV_CanSeeBox(int numsamples, vec_t eyejitter, vec_t enlarge, vec3_t eye, vec3_t entboxmins, vec3_t entboxmaxs); int SV_PointSuperContents(const vec3_t point); diff --git a/snd_main.c b/snd_main.c index e435485e..62807eb0 100644 --- a/snd_main.c +++ b/snd_main.c @@ -1502,7 +1502,7 @@ static void SND_Spatialize_WithSfx(channel_t *ch, qboolean isstatic, sfx_t *sfx) if(snd_spatialization_occlusion.integer & 2) if(!occluded) - if(cl.worldmodel && cl.worldmodel->brush.TraceLineOfSight && !cl.worldmodel->brush.TraceLineOfSight(cl.worldmodel, listener_origin, ch->origin)) + if(cl.worldmodel && cl.worldmodel->brush.TraceLineOfSight && !cl.worldmodel->brush.TraceLineOfSight(cl.worldmodel, listener_origin, ch->origin, ch->origin, ch->origin)) occluded = true; } if(occluded) diff --git a/sv_main.c b/sv_main.c index 85ca4fe5..d724aa1f 100644 --- 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_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_t sv_cullentities_trace_entityocclusion = {0, "sv_cullentities_trace_entityocclusion", "0", "also check if doors and other bsp models are in the way"}; @@ -493,6 +494,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_eyejitter); Cvar_RegisterVariable (&sv_cullentities_trace_entityocclusion); Cvar_RegisterVariable (&sv_cullentities_trace_prediction); Cvar_RegisterVariable (&sv_cullentities_trace_prediction_time); @@ -1485,12 +1487,14 @@ static void SV_PrepareEntitiesForSending(void) #define MAX_LINEOFSIGHTTRACES 64 -qboolean SV_CanSeeBox(int numtraces, vec_t enlarge, vec3_t eye, vec3_t entboxmins, vec3_t entboxmaxs) +qboolean SV_CanSeeBox(int numtraces, vec_t eyejitter, vec_t enlarge, vec3_t eye, vec3_t entboxmins, vec3_t entboxmaxs) { prvm_prog_t *prog = SVVM_prog; float pitchsign; float alpha; float starttransformed[3], endtransformed[3]; + float boxminstransformed[3], boxmaxstransformed[3]; + float localboxcenter[3], localboxextents[3], localboxmins[3], localboxmaxs[3]; int blocked = 0; int traceindex; int originalnumtouchedicts; @@ -1500,12 +1504,20 @@ qboolean SV_CanSeeBox(int numtraces, vec_t enlarge, vec3_t eye, vec3_t entboxmin dp_model_t *model; prvm_edict_t *touch; static prvm_edict_t *touchedicts[MAX_EDICTS]; + vec3_t eyemins, eyemaxs, start; vec3_t boxmins, boxmaxs; vec3_t clipboxmins, clipboxmaxs; vec3_t endpoints[MAX_LINEOFSIGHTTRACES]; numtraces = min(numtraces, MAX_LINEOFSIGHTTRACES); + // jitter the eye location within this box + eyemins[0] = eye[0] - eyejitter; + eyemaxs[0] = eye[0] + eyejitter; + eyemins[1] = eye[1] - eyejitter; + eyemaxs[1] = eye[1] + eyejitter; + 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]; @@ -1519,8 +1531,8 @@ qboolean SV_CanSeeBox(int numtraces, vec_t enlarge, vec3_t eye, vec3_t entboxmin VectorSet(endpoints[traceindex], lhrandom(boxmins[0], boxmaxs[0]), lhrandom(boxmins[1], boxmaxs[1]), lhrandom(boxmins[2], boxmaxs[2])); // calculate sweep box for the entire swarm of traces - VectorCopy(eye, clipboxmins); - VectorCopy(eye, clipboxmaxs); + VectorCopy(eyemins, clipboxmins); + VectorCopy(eyemaxs, clipboxmaxs); for (traceindex = 0;traceindex < numtraces;traceindex++) { clipboxmins[0] = min(clipboxmins[0], endpoints[traceindex][0]); @@ -1566,9 +1578,10 @@ qboolean SV_CanSeeBox(int numtraces, vec_t enlarge, vec3_t eye, vec3_t entboxmin for (traceindex = 0;traceindex < numtraces;traceindex++) { + VectorSet(start, lhrandom(eyemins[0], eyemaxs[0]), lhrandom(eyemins[1], eyemaxs[1]), lhrandom(eyemins[2], eyemaxs[2])); // check world occlusion if (sv.worldmodel && sv.worldmodel->brush.TraceLineOfSight) - if (!sv.worldmodel->brush.TraceLineOfSight(sv.worldmodel, eye, endpoints[traceindex])) + if (!sv.worldmodel->brush.TraceLineOfSight(sv.worldmodel, start, endpoints[traceindex], boxmins, boxmaxs)) continue; for (touchindex = 0;touchindex < numtouchedicts;touchindex++) { @@ -1581,9 +1594,22 @@ qboolean SV_CanSeeBox(int numtraces, vec_t enlarge, vec3_t eye, vec3_t entboxmin Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2], pitchsign * PRVM_serveredictvector(touch, angles)[0], PRVM_serveredictvector(touch, angles)[1], PRVM_serveredictvector(touch, angles)[2], 1); Matrix4x4_Invert_Simple(&imatrix, &matrix); // see if the ray hits this entity - Matrix4x4_Transform(&imatrix, eye, starttransformed); + Matrix4x4_Transform(&imatrix, start, starttransformed); Matrix4x4_Transform(&imatrix, endpoints[traceindex], endtransformed); - if (!model->brush.TraceLineOfSight(model, starttransformed, endtransformed)) + Matrix4x4_Transform(&imatrix, boxmins, boxminstransformed); + Matrix4x4_Transform(&imatrix, boxmaxs, boxmaxstransformed); + // transform the AABB to local space + VectorMAM(0.5f, boxminstransformed, 0.5f, boxmaxstransformed, localboxcenter); + localboxextents[0] = fabs(boxmaxstransformed[0] - localboxcenter[0]); + localboxextents[1] = fabs(boxmaxstransformed[1] - localboxcenter[1]); + localboxextents[2] = fabs(boxmaxstransformed[2] - localboxcenter[2]); + localboxmins[0] = localboxcenter[0] - localboxextents[0]; + localboxmins[1] = localboxcenter[1] - localboxextents[1]; + localboxmins[2] = localboxcenter[2] - localboxextents[2]; + localboxmaxs[0] = localboxcenter[0] + localboxextents[0]; + localboxmaxs[1] = localboxcenter[1] + localboxextents[1]; + localboxmaxs[2] = localboxcenter[2] + localboxextents[2]; + if (!model->brush.TraceLineOfSight(model, starttransformed, endtransformed, localboxmins, localboxmaxs)) { blocked++; break; @@ -1703,7 +1729,7 @@ static void SV_MarkWriteEntityStateToClient(entity_state_t *s) { int eyeindex; for (eyeindex = 0;eyeindex < sv.writeentitiestoclient_numeyes;eyeindex++) - if(SV_CanSeeBox(samples, enlarge, sv.writeentitiestoclient_eyes[eyeindex], ed->priv.server->cullmins, ed->priv.server->cullmaxs)) + if(SV_CanSeeBox(samples, sv_cullentities_trace_eyejitter.value, enlarge, 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] = @@ -1786,7 +1812,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_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.writeentitiestoclient_eyes[k], mi, ma)) { eye_levels[sv.writeentitiestoclient_numeyes] = eye_levels[k] + 1; VectorCopy(camera_origins[j], sv.writeentitiestoclient_eyes[sv.writeentitiestoclient_numeyes]); @@ -1847,7 +1873,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, eye, predeye, predeye)) + if (SV_CanSeeBox(1, 0, 0, eye, predeye, predeye)) { VectorCopy(predeye, sv.writeentitiestoclient_eyes[sv.writeentitiestoclient_numeyes]); sv.writeentitiestoclient_numeyes++; -- 2.39.2