270 lines
6.1 KiB
270 lines
6.1 KiB
/* |
|
pm_surface.c - surface tracing |
|
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 "pm_local.h" |
|
|
|
typedef struct |
|
{ |
|
float fraction; |
|
int contents; |
|
} linetrace_t; |
|
|
|
/* |
|
================== |
|
PM_RecursiveSurfCheck |
|
|
|
================== |
|
*/ |
|
msurface_t *PM_RecursiveSurfCheck( model_t *model, mnode_t *node, vec3_t p1, vec3_t p2 ) |
|
{ |
|
float t1, t2, frac; |
|
int side, ds, dt; |
|
mplane_t *plane; |
|
msurface_t *surf; |
|
vec3_t mid; |
|
int i; |
|
|
|
if( node->contents < 0 ) |
|
return NULL; |
|
|
|
plane = node->plane; |
|
|
|
if( plane->type < 3 ) |
|
{ |
|
t1 = p1[plane->type] - plane->dist; |
|
t2 = p2[plane->type] - plane->dist; |
|
} |
|
else |
|
{ |
|
t1 = DotProduct( plane->normal, p1 ) - plane->dist; |
|
t2 = DotProduct( plane->normal, p2 ) - plane->dist; |
|
} |
|
|
|
if( t1 >= 0.0f && t2 >= 0.0f ) |
|
return PM_RecursiveSurfCheck( model, node->children[0], p1, p2 ); |
|
if( t1 < 0.0f && t2 < 0.0f ) |
|
return PM_RecursiveSurfCheck( model, node->children[1], p1, p2 ); |
|
|
|
frac = t1 / ( t1 - t2 ); |
|
|
|
if( frac < 0.0f ) frac = 0.0f; |
|
if( frac > 1.0f ) frac = 1.0f; |
|
|
|
VectorLerp( p1, frac, p2, mid ); |
|
|
|
side = (t1 < 0.0f); |
|
|
|
// now this is weird. |
|
surf = PM_RecursiveSurfCheck( model, node->children[side], p1, mid ); |
|
|
|
if( surf != NULL || ( t1 >= 0.0f && t2 >= 0.0f ) || ( t1 < 0.0f && t2 < 0.0f )) |
|
{ |
|
return surf; |
|
} |
|
|
|
surf = model->surfaces + node->firstsurface; |
|
|
|
for( i = 0; i < node->numsurfaces; i++, surf++ ) |
|
{ |
|
ds = (int)((float)DotProduct( mid, surf->texinfo->vecs[0] ) + surf->texinfo->vecs[0][3] ); |
|
dt = (int)((float)DotProduct( mid, surf->texinfo->vecs[1] ) + surf->texinfo->vecs[1][3] ); |
|
|
|
if( ds >= surf->texturemins[0] && dt >= surf->texturemins[1] ) |
|
{ |
|
int s = ds - surf->texturemins[0]; |
|
int t = dt - surf->texturemins[1]; |
|
|
|
if( s <= surf->extents[0] && t <= surf->extents[1] ) |
|
return surf; |
|
} |
|
} |
|
|
|
return PM_RecursiveSurfCheck( model, node->children[side^1], mid, p2 ); |
|
} |
|
|
|
/* |
|
================== |
|
PM_TraceTexture |
|
|
|
find the face where the traceline hit |
|
assume physentity is valid |
|
================== |
|
*/ |
|
msurface_t *PM_TraceSurface( physent_t *pe, vec3_t start, vec3_t end ) |
|
{ |
|
matrix4x4 matrix; |
|
model_t *bmodel; |
|
hull_t *hull; |
|
vec3_t start_l, end_l; |
|
vec3_t offset; |
|
|
|
bmodel = pe->model; |
|
|
|
if( !bmodel || bmodel->type != mod_brush ) |
|
return NULL; |
|
|
|
hull = &pe->model->hulls[0]; |
|
VectorSubtract( hull->clip_mins, vec3_origin, offset ); |
|
VectorAdd( offset, pe->origin, offset ); |
|
|
|
VectorSubtract( start, offset, start_l ); |
|
VectorSubtract( end, offset, end_l ); |
|
|
|
// rotate start and end into the models frame of reference |
|
if( !VectorIsNull( pe->angles )) |
|
{ |
|
Matrix4x4_CreateFromEntity( matrix, pe->angles, offset, 1.0f ); |
|
Matrix4x4_VectorITransform( matrix, start, start_l ); |
|
Matrix4x4_VectorITransform( matrix, end, end_l ); |
|
} |
|
|
|
return PM_RecursiveSurfCheck( bmodel, &bmodel->nodes[hull->firstclipnode], start_l, end_l ); |
|
} |
|
|
|
/* |
|
================== |
|
PM_TraceTexture |
|
|
|
find the face where the traceline hit |
|
assume physentity is valid |
|
================== |
|
*/ |
|
const char *PM_TraceTexture( physent_t *pe, vec3_t start, vec3_t end ) |
|
{ |
|
msurface_t *surf = PM_TraceSurface( pe, start, end ); |
|
|
|
if( !surf || !surf->texinfo || !surf->texinfo->texture ) |
|
return NULL; |
|
|
|
return surf->texinfo->texture->name; |
|
} |
|
|
|
/* |
|
================== |
|
PM_TestLine_r |
|
|
|
optimized trace for light gathering |
|
================== |
|
*/ |
|
int PM_TestLine_r( mnode_t *node, vec_t p1f, vec_t p2f, const vec3_t start, const vec3_t stop, linetrace_t *trace ) |
|
{ |
|
float front, back; |
|
float frac, midf; |
|
int r, side; |
|
vec3_t mid; |
|
loc0: |
|
if( node->contents < 0 ) |
|
trace->contents = node->contents; |
|
if( node->contents == CONTENTS_SOLID ) |
|
return CONTENTS_SOLID; |
|
if( node->contents == CONTENTS_SKY ) |
|
return CONTENTS_SKY; |
|
if( node->contents < 0 ) |
|
return CONTENTS_EMPTY; |
|
|
|
front = PlaneDiff( start, node->plane ); |
|
back = PlaneDiff( stop, node->plane ); |
|
|
|
if( front >= -ON_EPSILON && back >= -ON_EPSILON ) |
|
{ |
|
node = node->children[0]; |
|
goto loc0; |
|
} |
|
|
|
if( front < ON_EPSILON && back < ON_EPSILON ) |
|
{ |
|
node = node->children[1]; |
|
goto loc0; |
|
} |
|
|
|
side = (front < 0); |
|
frac = front / (front - back); |
|
frac = bound( 0.0, frac, 1.0 ); |
|
|
|
VectorLerp( start, frac, stop, mid ); |
|
midf = p1f + ( p2f - p1f ) * frac; |
|
|
|
r = PM_TestLine_r( node->children[side], p1f, midf, start, mid, trace ); |
|
|
|
if( r != CONTENTS_EMPTY ) |
|
{ |
|
trace->fraction = midf; |
|
return r; |
|
} |
|
|
|
return PM_TestLine_r( node->children[!side], midf, p2f, mid, stop, trace ); |
|
} |
|
|
|
int PM_TestLineExt( playermove_t *pmove, physent_t *ents, int numents, const vec3_t start, const vec3_t end, int flags ) |
|
{ |
|
linetrace_t trace, trace_bbox; |
|
matrix4x4 matrix; |
|
hull_t *hull = NULL; |
|
vec3_t offset, start_l, end_l; |
|
qboolean rotated; |
|
physent_t *pe; |
|
int i; |
|
|
|
trace.contents = CONTENTS_EMPTY; |
|
trace.fraction = 1.0f; |
|
|
|
for( i = 0; i < numents; i++ ) |
|
{ |
|
pe = &ents[i]; |
|
|
|
if( i != 0 && ( flags & PM_WORLD_ONLY )) |
|
break; |
|
|
|
if( !pe->model || pe->model->type != mod_brush || pe->solid != SOLID_BSP ) |
|
continue; |
|
|
|
if( pe->rendermode != kRenderNormal ) |
|
continue; |
|
|
|
hull = &pe->model->hulls[0]; |
|
|
|
hull = PM_HullForBsp( pe, pmove, offset ); |
|
|
|
if( pe->solid == SOLID_BSP && !VectorIsNull( pe->angles )) |
|
rotated = true; |
|
else rotated = false; |
|
|
|
if( rotated ) |
|
{ |
|
Matrix4x4_CreateFromEntity( matrix, pe->angles, offset, 1.0f ); |
|
Matrix4x4_VectorITransform( matrix, start, start_l ); |
|
Matrix4x4_VectorITransform( matrix, end, end_l ); |
|
} |
|
else |
|
{ |
|
VectorSubtract( start, pe->origin, start_l ); |
|
VectorSubtract( end, pe->origin, end_l ); |
|
} |
|
|
|
trace_bbox.contents = CONTENTS_EMPTY; |
|
trace_bbox.fraction = 1.0f; |
|
|
|
PM_TestLine_r( &pe->model->nodes[hull->firstclipnode], 0.0f, 1.0f, start_l, end_l, &trace_bbox ); |
|
|
|
if( trace_bbox.contents != CONTENTS_EMPTY || trace_bbox.fraction < trace.fraction ) |
|
{ |
|
trace = trace_bbox; |
|
} |
|
} |
|
|
|
return trace.contents; |
|
} |