mirror of
https://github.com/FWGS/xash3d-fwgs
synced 2024-12-26 02:36:08 +01:00
390 lines
8.6 KiB
C
390 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 "mathlib.h"
|
|
#include "pm_local.h"
|
|
#include "ref_common.h"
|
|
|
|
#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( !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;
|
|
}
|
|
|
|
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.0, frac, 1.0 );
|
|
|
|
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;
|
|
}
|