This is another article I wrote on The Wavelength in 2003. It is specific to the Half-Life 1 SDK, incase there’s anybody out there still writing HL1 mods:
This tutorial is sort of a compilation of several tutorials I have found regarding field of view and line of sight. These people (including myself) should be given credit where/when it is due: X-0ut, bigguy, and the Holy Wars team.
For starters, we need to declare the external variable which stores the client’s view angles, so towards the top of the file entity.cpp find:
extern vec3_t v_origin;
int g_iAlive = 1;Add the v_angles variable to the end of the extern list after v_origin so that same block of code will look like this:
extern vec3_t v_origin, v_angles;
int g_iAlive = 1;Now that we have the client’s view angles, lets add some functions!
Still in the same file (entity.cpp) after:extern "C"
{
//—[SNIP]—//
struct cl_entity_s DLLEXPORT *HUD_GetUserEntity( int index );
}Put this code in:
// mazor @add// mazor – this is holy wars' code, be sure to thank them
#define FOV_MIN_DISTANCE 100
static bool PlayerIsInFOV( struct cl_entity_s *ent )
{
Vector up, right, forward;
AngleVectors( v_angles, forward, right, up );
float distance = ( ent->curstate.origin – v_origin ).Length();// nearby players are always in FOV
if ( distance <= FOV_MIN_DISTANCE * 2 )
return true;// if it's not in front, don't even bother
Vector vecDir = ( ent->curstate.origin – v_origin ).Normalize( );
if ( DotProduct ( vecDir, forward ) < 0 )
return false;return true;
}
// Returns true if the given entity is in the current Field Of View
// It's not 100% accurate, and it can fail for large objects – but it's fine
// for our purposes (optimization).
static bool IsInFOV( struct cl_entity_s *ent )
{
Vector forward, right, up;
AngleVectors( v_angles, forward, right, up );
float distance = ( ent->curstate.origin – v_origin ).Length();// nearby objects are always in FOV
if ( distance <= FOV_MIN_DISTANCE )
return true;// if it's not in front, don't even bother
Vector vecDir = ( ent->curstate.origin – v_origin ).Normalize( );
if ( DotProduct ( vecDir, forward ) < 0 )
return false;// this code is partially lifted from somewhere else in HL's code
float dot = fabs( DotProduct ( vecDir, right ) )
+ fabs( DotProduct ( vecDir, up ) ) * 0.5;
// tweak for distance
dot *= 1.0 + 0.2 * ( distance / 8192 );float arc = 1 – (gHUD.m_iFOV/360); // mazor – I'm not sure how accurate this is, but it seem to work fine
if ( dot > arc )
return false;return true;
}
// mazor – end holy wars' code// mazor – this is bigguy's code, thank him dearly
static bool IsInLOS( struct cl_entity_s *pTarget )
{
// we've got a 3rd person camera…
Vector forward, up, vecL
ook, vecTarget;
pmtrace_t tr;
AngleVectors(v_angles, forward, NULL, up);
vecLook = v_origin;
vecTarget = pTarget->origin;for (int i = 0; i < 6; i++)
{
vecTarget.x += gEngfuncs.pfnRandomFloat(pTarget->curstate.mins.x, pTarget->curstate.maxs.x);
vecTarget.y += gEngfuncs.pfnRandomFloat(pTarget->curstate.mins.y, pTarget->curstate.maxs.y);
vecTarget.z += gEngfuncs.pfnRandomFloat(pTarget->curstate.mins.z, pTarget->curstate.maxs.z);if (!pTarget->player) // ALWAYS ALWAYS draw the player if theyre in the FOV, regardless if we can trace to them
{
tr = *(gEngfuncs.PM_TraceLine( (float *)&vecLook, (float *)&vecTarget, 0, 2, -1 ));
// PM_ParticleLine((float *)&vecLook, (float *)&vecTarget, 77, 1.0, 0.0 );if (tr.fraction < 1)
{
if (tr.ent)
return true; // assume we hit something we can see through
else
continue;
}
else
return true;
}
else
return true;
vecTarget = pTarget->origin; // reset target's origin
}return false;
}
// mazor – end bigguy's code// mazor end
That just defined all the functions we’ll need to calculate if a player or entity is inside the client’s FOV and LOS. Now we just need to make use of these functions, so find the function int DLLEXPORT HUD_AddEntity( int type, struct cl_entity_s *ent, const char *modelname ), still in entity.cpp, and put this block of code inside of it:
// mazor begin
switch ( type )
{
case ET_NORMAL:
if( ent->model->type == mod_brush || ent->model->type == mod_sprite ) // always render brush models and sprites
break; // always draw brush models
if( g_iUser1 == 0 ) // if we aren't spectator
{
if( !IsInLOS(ent) )
return 0;
if( !IsInFOV(ent) )
return 0;
}
// mazor end
break;
case ET_PLAYER:
if( g_iUser1 == 0 )
{
if( !PlayerIsInFOV(ent) )
return 0;
}
break;
case ET_BEAM:
case ET_TEMPENTITY:
case ET_FRAGMENTED:
default:
break;
}
// mazor endPlease note that if you have any other special things you need to do to the entities, it might be a good idea to add them to this struct, but by default, it should work.
KNOWN ISSUE: When an entity is on the verge of being masked by a world brush, the LOS calculation will cause the entity to flicker very rapidly, hence why this SHOULD NOT be calculated on players!
You must be logged in to post a comment.