📄 cm_patch.c
字号:
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code 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 2 of the License,
or (at your option) any later version.
Quake III Arena source code 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.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "cm_local.h"
#include "cm_patch.h"
/*
This file does not reference any globals, and has these entry points:
void CM_ClearLevelPatches( void );
struct patchCollide_s *CM_GeneratePatchCollide( int width, int height, const vec3_t *points );
void CM_TraceThroughPatchCollide( traceWork_t *tw, const struct patchCollide_s *pc );
qboolean CM_PositionTestInPatchCollide( traceWork_t *tw, const struct patchCollide_s *pc );
void CM_DrawDebugSurface( void (*drawPoly)(int color, int numPoints, flaot *points) );
WARNING: this may misbehave with meshes that have rows or columns that only
degenerate a few triangles. Completely degenerate rows and columns are handled
properly.
*/
/*
#define MAX_FACETS 1024
#define MAX_PATCH_PLANES 2048
typedef struct {
float plane[4];
int signbits; // signx + (signy<<1) + (signz<<2), used as lookup during collision
} patchPlane_t;
typedef struct {
int surfacePlane;
int numBorders; // 3 or four + 6 axial bevels + 4 or 3 * 4 edge bevels
int borderPlanes[4+6+16];
int borderInward[4+6+16];
qboolean borderNoAdjust[4+6+16];
} facet_t;
typedef struct patchCollide_s {
vec3_t bounds[2];
int numPlanes; // surface planes plus edge planes
patchPlane_t *planes;
int numFacets;
facet_t *facets;
} patchCollide_t;
#define MAX_GRID_SIZE 129
typedef struct {
int width;
int height;
qboolean wrapWidth;
qboolean wrapHeight;
vec3_t points[MAX_GRID_SIZE][MAX_GRID_SIZE]; // [width][height]
} cGrid_t;
#define SUBDIVIDE_DISTANCE 16 //4 // never more than this units away from curve
#define PLANE_TRI_EPSILON 0.1
#define WRAP_POINT_EPSILON 0.1
*/
int c_totalPatchBlocks;
int c_totalPatchSurfaces;
int c_totalPatchEdges;
static const patchCollide_t *debugPatchCollide;
static const facet_t *debugFacet;
static qboolean debugBlock;
static vec3_t debugBlockPoints[4];
/*
=================
CM_ClearLevelPatches
=================
*/
void CM_ClearLevelPatches( void ) {
debugPatchCollide = NULL;
debugFacet = NULL;
}
/*
=================
CM_SignbitsForNormal
=================
*/
static int CM_SignbitsForNormal( vec3_t normal ) {
int bits, j;
bits = 0;
for (j=0 ; j<3 ; j++) {
if ( normal[j] < 0 ) {
bits |= 1<<j;
}
}
return bits;
}
/*
=====================
CM_PlaneFromPoints
Returns false if the triangle is degenrate.
The normal will point out of the clock for clockwise ordered points
=====================
*/
static qboolean CM_PlaneFromPoints( vec4_t plane, vec3_t a, vec3_t b, vec3_t c ) {
vec3_t d1, d2;
VectorSubtract( b, a, d1 );
VectorSubtract( c, a, d2 );
CrossProduct( d2, d1, plane );
if ( VectorNormalize( plane ) == 0 ) {
return qfalse;
}
plane[3] = DotProduct( a, plane );
return qtrue;
}
/*
================================================================================
GRID SUBDIVISION
================================================================================
*/
/*
=================
CM_NeedsSubdivision
Returns true if the given quadratic curve is not flat enough for our
collision detection purposes
=================
*/
static qboolean CM_NeedsSubdivision( vec3_t a, vec3_t b, vec3_t c ) {
vec3_t cmid;
vec3_t lmid;
vec3_t delta;
float dist;
int i;
// calculate the linear midpoint
for ( i = 0 ; i < 3 ; i++ ) {
lmid[i] = 0.5*(a[i] + c[i]);
}
// calculate the exact curve midpoint
for ( i = 0 ; i < 3 ; i++ ) {
cmid[i] = 0.5 * ( 0.5*(a[i] + b[i]) + 0.5*(b[i] + c[i]) );
}
// see if the curve is far enough away from the linear mid
VectorSubtract( cmid, lmid, delta );
dist = VectorLength( delta );
return dist >= SUBDIVIDE_DISTANCE;
}
/*
===============
CM_Subdivide
a, b, and c are control points.
the subdivided sequence will be: a, out1, out2, out3, c
===============
*/
static void CM_Subdivide( vec3_t a, vec3_t b, vec3_t c, vec3_t out1, vec3_t out2, vec3_t out3 ) {
int i;
for ( i = 0 ; i < 3 ; i++ ) {
out1[i] = 0.5 * (a[i] + b[i]);
out3[i] = 0.5 * (b[i] + c[i]);
out2[i] = 0.5 * (out1[i] + out3[i]);
}
}
/*
=================
CM_TransposeGrid
Swaps the rows and columns in place
=================
*/
static void CM_TransposeGrid( cGrid_t *grid ) {
int i, j, l;
vec3_t temp;
qboolean tempWrap;
if ( grid->width > grid->height ) {
for ( i = 0 ; i < grid->height ; i++ ) {
for ( j = i + 1 ; j < grid->width ; j++ ) {
if ( j < grid->height ) {
// swap the value
VectorCopy( grid->points[i][j], temp );
VectorCopy( grid->points[j][i], grid->points[i][j] );
VectorCopy( temp, grid->points[j][i] );
} else {
// just copy
VectorCopy( grid->points[j][i], grid->points[i][j] );
}
}
}
} else {
for ( i = 0 ; i < grid->width ; i++ ) {
for ( j = i + 1 ; j < grid->height ; j++ ) {
if ( j < grid->width ) {
// swap the value
VectorCopy( grid->points[j][i], temp );
VectorCopy( grid->points[i][j], grid->points[j][i] );
VectorCopy( temp, grid->points[i][j] );
} else {
// just copy
VectorCopy( grid->points[i][j], grid->points[j][i] );
}
}
}
}
l = grid->width;
grid->width = grid->height;
grid->height = l;
tempWrap = grid->wrapWidth;
grid->wrapWidth = grid->wrapHeight;
grid->wrapHeight = tempWrap;
}
/*
===================
CM_SetGridWrapWidth
If the left and right columns are exactly equal, set grid->wrapWidth qtrue
===================
*/
static void CM_SetGridWrapWidth( cGrid_t *grid ) {
int i, j;
float d;
for ( i = 0 ; i < grid->height ; i++ ) {
for ( j = 0 ; j < 3 ; j++ ) {
d = grid->points[0][i][j] - grid->points[grid->width-1][i][j];
if ( d < -WRAP_POINT_EPSILON || d > WRAP_POINT_EPSILON ) {
break;
}
}
if ( j != 3 ) {
break;
}
}
if ( i == grid->height ) {
grid->wrapWidth = qtrue;
} else {
grid->wrapWidth = qfalse;
}
}
/*
=================
CM_SubdivideGridColumns
Adds columns as necessary to the grid until
all the aproximating points are within SUBDIVIDE_DISTANCE
from the true curve
=================
*/
static void CM_SubdivideGridColumns( cGrid_t *grid ) {
int i, j, k;
for ( i = 0 ; i < grid->width - 2 ; ) {
// grid->points[i][x] is an interpolating control point
// grid->points[i+1][x] is an aproximating control point
// grid->points[i+2][x] is an interpolating control point
//
// first see if we can collapse the aproximating collumn away
//
for ( j = 0 ; j < grid->height ; j++ ) {
if ( CM_NeedsSubdivision( grid->points[i][j], grid->points[i+1][j], grid->points[i+2][j] ) ) {
break;
}
}
if ( j == grid->height ) {
// all of the points were close enough to the linear midpoints
// that we can collapse the entire column away
for ( j = 0 ; j < grid->height ; j++ ) {
// remove the column
for ( k = i + 2 ; k < grid->width ; k++ ) {
VectorCopy( grid->points[k][j], grid->points[k-1][j] );
}
}
grid->width--;
// go to the next curve segment
i++;
continue;
}
//
// we need to subdivide the curve
//
for ( j = 0 ; j < grid->height ; j++ ) {
vec3_t prev, mid, next;
// save the control points now
VectorCopy( grid->points[i][j], prev );
VectorCopy( grid->points[i+1][j], mid );
VectorCopy( grid->points[i+2][j], next );
// make room for two additional columns in the grid
// columns i+1 will be replaced, column i+2 will become i+4
// i+1, i+2, and i+3 will be generated
for ( k = grid->width - 1 ; k > i + 1 ; k-- ) {
VectorCopy( grid->points[k][j], grid->points[k+2][j] );
}
// generate the subdivided points
CM_Subdivide( prev, mid, next, grid->points[i+1][j], grid->points[i+2][j], grid->points[i+3][j] );
}
grid->width += 2;
// the new aproximating point at i+1 may need to be removed
// or subdivided farther, so don't advance i
}
}
/*
======================
CM_ComparePoints
======================
*/
#define POINT_EPSILON 0.1
static qboolean CM_ComparePoints( float *a, float *b ) {
float d;
d = a[0] - b[0];
if ( d < -POINT_EPSILON || d > POINT_EPSILON ) {
return qfalse;
}
d = a[1] - b[1];
if ( d < -POINT_EPSILON || d > POINT_EPSILON ) {
return qfalse;
}
d = a[2] - b[2];
if ( d < -POINT_EPSILON || d > POINT_EPSILON ) {
return qfalse;
}
return qtrue;
}
/*
=================
CM_RemoveDegenerateColumns
If there are any identical columns, remove them
=================
*/
static void CM_RemoveDegenerateColumns( cGrid_t *grid ) {
int i, j, k;
for ( i = 0 ; i < grid->width - 1 ; i++ ) {
for ( j = 0 ; j < grid->height ; j++ ) {
if ( !CM_ComparePoints( grid->points[i][j], grid->points[i+1][j] ) ) {
break;
}
}
if ( j != grid->height ) {
continue; // not degenerate
}
for ( j = 0 ; j < grid->height ; j++ ) {
// remove the column
for ( k = i + 2 ; k < grid->width ; k++ ) {
VectorCopy( grid->points[k][j], grid->points[k-1][j] );
}
}
grid->width--;
// check against the next column
i--;
}
}
/*
================================================================================
PATCH COLLIDE GENERATION
================================================================================
*/
static int numPlanes;
static patchPlane_t planes[MAX_PATCH_PLANES];
static int numFacets;
static facet_t facets[MAX_PATCH_PLANES]; //maybe MAX_FACETS ??
#define NORMAL_EPSILON 0.0001
#define DIST_EPSILON 0.02
/*
==================
CM_PlaneEqual
==================
*/
int CM_PlaneEqual(patchPlane_t *p, float plane[4], int *flipped) {
float invplane[4];
if (
fabs(p->plane[0] - plane[0]) < NORMAL_EPSILON
&& fabs(p->plane[1] - plane[1]) < NORMAL_EPSILON
&& fabs(p->plane[2] - plane[2]) < NORMAL_EPSILON
&& fabs(p->plane[3] - plane[3]) < DIST_EPSILON )
{
*flipped = qfalse;
return qtrue;
}
VectorNegate(plane, invplane);
invplane[3] = -plane[3];
if (
fabs(p->plane[0] - invplane[0]) < NORMAL_EPSILON
&& fabs(p->plane[1] - invplane[1]) < NORMAL_EPSILON
&& fabs(p->plane[2] - invplane[2]) < NORMAL_EPSILON
&& fabs(p->plane[3] - invplane[3]) < DIST_EPSILON )
{
*flipped = qtrue;
return qtrue;
}
return qfalse;
}
/*
==================
CM_SnapVector
==================
*/
void CM_SnapVector(vec3_t normal) {
int i;
for (i=0 ; i<3 ; i++)
{
if ( fabs(normal[i] - 1) < NORMAL_EPSILON )
{
VectorClear (normal);
normal[i] = 1;
break;
}
if ( fabs(normal[i] - -1) < NORMAL_EPSILON )
{
VectorClear (normal);
normal[i] = -1;
break;
}
}
}
/*
==================
CM_FindPlane2
==================
*/
int CM_FindPlane2(float plane[4], int *flipped) {
int i;
// see if the points are close enough to an existing plane
for ( i = 0 ; i < numPlanes ; i++ ) {
if (CM_PlaneEqual(&planes[i], plane, flipped)) return i;
}
// add a new plane
if ( numPlanes == MAX_PATCH_PLANES ) {
Com_Error( ERR_DROP, "MAX_PATCH_PLANES" );
}
Vector4Copy( plane, planes[numPlanes].plane );
planes[numPlanes].signbits = CM_SignbitsForNormal( plane );
numPlanes++;
*flipped = qfalse;
return numPlanes-1;
}
/*
==================
CM_FindPlane
==================
*/
static int CM_FindPlane( float *p1, float *p2, float *p3 ) {
float plane[4];
int i;
float d;
if ( !CM_PlaneFromPoints( plane, p1, p2, p3 ) ) {
return -1;
}
// see if the points are close enough to an existing plane
for ( i = 0 ; i < numPlanes ; i++ ) {
if ( DotProduct( plane, planes[i].plane ) < 0 ) {
continue; // allow backwards planes?
}
d = DotProduct( p1, planes[i].plane ) - planes[i].plane[3];
if ( d < -PLANE_TRI_EPSILON || d > PLANE_TRI_EPSILON ) {
continue;
}
d = DotProduct( p2, planes[i].plane ) - planes[i].plane[3];
if ( d < -PLANE_TRI_EPSILON || d > PLANE_TRI_EPSILON ) {
continue;
}
d = DotProduct( p3, planes[i].plane ) - planes[i].plane[3];
if ( d < -PLANE_TRI_EPSILON || d > PLANE_TRI_EPSILON ) {
continue;
}
// found it
return i;
}
// add a new plane
if ( numPlanes == MAX_PATCH_PLANES ) {
Com_Error( ERR_DROP, "MAX_PATCH_PLANES" );
}
Vector4Copy( plane, planes[numPlanes].plane );
planes[numPlanes].signbits = CM_SignbitsForNormal( plane );
numPlanes++;
return numPlanes-1;
}
/*
==================
CM_PointOnPlaneSide
==================
*/
static int CM_PointOnPlaneSide( float *p, int planeNum ) {
float *plane;
float d;
if ( planeNum == -1 ) {
return SIDE_ON;
}
plane = planes[ planeNum ].plane;
d = DotProduct( p, plane ) - plane[3];
if ( d > PLANE_TRI_EPSILON ) {
return SIDE_FRONT;
}
if ( d < -PLANE_TRI_EPSILON ) {
return SIDE_BACK;
}
return SIDE_ON;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -