This tutorial is sort of a compilation of several tutorials I have foun 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, vecLook, 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 end
|
Please 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!
Thats all, any questions, email me -Cale 'Mazor' Dunlap Firearms Half-Life |