This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/render/r_fragment.c

319 lines
8.4 KiB
C

//=======================================================================
// Copyright XashXT Group 2008 ©
// r_fragment.c - fragment clipping
//=======================================================================
#include "r_local.h"
#include "mathlib.h"
#define MAX_FRAGMENT_VERTS 128
#define MAX_DECAL_FRAGMENTS 128
#define MAX_DECAL_VERTS 384
static int r_numFragmentVerts;
static int r_maxFragmentVerts;
static vec3_t *r_fragmentVerts;
static int r_numFragments;
static int r_maxFragments;
static decalFragment_t *r_fragments;
static cplane_t r_fragmentPlanes[6];
static int r_fragmentCount;
/*
=================
R_ClipFragment_r
=================
*/
static void R_ClipFragment_r( int numVerts, vec3_t verts, int stage, decalFragment_t *df )
{
int i, f;
float *v;
bool frontSide;
vec3_t front[MAX_FRAGMENT_VERTS];
float dists[MAX_FRAGMENT_VERTS];
int sides[MAX_FRAGMENT_VERTS];
cplane_t *plane;
float dist;
if( numVerts > MAX_FRAGMENT_VERTS - 2 )
Host_Error( "R_ClipFragment: MAX_FRAGMENT_VERTS limit exceeded\n" );
if( stage == 6 )
{
// fully clipped
if( numVerts > 2 )
{
if( r_numFragmentVerts + numVerts > r_maxFragmentVerts )
return;
df->firstVert = r_numFragmentVerts;
df->numVerts = numVerts;
for( i = 0, v = verts; i < numVerts; i++, v += 3 )
VectorCopy( v, r_fragmentVerts[r_numFragmentVerts+i] );
r_numFragmentVerts += numVerts;
}
return;
}
frontSide = false;
plane = &r_fragmentPlanes[stage];
for( i = 0, v = verts; i < numVerts; i++, v += 3 )
{
if( plane->type < 3 )
dists[i] = dist = v[plane->type] - plane->dist;
else dists[i] = dist = DotProduct( v, plane->normal ) - plane->dist;
if( dist > ON_EPSILON )
{
frontSide = true;
sides[i] = SIDE_FRONT;
}
else if( dist < -ON_EPSILON )
sides[i] = SIDE_BACK;
else sides[i] = SIDE_ON;
}
if( !frontSide ) return; // not clipped
// clip it
dists[i] = dists[0];
sides[i] = sides[0];
VectorCopy( verts, (verts + (i * 3)));
for( i = f = 0, v = verts; i < numVerts; i++, v += 3 )
{
switch( sides[i] )
{
case SIDE_FRONT:
VectorCopy( v, front[f] );
f++;
break;
case SIDE_BACK:
break;
case SIDE_ON:
VectorCopy(v, front[f]);
f++;
break;
}
if( sides[i] == SIDE_ON || sides[i+1] == SIDE_ON || sides[i+1] == sides[i] )
continue;
dist = dists[i] / (dists[i] - dists[i+1]);
front[f][0] = v[0] + (v[3] - v[0]) * dist;
front[f][1] = v[1] + (v[4] - v[1]) * dist;
front[f][2] = v[2] + (v[5] - v[2]) * dist;
f++;
}
// continue
R_ClipFragment_r( f, front[0], stage+1, df );
}
/*
=================
R_ClipFragmentToSurface
=================
*/
static void R_ClipFragmentToSurface( surface_t *surf )
{
decalFragment_t *df;
surfPoly_t *p;
vec3_t verts[MAX_FRAGMENT_VERTS];
int i;
// copy vertex data and clip to each triangle
for( p = surf->poly; p; p = p->next )
{
for( i = 0; i < p->numIndices; i += 3 )
{
df = &r_fragments[r_numFragments];
df->firstVert = df->numVerts = 0;
VectorCopy( p->vertices[p->indices[i+0]].xyz, verts[0] );
VectorCopy( p->vertices[p->indices[i+1]].xyz, verts[1] );
VectorCopy( p->vertices[p->indices[i+2]].xyz, verts[2] );
R_ClipFragment_r( 3, verts[0], 0, df );
if( df->numVerts )
{
r_numFragments++;
if( r_numFragmentVerts == r_maxFragmentVerts || r_numFragments == r_maxFragments )
return; // already reached the limit
}
}
}
}
/*
=================
R_RecursiveFragmentNode
=================
*/
static void R_RecursiveFragmentNode( node_t *node, const vec3_t origin, const vec3_t normal, float radius )
{
int i;
float dist;
cplane_t *plane;
surface_t *surf;
if( node->contents != CONTENTS_NODE )
return;
if( r_numFragmentVerts == r_maxFragmentVerts || r_numFragments == r_maxFragments )
return; // already reached the limit somewhere else
// find which side of the node we are on
plane = node->plane;
if( plane->type < 3 )
dist = origin[plane->type] - plane->dist;
else dist = DotProduct( origin, plane->normal ) - plane->dist;
// go down the appropriate sides
if( dist > radius )
{
R_RecursiveFragmentNode( node->children[0], origin, normal, radius );
return;
}
if( dist < -radius )
{
R_RecursiveFragmentNode( node->children[1], origin, normal, radius );
return;
}
// clip to each surface
surf = r_worldModel->surfaces + node->firstSurface;
for( i = 0; i < node->numSurfaces; i++, surf++ )
{
if( r_numFragmentVerts == r_maxFragmentVerts || r_numFragments == r_maxFragments )
break; // already reached the limit
if( surf->fragmentFrame == r_fragmentCount )
continue; // already checked this surface in another node
surf->fragmentFrame = r_fragmentCount;
if( surf->texInfo->surfaceFlags & (SURF_SKY|SURF_3DSKY|SURF_NODRAW|SURF_WARP))
continue; // don't bother clipping
if( surf->texInfo->shader->flags & SHADER_NOFRAGMENTS )
continue; // don't bother clipping
if( !BoundsAndSphereIntersect( surf->mins, surf->maxs, origin, radius ))
continue; // no intersection
if(!( surf->flags & SURF_PLANEBACK ))
{
if( DotProduct( normal, surf->plane->normal ) < 0.5f )
continue; // greater than 60 degrees
}
else
{
if( DotProduct( normal, surf->plane->normal ) > -0.5f)
continue; // greater than 60 degrees
}
// clip to the surface
R_ClipFragmentToSurface( surf );
}
// recurse down the children
R_RecursiveFragmentNode( node->children[0], origin, normal, radius );
R_RecursiveFragmentNode( node->children[1], origin, normal, radius );
}
/*
=================
R_DecalFragments
=================
*/
int R_DecalFragments( const vec3_t origin, const matrix3x3 matrix, float radius, int maxVerts, vec3_t *verts, int maxFragments, decalFragment_t *fragments )
{
int i;
float dot;
if( !r_worldModel ) return 0; // map not loaded
r_fragmentCount++; // for multi-check avoidance
// initialize fragments
r_numFragmentVerts = 0;
r_maxFragmentVerts = maxVerts;
r_fragmentVerts = verts;
r_numFragments = 0;
r_maxFragments = maxFragments;
r_fragments = fragments;
// calculate clipping planes
for( i = 0; i < 3; i++ )
{
dot = DotProduct( origin, matrix[i] );
VectorCopy( matrix[i], r_fragmentPlanes[i*2+0].normal );
r_fragmentPlanes[i*2+0].dist = dot - radius;
PlaneClassify( &r_fragmentPlanes[i*2+0] );
VectorNegate( matrix[i], r_fragmentPlanes[i*2+1].normal );
r_fragmentPlanes[i*2+1].dist = -dot - radius;
PlaneClassify( &r_fragmentPlanes[i*2+1] );
}
// clip against world geometry
R_RecursiveFragmentNode( r_worldModel->nodes, origin, matrix[0], radius );
return r_numFragments;
}
/*
=================
R_ImpactMark
temporary marks will be inmediately passed to the renderer
=================
*/
void R_ImpactMark( vec3_t org, vec3_t dir, float rot, float radius, rgba_t rgba, bool fade, shader_t shader, bool temp )
{
int i, j;
vec3_t delta;
matrix3x3 matrix;
int numFragments;
decalFragment_t decalFragments[MAX_DECAL_FRAGMENTS], *df;
vec3_t decalVerts[MAX_DECAL_VERTS];
polyVert_t verts[MAX_VERTS_ON_POLY];
// find orientation vectors
VectorNormalize2( dir, matrix[0] );
PerpendicularVector( matrix[1], matrix[0] );
RotatePointAroundVector( matrix[2], matrix[0], matrix[1], rot );
CrossProduct( matrix[0], matrix[2], matrix[1] );
// get the clipped decal fragments
numFragments = R_DecalFragments( org, matrix, radius, MAX_DECAL_VERTS, decalVerts, MAX_DECAL_FRAGMENTS, decalFragments );
if( !numFragments ) return;
VectorScale( matrix[1], 0.5 / radius, matrix[1] );
VectorScale( matrix[2], 0.5 / radius, matrix[2] );
for( i = 0, df = decalFragments; i < numFragments; i++, df++ )
{
if( !df->numVerts ) continue;
if( df->numVerts > MAX_VERTS_ON_POLY )
df->numVerts = MAX_VERTS_ON_POLY;
// if it is a temporary decal, pass it to the renderer without storing
if( temp )
{
for( j = 0; j < df->numVerts; j++ )
{
VectorCopy( decalVerts[df->firstVert + j], verts[j].point );
VectorSubtract( verts[j].point, org, delta );
verts[j].st[0] = 0.5 + DotProduct( delta, matrix[1] );
verts[j].st[1] = 0.5 + DotProduct( delta, matrix[2] );
Vector4Copy( rgba, verts[j].modulate );
}
R_AddPolyToScene( shader, df->numVerts, verts );
continue;
}
ri.AddDecal( org, matrix, shader, rgba, fade, df, decalVerts );
}
}