mirror of https://github.com/FWGS/xash3d-fwgs
653 lines
16 KiB
C
653 lines
16 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 "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 );
|
|
MsgDev( D_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 );
|
|
else CL_ClipPMoveToEntity( pe, start, mins, maxs, end, &trace_bbox );
|
|
}
|
|
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 );
|
|
else CL_ClipPMoveToEntity( pe, pos, mins, maxs, pos, &trace );
|
|
|
|
// 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
|
|
} |