xash3d-fwgs/engine/common/pm_trace.c
Gleb Mazovetskiy 5e0a0765ce Trim all trailing whitespace
The `.editorconfig` file in this repo is configured to trim all trailing
whitespace regardless of whether the line is modified.

Trims all trailing whitespace in the repository to make the codebase easier
to work with in editors that respect `.editorconfig`.

`git blame` becomes less useful on these lines but it already isn't very useful.

Commands:

```
find . -type f -name '*.h' -exec sed --in-place 's/[[:space:]]\+$//' {} \+
find . -type f -name '*.c' -exec sed --in-place 's/[[:space:]]\+$//' {} \+
```
2021-01-04 20:55:10 +03:00

736 lines
17 KiB
C

/*
pm_trace.c - pmove player trace code
Copyright (C) 2010 Uncle Mike
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "common.h"
#include "xash3d_mathlib.h"
#include "mod_local.h"
#include "pm_local.h"
#include "pm_movevars.h"
#include "enginefeatures.h"
#include "studio.h"
#include "world.h"
#define PM_AllowHitBoxTrace( model, hull ) ( model && model->type == mod_studio && ( FBitSet( model->flags, STUDIO_TRACE_HITBOX ) || hull == 2 ))
static mplane_t pm_boxplanes[6];
static mclipnode_t pm_boxclipnodes[6];
static hull_t pm_boxhull;
// default hullmins
static const vec3_t pm_hullmins[MAX_MAP_HULLS] =
{
{ -16, -16, -36 },
{ -16, -16, -18 },
{ 0, 0, 0 },
{ -32, -32, -32 },
};
// defualt hullmaxs
static const vec3_t pm_hullmaxs[MAX_MAP_HULLS] =
{
{ 16, 16, 36 },
{ 16, 16, 18 },
{ 0, 0, 0 },
{ 32, 32, 32 },
};
void Pmove_Init( void )
{
PM_InitBoxHull ();
// init default hull sizes
memcpy( host.player_mins, pm_hullmins, sizeof( pm_hullmins ));
memcpy( host.player_maxs, pm_hullmaxs, sizeof( pm_hullmaxs ));
}
/*
===================
PM_InitBoxHull
Set up the planes and clipnodes so that the six floats of a bounding box
can just be stored out and get a proper hull_t structure.
===================
*/
void PM_InitBoxHull( void )
{
int i, side;
pm_boxhull.clipnodes = pm_boxclipnodes;
pm_boxhull.planes = pm_boxplanes;
pm_boxhull.firstclipnode = 0;
pm_boxhull.lastclipnode = 5;
for( i = 0; i < 6; i++ )
{
pm_boxclipnodes[i].planenum = i;
side = i & 1;
pm_boxclipnodes[i].children[side] = CONTENTS_EMPTY;
if( i != 5 ) pm_boxclipnodes[i].children[side^1] = i + 1;
else pm_boxclipnodes[i].children[side^1] = CONTENTS_SOLID;
pm_boxplanes[i].type = i>>1;
pm_boxplanes[i].normal[i>>1] = 1.0f;
pm_boxplanes[i].signbits = 0;
}
}
/*
===================
PM_HullForBox
To keep everything totally uniform, bounding boxes are turned into small
BSP trees instead of being compared directly.
===================
*/
hull_t *PM_HullForBox( const vec3_t mins, const vec3_t maxs )
{
pm_boxplanes[0].dist = maxs[0];
pm_boxplanes[1].dist = mins[0];
pm_boxplanes[2].dist = maxs[1];
pm_boxplanes[3].dist = mins[1];
pm_boxplanes[4].dist = maxs[2];
pm_boxplanes[5].dist = mins[2];
return &pm_boxhull;
}
void PM_ConvertTrace( trace_t *out, pmtrace_t *in, edict_t *ent )
{
memcpy( out, in, 48 ); // matched
out->hitgroup = in->hitgroup;
out->ent = ent;
}
/*
==================
PM_HullPointContents
==================
*/
int PM_HullPointContents( hull_t *hull, int num, const vec3_t p )
{
mplane_t *plane;
if( !hull || !hull->planes ) // fantom bmodels?
return CONTENTS_NONE;
while( num >= 0 )
{
plane = &hull->planes[hull->clipnodes[num].planenum];
num = hull->clipnodes[num].children[PlaneDiff( p, plane ) < 0];
}
return num;
}
/*
==================
PM_HullForBsp
assume physent is valid
==================
*/
hull_t *PM_HullForBsp( physent_t *pe, playermove_t *pmove, float *offset )
{
hull_t *hull;
Assert( pe != NULL );
Assert( pe->model != NULL );
switch( pmove->usehull )
{
case 1:
hull = &pe->model->hulls[3];
break;
case 2:
hull = &pe->model->hulls[0];
break;
case 3:
hull = &pe->model->hulls[2];
break;
default:
hull = &pe->model->hulls[1];
break;
}
Assert( hull != NULL );
// calculate an offset value to center the origin
VectorSubtract( hull->clip_mins, pmove->player_mins[pmove->usehull], offset );
VectorAdd( offset, pe->origin, offset );
return hull;
}
/*
==================
PM_HullForStudio
generate multiple hulls as hitboxes
==================
*/
hull_t *PM_HullForStudio( physent_t *pe, playermove_t *pmove, int *numhitboxes )
{
vec3_t size;
VectorSubtract( pmove->player_maxs[pmove->usehull], pmove->player_mins[pmove->usehull], size );
VectorScale( size, 0.5f, size );
return Mod_HullForStudio( pe->studiomodel, pe->frame, pe->sequence, pe->angles, pe->origin, size, pe->controller, pe->blending, numhitboxes, NULL );
}
/*
==================
PM_RecursiveHullCheck
==================
*/
qboolean PM_RecursiveHullCheck( hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, pmtrace_t *trace )
{
mclipnode_t *node;
mplane_t *plane;
float t1, t2;
float frac, midf;
int side;
vec3_t mid;
loc0:
// check for empty
if( num < 0 )
{
if( num != CONTENTS_SOLID )
{
trace->allsolid = false;
if( num == CONTENTS_EMPTY )
trace->inopen = true;
else trace->inwater = true;
}
else trace->startsolid = true;
return true; // empty
}
if( hull->firstclipnode >= hull->lastclipnode )
{
// empty hull?
trace->allsolid = false;
trace->inopen = true;
return true;
}
if( num < hull->firstclipnode || num > hull->lastclipnode )
Host_Error( "PM_RecursiveHullCheck: bad node number %i\n", num );
// find the point distances
node = hull->clipnodes + num;
plane = hull->planes + node->planenum;
t1 = PlaneDiff( p1, plane );
t2 = PlaneDiff( p2, plane );
if( t1 >= 0.0f && t2 >= 0.0f )
{
num = node->children[0];
goto loc0;
}
if( t1 < 0.0f && t2 < 0.0f )
{
num = node->children[1];
goto loc0;
}
// put the crosspoint DIST_EPSILON pixels on the near side
side = (t1 < 0.0f);
if( side ) frac = ( t1 + DIST_EPSILON ) / ( t1 - t2 );
else frac = ( t1 - DIST_EPSILON ) / ( t1 - t2 );
if( frac < 0.0f ) frac = 0.0f;
if( frac > 1.0f ) frac = 1.0f;
midf = p1f + ( p2f - p1f ) * frac;
VectorLerp( p1, frac, p2, mid );
// move up to the node
if( !PM_RecursiveHullCheck( hull, node->children[side], p1f, midf, p1, mid, trace ))
return false;
// this recursion can not be optimized because mid would need to be duplicated on a stack
if( PM_HullPointContents( hull, node->children[side^1], mid ) != CONTENTS_SOLID )
{
// go past the node
return PM_RecursiveHullCheck( hull, node->children[side^1], midf, p2f, mid, p2, trace );
}
// never got out of the solid area
if( trace->allsolid )
return false;
// the other side of the node is solid, this is the impact point
if( !side )
{
VectorCopy( plane->normal, trace->plane.normal );
trace->plane.dist = plane->dist;
}
else
{
VectorNegate( plane->normal, trace->plane.normal );
trace->plane.dist = -plane->dist;
}
while( PM_HullPointContents( hull, hull->firstclipnode, mid ) == CONTENTS_SOLID )
{
// shouldn't really happen, but does occasionally
frac -= 0.1f;
if( frac < 0.0f )
{
trace->fraction = midf;
VectorCopy( mid, trace->endpos );
Con_Reportf( S_WARN "trace backed up past 0.0\n" );
return false;
}
midf = p1f + ( p2f - p1f ) * frac;
VectorLerp( p1, frac, p2, mid );
}
trace->fraction = midf;
VectorCopy( mid, trace->endpos );
return false;
}
pmtrace_t PM_PlayerTraceExt( playermove_t *pmove, vec3_t start, vec3_t end, int flags, int numents, physent_t *ents, int ignore_pe, pfnIgnore pmFilter )
{
physent_t *pe;
matrix4x4 matrix;
pmtrace_t trace_bbox;
pmtrace_t trace_hitbox;
pmtrace_t trace_total;
vec3_t offset, start_l, end_l;
vec3_t temp, mins, maxs;
int i, j, hullcount;
qboolean rotated, transform_bbox;
hull_t *hull = NULL;
memset( &trace_total, 0, sizeof( trace_total ));
VectorCopy( end, trace_total.endpos );
trace_total.fraction = 1.0f;
trace_total.ent = -1;
for( i = 0; i < numents; i++ )
{
pe = &ents[i];
if( i != 0 && ( flags & PM_WORLD_ONLY ))
break;
// run custom user filter
if( pmFilter != NULL )
{
if( pmFilter( pe ))
continue;
}
else if( ignore_pe != -1 )
{
if( i == ignore_pe )
continue;
}
if( pe->model != NULL && pe->solid == SOLID_NOT && pe->skin != CONTENTS_NONE )
continue;
if(( flags & PM_GLASS_IGNORE ) && pe->rendermode != kRenderNormal )
continue;
if(( flags & PM_CUSTOM_IGNORE ) && pe->solid == SOLID_CUSTOM )
continue;
hullcount = 1;
if( pe->solid == SOLID_CUSTOM )
{
VectorCopy( pmove->player_mins[pmove->usehull], mins );
VectorCopy( pmove->player_maxs[pmove->usehull], maxs );
VectorClear( offset );
}
else if( pe->model )
{
hull = PM_HullForBsp( pe, pmove, offset );
}
else
{
if( pe->studiomodel )
{
if( FBitSet( flags, PM_STUDIO_IGNORE ))
continue;
if( PM_AllowHitBoxTrace( pe->studiomodel, pmove->usehull ) && !FBitSet( flags, PM_STUDIO_BOX ))
{
hull = PM_HullForStudio( pe, pmove, &hullcount );
VectorClear( offset );
}
else
{
VectorSubtract( pe->mins, pmove->player_maxs[pmove->usehull], mins );
VectorSubtract( pe->maxs, pmove->player_mins[pmove->usehull], maxs );
hull = PM_HullForBox( mins, maxs );
VectorCopy( pe->origin, offset );
}
}
else
{
VectorSubtract( pe->mins, pmove->player_maxs[pmove->usehull], mins );
VectorSubtract( pe->maxs, pmove->player_mins[pmove->usehull], maxs );
hull = PM_HullForBox( mins, maxs );
VectorCopy( pe->origin, offset );
}
}
if( pe->solid == SOLID_BSP && !VectorIsNull( pe->angles ))
rotated = true;
else rotated = false;
if( FBitSet( host.features, ENGINE_PHYSICS_PUSHER_EXT ))
{
if(( check_angles( pe->angles[0] ) || check_angles( pe->angles[2] )) && pmove->usehull != 2 )
transform_bbox = true;
else transform_bbox = false;
}
else transform_bbox = false;
if( rotated )
{
if( transform_bbox )
Matrix4x4_CreateFromEntity( matrix, pe->angles, pe->origin, 1.0f );
else Matrix4x4_CreateFromEntity( matrix, pe->angles, offset, 1.0f );
Matrix4x4_VectorITransform( matrix, start, start_l );
Matrix4x4_VectorITransform( matrix, end, end_l );
if( transform_bbox )
{
World_TransformAABB( matrix, pmove->player_mins[pmove->usehull], pmove->player_maxs[pmove->usehull], mins, maxs );
VectorSubtract( hull->clip_mins, mins, offset ); // calc new local offset
for( j = 0; j < 3; j++ )
{
if( start_l[j] >= 0.0f )
start_l[j] -= offset[j];
else start_l[j] += offset[j];
if( end_l[j] >= 0.0f )
end_l[j] -= offset[j];
else end_l[j] += offset[j];
}
}
}
else
{
VectorSubtract( start, offset, start_l );
VectorSubtract( end, offset, end_l );
}
memset( &trace_bbox, 0, sizeof( trace_bbox ));
VectorCopy( end, trace_bbox.endpos );
trace_bbox.allsolid = true;
trace_bbox.fraction = 1.0f;
if( hullcount < 1 )
{
// g-cont. probably this never happens
trace_bbox.allsolid = false;
}
else if( pe->solid == SOLID_CUSTOM )
{
// run custom sweep callback
if( pmove->server || Host_IsLocalClient( ))
SV_ClipPMoveToEntity( pe, start, mins, maxs, end, &trace_bbox );
#if !XASH_DEDICATED
else CL_ClipPMoveToEntity( pe, start, mins, maxs, end, &trace_bbox );
#endif
}
else if( hullcount == 1 )
{
PM_RecursiveHullCheck( hull, hull->firstclipnode, 0, 1, start_l, end_l, &trace_bbox );
}
else
{
int last_hitgroup;
for( last_hitgroup = 0, j = 0; j < hullcount; j++ )
{
memset( &trace_hitbox, 0, sizeof( trace_hitbox ));
VectorCopy( end, trace_hitbox.endpos );
trace_hitbox.allsolid = true;
trace_hitbox.fraction = 1.0f;
PM_RecursiveHullCheck( &hull[j], hull[j].firstclipnode, 0, 1, start_l, end_l, &trace_hitbox );
if( j == 0 || trace_hitbox.allsolid || trace_hitbox.startsolid || trace_hitbox.fraction < trace_bbox.fraction )
{
if( trace_bbox.startsolid )
{
trace_bbox = trace_hitbox;
trace_bbox.startsolid = true;
}
else trace_bbox = trace_hitbox;
last_hitgroup = j;
}
}
trace_bbox.hitgroup = Mod_HitgroupForStudioHull( last_hitgroup );
}
if( trace_bbox.allsolid )
trace_bbox.startsolid = true;
if( trace_bbox.startsolid )
trace_bbox.fraction = 0.0f;
if( !trace_bbox.startsolid )
{
VectorLerp( start, trace_bbox.fraction, end, trace_bbox.endpos );
if( rotated )
{
VectorCopy( trace_bbox.plane.normal, temp );
Matrix4x4_TransformPositivePlane( matrix, temp, trace_bbox.plane.dist, trace_bbox.plane.normal, &trace_bbox.plane.dist );
}
else
{
trace_bbox.plane.dist = DotProduct( trace_bbox.endpos, trace_bbox.plane.normal );
}
}
if( trace_bbox.fraction < trace_total.fraction )
{
trace_total = trace_bbox;
trace_total.ent = i;
}
}
return trace_total;
}
int PM_TestPlayerPosition( playermove_t *pmove, vec3_t pos, pmtrace_t *ptrace, pfnIgnore pmFilter )
{
int i, j, hullcount;
vec3_t pos_l, offset;
hull_t *hull = NULL;
vec3_t mins, maxs;
pmtrace_t trace;
physent_t *pe;
trace = PM_PlayerTraceExt( pmove, pmove->origin, pmove->origin, 0, pmove->numphysent, pmove->physents, -1, pmFilter );
if( ptrace ) *ptrace = trace;
for( i = 0; i < pmove->numphysent; i++ )
{
pe = &pmove->physents[i];
// run custom user filter
if( pmFilter != NULL )
{
if( pmFilter( pe ))
continue;
}
if( pe->model != NULL && pe->solid == SOLID_NOT && pe->skin != CONTENTS_NONE )
continue;
hullcount = 1;
if( pe->solid == SOLID_CUSTOM )
{
VectorCopy( pmove->player_mins[pmove->usehull], mins );
VectorCopy( pmove->player_maxs[pmove->usehull], maxs );
VectorClear( offset );
}
else if( pe->model )
{
hull = PM_HullForBsp( pe, pmove, offset );
}
else if( PM_AllowHitBoxTrace( pe->studiomodel, pmove->usehull ))
{
hull = PM_HullForStudio( pe, pmove, &hullcount );
VectorClear( offset );
}
else
{
VectorSubtract( pe->mins, pmove->player_maxs[pmove->usehull], mins );
VectorSubtract( pe->maxs, pmove->player_mins[pmove->usehull], maxs );
hull = PM_HullForBox( mins, maxs );
VectorCopy( pe->origin, offset );
}
// CM_TransformedPointContents :-)
if( pe->solid == SOLID_BSP && !VectorIsNull( pe->angles ))
{
qboolean transform_bbox = false;
matrix4x4 matrix;
if( FBitSet( host.features, ENGINE_PHYSICS_PUSHER_EXT ))
{
if(( check_angles( pe->angles[0] ) || check_angles( pe->angles[2] )) && pmove->usehull != 2 )
transform_bbox = true;
}
if( transform_bbox )
Matrix4x4_CreateFromEntity( matrix, pe->angles, pe->origin, 1.0f );
else Matrix4x4_CreateFromEntity( matrix, pe->angles, offset, 1.0f );
Matrix4x4_VectorITransform( matrix, pos, pos_l );
if( transform_bbox )
{
World_TransformAABB( matrix, pmove->player_mins[pmove->usehull], pmove->player_maxs[pmove->usehull], mins, maxs );
VectorSubtract( hull->clip_mins, mins, offset ); // calc new local offset
for( j = 0; j < 3; j++ )
{
if( pos_l[j] >= 0.0f )
pos_l[j] -= offset[j];
else pos_l[j] += offset[j];
}
}
}
else
{
// offset the test point appropriately for this hull.
VectorSubtract( pos, offset, pos_l );
}
if( pe->solid == SOLID_CUSTOM )
{
pmtrace_t trace;
memset( &trace, 0, sizeof( trace ));
VectorCopy( pos, trace.endpos );
trace.allsolid = true;
trace.fraction = 1.0f;
// run custom sweep callback
if( pmove->server || Host_IsLocalClient( ))
SV_ClipPMoveToEntity( pe, pos, mins, maxs, pos, &trace );
#if !XASH_DEDICATED
else CL_ClipPMoveToEntity( pe, pos, mins, maxs, pos, &trace );
#endif
// if we inside the custom hull
if( trace.allsolid )
return i;
}
else if( hullcount == 1 )
{
if( PM_HullPointContents( hull, hull->firstclipnode, pos_l ) == CONTENTS_SOLID )
return i;
}
else
{
for( j = 0; j < hullcount; j++ )
{
if( PM_HullPointContents( &hull[j], hull[j].firstclipnode, pos_l ) == CONTENTS_SOLID )
return i;
}
}
}
return -1; // didn't hit anything
}
/*
=============
PM_TruePointContents
=============
*/
int PM_TruePointContents( playermove_t *pmove, const vec3_t p )
{
hull_t *hull = &pmove->physents[0].model->hulls[0];
if( hull )
{
return PM_HullPointContents( hull, hull->firstclipnode, p );
}
else
{
return CONTENTS_EMPTY;
}
}
/*
=============
PM_PointContents
=============
*/
int PM_PointContents( playermove_t *pmove, const vec3_t p )
{
int i, contents;
hull_t *hull;
vec3_t test;
physent_t *pe;
// sanity check
if( !p || !pmove->physents[0].model )
return CONTENTS_NONE;
// get base contents from world
contents = PM_HullPointContents( &pmove->physents[0].model->hulls[0], 0, p );
for( i = 1; i < pmove->numphysent; i++ )
{
pe = &pmove->physents[i];
if( pe->solid != SOLID_NOT ) // disabled ?
continue;
// only brushes can have special contents
if( !pe->model ) continue;
// check water brushes accuracy
hull = &pe->model->hulls[0];
if( FBitSet( pe->model->flags, MODEL_HAS_ORIGIN ) && !VectorIsNull( pe->angles ))
{
matrix4x4 matrix;
Matrix4x4_CreateFromEntity( matrix, pe->angles, pe->origin, 1.0f );
Matrix4x4_VectorITransform( matrix, p, test );
}
else
{
// offset the test point appropriately for this hull.
VectorSubtract( p, pe->origin, test );
}
// test hull for intersection with this model
if( PM_HullPointContents( hull, hull->firstclipnode, test ) == CONTENTS_EMPTY )
continue;
// compare contents ranking
if( RankForContents( pe->skin ) > RankForContents( contents ))
contents = pe->skin; // new content has more priority
}
return contents;
}