mirror of
https://github.com/FWGS/xash3d-fwgs
synced 2024-11-25 11:19:59 +01:00
5e0a0765ce
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:]]\+$//' {} \+ ```
393 lines
8.6 KiB
C
393 lines
8.6 KiB
C
/*
|
|
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 "xash3d_mathlib.h"
|
|
#include "pm_local.h"
|
|
#include "ref_common.h"
|
|
|
|
#undef FRAC_EPSILON
|
|
#define FRAC_EPSILON (1.0f / 32.0f)
|
|
|
|
typedef struct
|
|
{
|
|
float fraction;
|
|
int contents;
|
|
msurface_t *surface;
|
|
} linetrace_t;
|
|
|
|
/*
|
|
==============
|
|
fix_coord
|
|
|
|
converts the reletive tex coords to absolute
|
|
==============
|
|
*/
|
|
static uint fix_coord( vec_t in, uint width )
|
|
{
|
|
if( in > 0 ) return (uint)in % width;
|
|
return width - ((uint)fabs( in ) % width);
|
|
}
|
|
|
|
/*
|
|
=============
|
|
SampleMiptex
|
|
|
|
fence texture testing
|
|
=============
|
|
*/
|
|
int PM_SampleMiptex( const msurface_t *surf, const vec3_t point )
|
|
{
|
|
mextrasurf_t *info = surf->info;
|
|
mfacebevel_t *fb = info->bevel;
|
|
int contents;
|
|
vec_t ds, dt;
|
|
int x, y;
|
|
mtexinfo_t *tx;
|
|
texture_t *mt;
|
|
|
|
// fill the default contents
|
|
if( fb ) contents = fb->contents;
|
|
else contents = CONTENTS_SOLID;
|
|
|
|
if( !surf->texinfo || !surf->texinfo->texture )
|
|
return contents;
|
|
|
|
tx = surf->texinfo;
|
|
mt = tx->texture;
|
|
|
|
if( mt->name[0] != '{' )
|
|
return contents;
|
|
|
|
// TODO: this won't work under dedicated
|
|
// should we bring up imagelib and keep original buffers?
|
|
#if !XASH_DEDICATED
|
|
if( !Host_IsDedicated() )
|
|
{
|
|
const byte *data;
|
|
|
|
data = ref.dllFuncs.R_GetTextureOriginalBuffer( mt->gl_texturenum );
|
|
|
|
if( !data ) return contents; // original doesn't kept
|
|
|
|
ds = DotProduct( point, tx->vecs[0] ) + tx->vecs[0][3];
|
|
dt = DotProduct( point, tx->vecs[1] ) + tx->vecs[1][3];
|
|
|
|
// convert ST to real pixels position
|
|
x = fix_coord( ds, mt->width - 1 );
|
|
y = fix_coord( dt, mt->height - 1 );
|
|
|
|
ASSERT( x >= 0 && y >= 0 );
|
|
|
|
if( data[(mt->width * y) + x] == 255 )
|
|
return CONTENTS_EMPTY;
|
|
return CONTENTS_SOLID;
|
|
}
|
|
#endif // !XASH_DEDICATED
|
|
|
|
return contents;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
PM_RecursiveSurfCheck
|
|
|
|
==================
|
|
*/
|
|
msurface_t *PM_RecursiveSurfCheck( model_t *mod, mnode_t *node, vec3_t p1, vec3_t p2 )
|
|
{
|
|
float t1, t2, frac;
|
|
int i, side;
|
|
msurface_t *surf;
|
|
vec3_t mid;
|
|
loc0:
|
|
if( node->contents < 0 )
|
|
return NULL;
|
|
|
|
t1 = PlaneDiff( p1, node->plane );
|
|
t2 = PlaneDiff( p2, node->plane );
|
|
|
|
if( t1 >= -FRAC_EPSILON && t2 >= -FRAC_EPSILON )
|
|
{
|
|
node = node->children[0];
|
|
goto loc0;
|
|
}
|
|
|
|
if( t1 < FRAC_EPSILON && t2 < FRAC_EPSILON )
|
|
{
|
|
node = node->children[1];
|
|
goto loc0;
|
|
}
|
|
|
|
side = (t1 < 0.0f);
|
|
frac = t1 / ( t1 - t2 );
|
|
frac = bound( 0.0f, frac, 1.0f );
|
|
|
|
VectorLerp( p1, frac, p2, mid );
|
|
|
|
if(( surf = PM_RecursiveSurfCheck( mod, node->children[side], p1, mid )) != NULL )
|
|
return surf;
|
|
|
|
// walk through real faces
|
|
for( i = 0; i < node->numsurfaces; i++ )
|
|
{
|
|
msurface_t *surf = &mod->surfaces[node->firstsurface + i];
|
|
mextrasurf_t *info = surf->info;
|
|
mfacebevel_t *fb = info->bevel;
|
|
int j, contents;
|
|
vec3_t delta;
|
|
|
|
if( !fb ) continue; // ???
|
|
|
|
VectorSubtract( mid, fb->origin, delta );
|
|
if( DotProduct( delta, delta ) >= fb->radius )
|
|
continue; // no intersection
|
|
|
|
for( j = 0; j < fb->numedges; j++ )
|
|
{
|
|
if( PlaneDiff( mid, &fb->edges[j] ) > FRAC_EPSILON )
|
|
break; // outside the bounds
|
|
}
|
|
|
|
if( j != fb->numedges )
|
|
continue; // we are outside the bounds of the facet
|
|
|
|
// hit the surface
|
|
contents = PM_SampleMiptex( surf, mid );
|
|
|
|
if( contents != CONTENTS_EMPTY )
|
|
return surf;
|
|
return NULL; // through the fence
|
|
}
|
|
|
|
return PM_RecursiveSurfCheck( mod, 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( model_t *mod, 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 i, r, side;
|
|
vec3_t mid;
|
|
loc0:
|
|
if( node->contents < 0 )
|
|
{
|
|
// water, slime or lava interpret as empty
|
|
if( node->contents == CONTENTS_SOLID )
|
|
return CONTENTS_SOLID;
|
|
if( node->contents == CONTENTS_SKY )
|
|
return CONTENTS_SKY;
|
|
trace->fraction = 1.0f;
|
|
return CONTENTS_EMPTY;
|
|
}
|
|
|
|
front = PlaneDiff( start, node->plane );
|
|
back = PlaneDiff( stop, node->plane );
|
|
|
|
if( front >= -FRAC_EPSILON && back >= -FRAC_EPSILON )
|
|
{
|
|
node = node->children[0];
|
|
goto loc0;
|
|
}
|
|
|
|
if( front < FRAC_EPSILON && back < FRAC_EPSILON )
|
|
{
|
|
node = node->children[1];
|
|
goto loc0;
|
|
}
|
|
|
|
side = (front < 0);
|
|
frac = front / (front - back);
|
|
frac = bound( 0.0f, frac, 1.0f );
|
|
|
|
VectorLerp( start, frac, stop, mid );
|
|
midf = p1f + ( p2f - p1f ) * frac;
|
|
|
|
r = PM_TestLine_r( mod, node->children[side], p1f, midf, start, mid, trace );
|
|
|
|
if( r != CONTENTS_EMPTY )
|
|
{
|
|
if( trace->surface == NULL )
|
|
trace->fraction = midf;
|
|
trace->contents = r;
|
|
return r;
|
|
}
|
|
|
|
// walk through real faces
|
|
for( i = 0; i < node->numsurfaces; i++ )
|
|
{
|
|
msurface_t *surf = &mod->surfaces[node->firstsurface + i];
|
|
mextrasurf_t *info = surf->info;
|
|
mfacebevel_t *fb = info->bevel;
|
|
int j, contents;
|
|
vec3_t delta;
|
|
|
|
if( !fb ) continue;
|
|
|
|
VectorSubtract( mid, fb->origin, delta );
|
|
if( DotProduct( delta, delta ) >= fb->radius )
|
|
continue; // no intersection
|
|
|
|
for( j = 0; j < fb->numedges; j++ )
|
|
{
|
|
if( PlaneDiff( mid, &fb->edges[j] ) > FRAC_EPSILON )
|
|
break; // outside the bounds
|
|
}
|
|
|
|
if( j != fb->numedges )
|
|
continue; // we are outside the bounds of the facet
|
|
|
|
// hit the surface
|
|
contents = PM_SampleMiptex( surf, mid );
|
|
|
|
// fill the trace and out
|
|
trace->contents = contents;
|
|
trace->fraction = midf;
|
|
|
|
if( contents != CONTENTS_EMPTY )
|
|
trace->surface = surf;
|
|
|
|
return contents;
|
|
}
|
|
|
|
return PM_TestLine_r( mod, 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;
|
|
trace.surface = NULL;
|
|
|
|
for( i = 0; i < numents; i++ )
|
|
{
|
|
pe = &ents[i];
|
|
|
|
if( i != 0 && FBitSet( flags, PM_WORLD_ONLY ))
|
|
break;
|
|
|
|
if( !pe->model || pe->model->type != mod_brush || pe->solid != SOLID_BSP )
|
|
continue;
|
|
|
|
if( FBitSet( flags, PM_GLASS_IGNORE ) && 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;
|
|
trace_bbox.surface = NULL;
|
|
|
|
PM_TestLine_r( pe->model, &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;
|
|
}
|