2005-08-26 17:39:27 +00:00
|
|
|
/*
|
|
|
|
===========================================================================
|
|
|
|
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 "qbsp.h"
|
|
|
|
#include "l_mem.h"
|
|
|
|
#include "../botlib/aasfile.h"
|
|
|
|
#include "aas_store.h"
|
|
|
|
#include "aas_cfg.h"
|
|
|
|
#include "aas_file.h"
|
|
|
|
|
|
|
|
//
|
|
|
|
// creating tetrahedrons from a arbitrary world bounded by triangles
|
|
|
|
//
|
|
|
|
// a triangle has 3 corners and 3 edges
|
|
|
|
// a tetrahedron is build out of 4 triangles
|
|
|
|
// a tetrahedron has 6 edges
|
|
|
|
// we start with a world bounded by triangles, a side of a triangle facing
|
|
|
|
// towards the oudside of the world is marked as part of tetrahedron -1
|
|
|
|
//
|
|
|
|
// a tetrahedron is defined by two non-coplanar triangles with a shared edge
|
|
|
|
//
|
|
|
|
// a tetrahedron is defined by one triangle and a vertex not in the triangle plane
|
|
|
|
//
|
|
|
|
// if all triangles using a specific vertex have tetrahedrons
|
|
|
|
// at both sides then this vertex will never be part of a new tetrahedron
|
|
|
|
//
|
|
|
|
// if all triangles using a specific edge have tetrahedrons
|
|
|
|
// at both sides then this vertex will never be part of a new tetrahedron
|
|
|
|
//
|
|
|
|
// each triangle can only be shared by two tetrahedrons
|
|
|
|
// when all triangles have tetrahedrons at both sides then we're done
|
|
|
|
//
|
|
|
|
// if we cannot create any new tetrahedrons and there is at least one triangle
|
|
|
|
// which has a tetrahedron only at one side then the world leaks
|
|
|
|
//
|
|
|
|
|
|
|
|
#define Sign(x) (x < 0 ? 1 : 0)
|
|
|
|
|
|
|
|
#define MAX_TH_VERTEXES 128000
|
|
|
|
#define MAX_TH_PLANES 128000
|
|
|
|
#define MAX_TH_EDGES 512000
|
|
|
|
#define MAX_TH_TRIANGLES 51200
|
|
|
|
#define MAX_TH_TETRAHEDRONS 12800
|
|
|
|
|
|
|
|
#define PLANEHASH_SIZE 1024
|
|
|
|
#define EDGEHASH_SIZE 1024
|
|
|
|
#define TRIANGLEHASH_SIZE 1024
|
|
|
|
#define VERTEXHASH_SHIFT 7
|
|
|
|
#define VERTEXHASH_SIZE ((MAX_MAP_BOUNDS>>(VERTEXHASH_SHIFT-1))+1) //was 64
|
|
|
|
|
|
|
|
#define NORMAL_EPSILON 0.0001
|
|
|
|
#define DIST_EPSILON 0.1
|
|
|
|
#define VERTEX_EPSILON 0.01
|
|
|
|
#define INTEGRAL_EPSILON 0.01
|
|
|
|
|
|
|
|
|
|
|
|
//plane
|
|
|
|
typedef struct th_plane_s
|
|
|
|
{
|
|
|
|
vec3_t normal;
|
|
|
|
float dist;
|
|
|
|
int type;
|
|
|
|
int signbits;
|
|
|
|
struct th_plane_s *hashnext; //next plane in hash
|
|
|
|
} th_plane_t;
|
|
|
|
|
|
|
|
//vertex
|
|
|
|
typedef struct th_vertex_s
|
|
|
|
{
|
|
|
|
vec3_t v;
|
|
|
|
int usercount; //2x the number of to be processed
|
|
|
|
//triangles using this vertex
|
|
|
|
struct th_vertex_s *hashnext; //next vertex in hash
|
|
|
|
} th_vertex_t;
|
|
|
|
|
|
|
|
//edge
|
|
|
|
typedef struct th_edge_s
|
|
|
|
{
|
|
|
|
int v[2]; //vertex indexes
|
|
|
|
int usercount; //number of to be processed
|
|
|
|
//triangles using this edge
|
|
|
|
struct th_edge_s *hashnext; //next edge in hash
|
|
|
|
} th_edge_t;
|
|
|
|
|
|
|
|
//triangle
|
|
|
|
typedef struct th_triangle_s
|
|
|
|
{
|
|
|
|
int edges[3]; //negative if edge is flipped
|
|
|
|
th_plane_t planes[3]; //triangle bounding planes
|
|
|
|
int planenum; //plane the triangle is in
|
|
|
|
int front; //tetrahedron at the front
|
|
|
|
int back; //tetrahedron at the back
|
|
|
|
vec3_t mins, maxs; //triangle bounding box
|
|
|
|
struct th_triangle_s *prev, *next; //links in linked triangle lists
|
|
|
|
struct th_triangle_s *hashnext; //next triangle in hash
|
|
|
|
} th_triangle_t;
|
|
|
|
|
|
|
|
//tetrahedron
|
|
|
|
typedef struct th_tetrahedron_s
|
|
|
|
{
|
|
|
|
int triangles[4]; //negative if at backside of triangle
|
|
|
|
float volume; //tetrahedron volume
|
|
|
|
} th_tetrahedron_t;
|
|
|
|
|
|
|
|
typedef struct th_s
|
|
|
|
{
|
|
|
|
//vertexes
|
|
|
|
int numvertexes;
|
|
|
|
th_vertex_t *vertexes;
|
|
|
|
th_vertex_t *vertexhash[VERTEXHASH_SIZE * VERTEXHASH_SIZE];
|
|
|
|
//planes
|
|
|
|
int numplanes;
|
|
|
|
th_plane_t *planes;
|
|
|
|
th_plane_t *planehash[PLANEHASH_SIZE];
|
|
|
|
//edges
|
|
|
|
int numedges;
|
|
|
|
th_edge_t *edges;
|
|
|
|
th_edge_t *edgehash[EDGEHASH_SIZE];
|
|
|
|
//triangles
|
|
|
|
int numtriangles;
|
|
|
|
th_triangle_t *triangles;
|
|
|
|
th_triangle_t *trianglehash[TRIANGLEHASH_SIZE];
|
|
|
|
//tetrahedrons
|
|
|
|
int numtetrahedrons;
|
|
|
|
th_tetrahedron_t *tetrahedrons;
|
|
|
|
} th_t;
|
|
|
|
|
|
|
|
th_t thworld;
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
void TH_InitMaxTH(void)
|
|
|
|
{
|
|
|
|
//get memory for the tetrahedron data
|
|
|
|
thworld.vertexes = (th_vertex_t *) GetClearedMemory(MAX_TH_VERTEXES * sizeof(th_vertex_t));
|
|
|
|
thworld.planes = (th_plane_t *) GetClearedMemory(MAX_TH_PLANES * sizeof(th_plane_t));
|
|
|
|
thworld.edges = (th_edge_t *) GetClearedMemory(MAX_TH_EDGES * sizeof(th_edge_t));
|
|
|
|
thworld.triangles = (th_triangle_t *) GetClearedMemory(MAX_TH_TRIANGLES * sizeof(th_triangle_t));
|
|
|
|
thworld.tetrahedrons = (th_tetrahedron_t *) GetClearedMemory(MAX_TH_TETRAHEDRONS * sizeof(th_tetrahedron_t));
|
|
|
|
//reset the hash tables
|
|
|
|
memset(thworld.vertexhash, 0, VERTEXHASH_SIZE * sizeof(th_vertex_t *));
|
|
|
|
memset(thworld.planehash, 0, PLANEHASH_SIZE * sizeof(th_plane_t *));
|
|
|
|
memset(thworld.edgehash, 0, EDGEHASH_SIZE * sizeof(th_edge_t *));
|
|
|
|
memset(thworld.trianglehash, 0, TRIANGLEHASH_SIZE * sizeof(th_triangle_t *));
|
|
|
|
} //end of the function TH_InitMaxTH
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
void TH_FreeMaxTH(void)
|
|
|
|
{
|
|
|
|
if (thworld.vertexes) FreeMemory(thworld.vertexes);
|
|
|
|
thworld.vertexes = NULL;
|
|
|
|
thworld.numvertexes = 0;
|
|
|
|
if (thworld.planes) FreeMemory(thworld.planes);
|
|
|
|
thworld.planes = NULL;
|
|
|
|
thworld.numplanes = 0;
|
|
|
|
if (thworld.edges) FreeMemory(thworld.edges);
|
|
|
|
thworld.edges = NULL;
|
|
|
|
thworld.numedges = 0;
|
|
|
|
if (thworld.triangles) FreeMemory(thworld.triangles);
|
|
|
|
thworld.triangles = NULL;
|
|
|
|
thworld.numtriangles = 0;
|
|
|
|
if (thworld.tetrahedrons) FreeMemory(thworld.tetrahedrons);
|
|
|
|
thworld.tetrahedrons = NULL;
|
|
|
|
thworld.numtetrahedrons = 0;
|
|
|
|
} //end of the function TH_FreeMaxTH
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
float TH_TriangleArea(th_triangle_t *tri)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
} //end of the function TH_TriangleArea
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
float TH_TetrahedronVolume(th_tetrahedron_t *tetrahedron)
|
|
|
|
{
|
|
|
|
int edgenum, verts[3], i, j, v2;
|
|
|
|
float volume, d;
|
|
|
|
th_triangle_t *tri, *tri2;
|
|
|
|
th_plane_t *plane;
|
|
|
|
|
|
|
|
tri = &thworld.triangles[abs(tetrahedron->triangles[0])];
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
{
|
|
|
|
edgenum = tri->edges[i];
|
|
|
|
if (edgenum < 0) verts[i] = thworld.edges[abs(edgenum)].v[1];
|
|
|
|
else verts[i] = thworld.edges[edgenum].v[0];
|
|
|
|
} //end for
|
|
|
|
//
|
|
|
|
tri2 = &thworld.triangles[abs(tetrahedron->triangles[1])];
|
|
|
|
for (j = 0; j < 3; j++)
|
|
|
|
{
|
|
|
|
edgenum = tri2->edges[i];
|
|
|
|
if (edgenum < 0) v2 = thworld.edges[abs(edgenum)].v[1];
|
|
|
|
else v2 = thworld.edges[edgenum].v[0];
|
|
|
|
if (v2 != verts[0] &&
|
|
|
|
v2 != verts[1] &&
|
|
|
|
v2 != verts[2]) break;
|
|
|
|
} //end for
|
|
|
|
|
|
|
|
plane = &thworld.planes[tri->planenum];
|
|
|
|
d = -(DotProduct (thworld.vertexes[v2].v, plane->normal) - plane->dist);
|
|
|
|
volume = TH_TriangleArea(tri) * d / 3;
|
|
|
|
return volume;
|
|
|
|
} //end of the function TH_TetrahedronVolume
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
int TH_PlaneSignBits(vec3_t normal)
|
|
|
|
{
|
|
|
|
int i, signbits;
|
|
|
|
|
|
|
|
signbits = 0;
|
|
|
|
for (i = 2; i >= 0; i--)
|
|
|
|
{
|
|
|
|
signbits = (signbits << 1) + Sign(normal[i]);
|
|
|
|
} //end for
|
|
|
|
return signbits;
|
|
|
|
} //end of the function TH_PlaneSignBits
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
int TH_PlaneTypeForNormal(vec3_t normal)
|
|
|
|
{
|
|
|
|
vec_t ax, ay, az;
|
|
|
|
|
|
|
|
// NOTE: should these have an epsilon around 1.0?
|
|
|
|
if (normal[0] == 1.0 || normal[0] == -1.0)
|
|
|
|
return PLANE_X;
|
|
|
|
if (normal[1] == 1.0 || normal[1] == -1.0)
|
|
|
|
return PLANE_Y;
|
|
|
|
if (normal[2] == 1.0 || normal[2] == -1.0)
|
|
|
|
return PLANE_Z;
|
|
|
|
|
|
|
|
ax = fabs(normal[0]);
|
|
|
|
ay = fabs(normal[1]);
|
|
|
|
az = fabs(normal[2]);
|
|
|
|
|
|
|
|
if (ax >= ay && ax >= az)
|
|
|
|
return PLANE_ANYX;
|
|
|
|
if (ay >= ax && ay >= az)
|
|
|
|
return PLANE_ANYY;
|
|
|
|
return PLANE_ANYZ;
|
|
|
|
} //end of the function TH_PlaneTypeForNormal
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
qboolean TH_PlaneEqual(th_plane_t *p, vec3_t normal, vec_t dist)
|
|
|
|
{
|
|
|
|
if (
|
|
|
|
fabs(p->normal[0] - normal[0]) < NORMAL_EPSILON
|
|
|
|
&& fabs(p->normal[1] - normal[1]) < NORMAL_EPSILON
|
|
|
|
&& fabs(p->normal[2] - normal[2]) < NORMAL_EPSILON
|
|
|
|
&& fabs(p->dist - dist) < DIST_EPSILON )
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
} //end of the function TH_PlaneEqual
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
void TH_AddPlaneToHash(th_plane_t *p)
|
|
|
|
{
|
|
|
|
int hash;
|
|
|
|
|
|
|
|
hash = (int)fabs(p->dist) / 8;
|
|
|
|
hash &= (PLANEHASH_SIZE-1);
|
|
|
|
|
|
|
|
p->hashnext = thworld.planehash[hash];
|
|
|
|
thworld.planehash[hash] = p;
|
|
|
|
} //end of the function TH_AddPlaneToHash
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
int TH_CreateFloatPlane(vec3_t normal, vec_t dist)
|
|
|
|
{
|
|
|
|
th_plane_t *p, temp;
|
|
|
|
|
|
|
|
if (VectorLength(normal) < 0.5)
|
|
|
|
Error ("FloatPlane: bad normal");
|
|
|
|
// create a new plane
|
|
|
|
if (thworld.numplanes+2 > MAX_TH_PLANES)
|
|
|
|
Error ("MAX_TH_PLANES");
|
|
|
|
|
|
|
|
p = &thworld.planes[thworld.numplanes];
|
|
|
|
VectorCopy (normal, p->normal);
|
|
|
|
p->dist = dist;
|
|
|
|
p->type = (p+1)->type = TH_PlaneTypeForNormal (p->normal);
|
|
|
|
p->signbits = TH_PlaneSignBits(p->normal);
|
|
|
|
|
|
|
|
VectorSubtract (vec3_origin, normal, (p+1)->normal);
|
|
|
|
(p+1)->dist = -dist;
|
|
|
|
(p+1)->signbits = TH_PlaneSignBits((p+1)->normal);
|
|
|
|
|
|
|
|
thworld.numplanes += 2;
|
|
|
|
|
|
|
|
// allways put axial planes facing positive first
|
|
|
|
if (p->type < 3)
|
|
|
|
{
|
|
|
|
if (p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0)
|
|
|
|
{
|
|
|
|
// flip order
|
|
|
|
temp = *p;
|
|
|
|
*p = *(p+1);
|
|
|
|
*(p+1) = temp;
|
|
|
|
|
|
|
|
TH_AddPlaneToHash(p);
|
|
|
|
TH_AddPlaneToHash(p+1);
|
|
|
|
return thworld.numplanes - 1;
|
|
|
|
} //end if
|
|
|
|
} //end if
|
|
|
|
|
|
|
|
TH_AddPlaneToHash(p);
|
|
|
|
TH_AddPlaneToHash(p+1);
|
|
|
|
return thworld.numplanes - 2;
|
|
|
|
} //end of the function TH_CreateFloatPlane
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
void TH_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;
|
|
|
|
} //end if
|
|
|
|
if ( fabs(normal[i] - -1) < NORMAL_EPSILON )
|
|
|
|
{
|
|
|
|
VectorClear (normal);
|
|
|
|
normal[i] = -1;
|
|
|
|
break;
|
|
|
|
} //end if
|
|
|
|
} //end for
|
|
|
|
} //end of the function TH_SnapVector
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
void TH_SnapPlane(vec3_t normal, vec_t *dist)
|
|
|
|
{
|
|
|
|
TH_SnapVector(normal);
|
|
|
|
|
|
|
|
if (fabs(*dist-Q_rint(*dist)) < DIST_EPSILON)
|
|
|
|
*dist = Q_rint(*dist);
|
|
|
|
} //end of the function TH_SnapPlane
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
int TH_FindFloatPlane(vec3_t normal, vec_t dist)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
th_plane_t *p;
|
|
|
|
int hash, h;
|
|
|
|
|
|
|
|
TH_SnapPlane (normal, &dist);
|
|
|
|
hash = (int)fabs(dist) / 8;
|
|
|
|
hash &= (PLANEHASH_SIZE-1);
|
|
|
|
|
|
|
|
// search the border bins as well
|
|
|
|
for (i = -1; i <= 1; i++)
|
|
|
|
{
|
|
|
|
h = (hash+i)&(PLANEHASH_SIZE-1);
|
|
|
|
for (p = thworld.planehash[h]; p; p = p->hashnext)
|
|
|
|
{
|
|
|
|
if (TH_PlaneEqual(p, normal, dist))
|
|
|
|
{
|
|
|
|
return p - thworld.planes;
|
|
|
|
} //end if
|
|
|
|
} //end for
|
|
|
|
} //end for
|
|
|
|
return TH_CreateFloatPlane(normal, dist);
|
|
|
|
} //end of the function TH_FindFloatPlane
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
int TH_PlaneFromPoints(int v1, int v2, int v3)
|
|
|
|
{
|
|
|
|
vec3_t t1, t2, normal;
|
|
|
|
vec_t dist;
|
|
|
|
float *p0, *p1, *p2;
|
|
|
|
|
|
|
|
p0 = thworld.vertexes[v1].v;
|
|
|
|
p1 = thworld.vertexes[v2].v;
|
|
|
|
p2 = thworld.vertexes[v3].v;
|
|
|
|
|
|
|
|
VectorSubtract(p0, p1, t1);
|
|
|
|
VectorSubtract(p2, p1, t2);
|
|
|
|
CrossProduct(t1, t2, normal);
|
|
|
|
VectorNormalize(normal);
|
|
|
|
|
|
|
|
dist = DotProduct(p0, normal);
|
|
|
|
|
|
|
|
return TH_FindFloatPlane(normal, dist);
|
|
|
|
} //end of the function TH_PlaneFromPoints
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
void TH_AddEdgeUser(int edgenum)
|
|
|
|
{
|
|
|
|
th_edge_t *edge;
|
|
|
|
|
|
|
|
edge = &thworld.edges[abs(edgenum)];
|
|
|
|
//increase edge user count
|
|
|
|
edge->usercount++;
|
|
|
|
//increase vertex user count as well
|
|
|
|
thworld.vertexes[edge->v[0]].usercount++;
|
|
|
|
thworld.vertexes[edge->v[1]].usercount++;
|
|
|
|
} //end of the function TH_AddEdgeUser
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
void TH_RemoveEdgeUser(int edgenum)
|
|
|
|
{
|
|
|
|
th_edge_t *edge;
|
|
|
|
|
|
|
|
edge = &thworld.edges[abs(edgenum)];
|
|
|
|
//decrease edge user count
|
|
|
|
edge->usercount--;
|
|
|
|
//decrease vertex user count as well
|
|
|
|
thworld.vertexes[edge->v[0]].usercount--;
|
|
|
|
thworld.vertexes[edge->v[1]].usercount--;
|
|
|
|
} //end of the function TH_RemoveEdgeUser
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
void TH_FreeTriangleEdges(th_triangle_t *tri)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
{
|
|
|
|
TH_RemoveEdgeUser(abs(tri->edges[i]));
|
|
|
|
} //end for
|
|
|
|
} //end of the function TH_FreeTriangleEdges
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
unsigned TH_HashVec(vec3_t vec)
|
|
|
|
{
|
|
|
|
int x, y;
|
|
|
|
|
|
|
|
x = (MAX_MAP_BOUNDS + (int)(vec[0]+0.5)) >> VERTEXHASH_SHIFT;
|
|
|
|
y = (MAX_MAP_BOUNDS + (int)(vec[1]+0.5)) >> VERTEXHASH_SHIFT;
|
|
|
|
|
|
|
|
if (x < 0 || x >= VERTEXHASH_SIZE || y < 0 || y >= VERTEXHASH_SIZE)
|
|
|
|
Error("HashVec: point %f %f %f outside valid range", vec[0], vec[1], vec[2]);
|
|
|
|
|
|
|
|
return y*VERTEXHASH_SIZE + x;
|
|
|
|
} //end of the function TH_HashVec
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
int TH_FindVertex(vec3_t v)
|
|
|
|
{
|
|
|
|
int i, h;
|
|
|
|
th_vertex_t *vertex;
|
|
|
|
vec3_t vert;
|
|
|
|
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
{
|
|
|
|
if ( fabs(v[i] - Q_rint(v[i])) < INTEGRAL_EPSILON)
|
|
|
|
vert[i] = Q_rint(v[i]);
|
|
|
|
else
|
|
|
|
vert[i] = v[i];
|
|
|
|
} //end for
|
|
|
|
|
|
|
|
h = TH_HashVec(vert);
|
|
|
|
|
|
|
|
for (vertex = thworld.vertexhash[h]; vertex; vertex = vertex->hashnext)
|
|
|
|
{
|
|
|
|
if (fabs(vertex->v[0] - vert[0]) < VERTEX_EPSILON &&
|
|
|
|
fabs(vertex->v[1] - vert[1]) < VERTEX_EPSILON &&
|
|
|
|
fabs(vertex->v[2] - vert[2]) < VERTEX_EPSILON)
|
|
|
|
{
|
|
|
|
return vertex - thworld.vertexes;
|
|
|
|
} //end if
|
|
|
|
} //end for
|
|
|
|
return 0;
|
|
|
|
} //end of the function TH_FindVertex
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
void TH_AddVertexToHash(th_vertex_t *vertex)
|
|
|
|
{
|
|
|
|
int hashvalue;
|
|
|
|
|
|
|
|
hashvalue = TH_HashVec(vertex->v);
|
|
|
|
vertex->hashnext = thworld.vertexhash[hashvalue];
|
|
|
|
thworld.vertexhash[hashvalue] = vertex;
|
|
|
|
} //end of the function TH_AddVertexToHash
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
int TH_CreateVertex(vec3_t v)
|
|
|
|
{
|
|
|
|
if (thworld.numvertexes == 0) thworld.numvertexes = 1;
|
|
|
|
if (thworld.numvertexes >= MAX_TH_VERTEXES)
|
|
|
|
Error("MAX_TH_VERTEXES");
|
|
|
|
VectorCopy(v, thworld.vertexes[thworld.numvertexes].v);
|
|
|
|
thworld.vertexes[thworld.numvertexes].usercount = 0;
|
|
|
|
TH_AddVertexToHash(&thworld.vertexes[thworld.numvertexes]);
|
|
|
|
thworld.numvertexes++;
|
|
|
|
return thworld.numvertexes-1;
|
|
|
|
} //end of the function TH_CreateVertex
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
int TH_FindOrCreateVertex(vec3_t v)
|
|
|
|
{
|
|
|
|
int vertexnum;
|
|
|
|
|
|
|
|
vertexnum = TH_FindVertex(v);
|
|
|
|
if (!vertexnum) vertexnum = TH_CreateVertex(v);
|
|
|
|
return vertexnum;
|
|
|
|
} //end of the function TH_FindOrCreateVertex
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
int TH_FindEdge(int v1, int v2)
|
|
|
|
{
|
|
|
|
int hashvalue;
|
|
|
|
th_edge_t *edge;
|
|
|
|
|
|
|
|
hashvalue = (v1 + v2) & (EDGEHASH_SIZE-1);
|
|
|
|
|
|
|
|
for (edge = thworld.edgehash[hashvalue]; edge; edge = edge->hashnext)
|
|
|
|
{
|
|
|
|
if (edge->v[0] == v1 && edge->v[1] == v2) return edge - thworld.edges;
|
|
|
|
if (edge->v[1] == v1 && edge->v[0] == v2) return -(edge - thworld.edges);
|
|
|
|
} //end for
|
|
|
|
return 0;
|
|
|
|
} //end of the function TH_FindEdge
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
void TH_AddEdgeToHash(th_edge_t *edge)
|
|
|
|
{
|
|
|
|
int hashvalue;
|
|
|
|
|
|
|
|
hashvalue = (edge->v[0] + edge->v[1]) & (EDGEHASH_SIZE-1);
|
|
|
|
edge->hashnext = thworld.edgehash[hashvalue];
|
|
|
|
thworld.edgehash[hashvalue] = edge;
|
|
|
|
} //end of the function TH_AddEdgeToHash
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
int TH_CreateEdge(int v1, int v2)
|
|
|
|
{
|
|
|
|
th_edge_t *edge;
|
|
|
|
|
|
|
|
if (thworld.numedges == 0) thworld.numedges = 1;
|
|
|
|
if (thworld.numedges >= MAX_TH_EDGES)
|
|
|
|
Error("MAX_TH_EDGES");
|
|
|
|
edge = &thworld.edges[thworld.numedges++];
|
|
|
|
edge->v[0] = v1;
|
|
|
|
edge->v[1] = v2;
|
|
|
|
TH_AddEdgeToHash(edge);
|
|
|
|
return thworld.numedges-1;
|
|
|
|
} //end of the function TH_CreateEdge
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
int TH_FindOrCreateEdge(int v1, int v2)
|
|
|
|
{
|
|
|
|
int edgenum;
|
|
|
|
|
|
|
|
edgenum = TH_FindEdge(v1, v2);
|
|
|
|
if (!edgenum) edgenum = TH_CreateEdge(v1, v2);
|
|
|
|
return edgenum;
|
|
|
|
} //end of the function TH_FindOrCreateEdge
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
int TH_FindTriangle(int verts[3])
|
|
|
|
{
|
|
|
|
int i, hashvalue, edges[3];
|
|
|
|
th_triangle_t *tri;
|
|
|
|
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
{
|
|
|
|
edges[i] = TH_FindEdge(verts[i], verts[(i+1)%3]);
|
|
|
|
if (!edges[i]) return false;
|
|
|
|
} //end for
|
|
|
|
hashvalue = (abs(edges[0]) + abs(edges[1]) + abs(edges[2])) & (TRIANGLEHASH_SIZE-1);
|
|
|
|
for (tri = thworld.trianglehash[hashvalue]; tri; tri = tri->next)
|
|
|
|
{
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
{
|
|
|
|
if (abs(tri->edges[i]) != abs(edges[0]) &&
|
|
|
|
abs(tri->edges[i]) != abs(edges[1]) &&
|
|
|
|
abs(tri->edges[i]) != abs(edges[2])) break;
|
|
|
|
} //end for
|
|
|
|
if (i >= 3) return tri - thworld.triangles;
|
|
|
|
} //end for
|
|
|
|
return 0;
|
|
|
|
} //end of the function TH_FindTriangle
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
void TH_AddTriangleToHash(th_triangle_t *tri)
|
|
|
|
{
|
|
|
|
int hashvalue;
|
|
|
|
|
|
|
|
hashvalue = (abs(tri->edges[0]) + abs(tri->edges[1]) + abs(tri->edges[2])) & (TRIANGLEHASH_SIZE-1);
|
|
|
|
tri->hashnext = thworld.trianglehash[hashvalue];
|
|
|
|
thworld.trianglehash[hashvalue] = tri;
|
|
|
|
} //end of the function TH_AddTriangleToHash
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
void TH_CreateTrianglePlanes(int verts[3], th_plane_t *triplane, th_plane_t *planes)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
vec3_t dir;
|
|
|
|
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
{
|
|
|
|
VectorSubtract(thworld.vertexes[verts[(i+1)%3]].v, thworld.vertexes[verts[i]].v, dir);
|
|
|
|
CrossProduct(dir, triplane->normal, planes[i].normal);
|
|
|
|
VectorNormalize(planes[i].normal);
|
|
|
|
planes[i].dist = DotProduct(thworld.vertexes[verts[i]].v, planes[i].normal);
|
|
|
|
} //end for
|
|
|
|
} //end of the function TH_CreateTrianglePlanes
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
int TH_CreateTriangle(int verts[3])
|
|
|
|
{
|
|
|
|
th_triangle_t *tri;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (thworld.numtriangles == 0) thworld.numtriangles = 1;
|
|
|
|
if (thworld.numtriangles >= MAX_TH_TRIANGLES)
|
|
|
|
Error("MAX_TH_TRIANGLES");
|
|
|
|
tri = &thworld.triangles[thworld.numtriangles++];
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
{
|
|
|
|
tri->edges[i] = TH_FindOrCreateEdge(verts[i], verts[(i+1)%3]);
|
|
|
|
TH_AddEdgeUser(abs(tri->edges[i]));
|
|
|
|
} //end for
|
|
|
|
tri->front = 0;
|
|
|
|
tri->back = 0;
|
|
|
|
tri->planenum = TH_PlaneFromPoints(verts[0], verts[1], verts[2]);
|
|
|
|
tri->prev = NULL;
|
|
|
|
tri->next = NULL;
|
|
|
|
tri->hashnext = NULL;
|
|
|
|
TH_CreateTrianglePlanes(verts, &thworld.planes[tri->planenum], tri->planes);
|
|
|
|
TH_AddTriangleToHash(tri);
|
|
|
|
ClearBounds(tri->mins, tri->maxs);
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
{
|
|
|
|
AddPointToBounds(thworld.vertexes[verts[i]].v, tri->mins, tri->maxs);
|
|
|
|
} //end for
|
|
|
|
return thworld.numtriangles-1;
|
|
|
|
} //end of the function TH_CreateTriangle
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
int TH_CreateTetrahedron(int triangles[4])
|
|
|
|
{
|
|
|
|
th_tetrahedron_t *tetrahedron;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (thworld.numtetrahedrons == 0) thworld.numtetrahedrons = 1;
|
|
|
|
if (thworld.numtetrahedrons >= MAX_TH_TETRAHEDRONS)
|
|
|
|
Error("MAX_TH_TETRAHEDRONS");
|
|
|
|
tetrahedron = &thworld.tetrahedrons[thworld.numtetrahedrons++];
|
|
|
|
for (i = 0; i < 4; i++)
|
|
|
|
{
|
|
|
|
tetrahedron->triangles[i] = triangles[i];
|
|
|
|
if (thworld.triangles[abs(triangles[i])].front)
|
|
|
|
{
|
|
|
|
thworld.triangles[abs(triangles[i])].back = thworld.numtetrahedrons-1;
|
|
|
|
} //end if
|
|
|
|
else
|
|
|
|
{
|
|
|
|
thworld.triangles[abs(triangles[i])].front = thworld.numtetrahedrons-1;
|
|
|
|
} //end else
|
|
|
|
} //end for
|
|
|
|
tetrahedron->volume = 0;
|
|
|
|
return thworld.numtetrahedrons-1;
|
|
|
|
} //end of the function TH_CreateTetrahedron
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
int TH_IntersectTrianglePlanes(int v1, int v2, th_plane_t *triplane, th_plane_t *planes)
|
|
|
|
{
|
|
|
|
float *p1, *p2, front, back, frac, d;
|
|
|
|
int i, side, lastside;
|
|
|
|
vec3_t mid;
|
|
|
|
|
|
|
|
p1 = thworld.vertexes[v1].v;
|
|
|
|
p2 = thworld.vertexes[v2].v;
|
|
|
|
|
|
|
|
front = DotProduct(p1, triplane->normal) - triplane->dist;
|
|
|
|
back = DotProduct(p2, triplane->normal) - triplane->dist;
|
|
|
|
//if both points at the same side of the plane
|
|
|
|
if (front < 0.1 && back < 0.1) return false;
|
|
|
|
if (front > -0.1 && back > -0.1) return false;
|
|
|
|
//
|
|
|
|
frac = front/(front-back);
|
|
|
|
mid[0] = p1[0] + (p2[0] - p1[0]) * frac;
|
|
|
|
mid[1] = p1[1] + (p2[1] - p1[1]) * frac;
|
|
|
|
mid[2] = p1[2] + (p2[2] - p1[2]) * frac;
|
|
|
|
//if the mid point is at the same side of all the tri bounding planes
|
|
|
|
lastside = 0;
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
{
|
|
|
|
d = DotProduct(mid, planes[i].normal) - planes[i].dist;
|
|
|
|
side = d < 0;
|
|
|
|
if (i && side != lastside) return false;
|
|
|
|
lastside = side;
|
|
|
|
} //end for
|
|
|
|
return true;
|
|
|
|
} //end of the function TH_IntersectTrianglePlanes
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
int TH_OutsideBoundingBox(int v1, int v2, vec3_t mins, vec3_t maxs)
|
|
|
|
{
|
|
|
|
float *p1, *p2;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
p1 = thworld.vertexes[v1].v;
|
|
|
|
p2 = thworld.vertexes[v2].v;
|
|
|
|
//if both points are at the outer side of one of the bounding box planes
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
{
|
|
|
|
if (p1[i] < mins[i] && p2[i] < mins[i]) return true;
|
|
|
|
if (p1[i] > maxs[i] && p2[i] > maxs[i]) return true;
|
|
|
|
} //end for
|
|
|
|
return false;
|
|
|
|
} //end of the function TH_OutsideBoundingBox
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
int TH_TryEdge(int v1, int v2)
|
|
|
|
{
|
|
|
|
int i, j, v;
|
|
|
|
th_plane_t *plane;
|
|
|
|
th_triangle_t *tri;
|
|
|
|
|
|
|
|
//if the edge already exists it must be valid
|
|
|
|
if (TH_FindEdge(v1, v2)) return true;
|
|
|
|
//test the edge with all existing triangles
|
|
|
|
for (i = 1; i < thworld.numtriangles; i++)
|
|
|
|
{
|
|
|
|
tri = &thworld.triangles[i];
|
|
|
|
//if triangle is enclosed by two tetrahedrons we don't have to test it
|
|
|
|
//because the edge always has to go through another triangle of those
|
|
|
|
//tetrahedrons first to reach the enclosed triangle
|
|
|
|
if (tri->front && tri->back) continue;
|
|
|
|
//if the edges is totally outside the triangle bounding box
|
|
|
|
if (TH_OutsideBoundingBox(v1, v2, tri->mins, tri->maxs)) continue;
|
|
|
|
//if one of the edge vertexes is used by this triangle
|
|
|
|
for (j = 0; j < 3; j++)
|
|
|
|
{
|
|
|
|
v = thworld.edges[abs(tri->edges[j])].v[tri->edges[j] < 0];
|
|
|
|
if (v == v1 || v == v2) break;
|
|
|
|
} //end for
|
|
|
|
if (j < 3) continue;
|
|
|
|
//get the triangle plane
|
|
|
|
plane = &thworld.planes[tri->planenum];
|
|
|
|
//if the edge intersects with a triangle then it's not valid
|
|
|
|
if (TH_IntersectTrianglePlanes(v1, v2, plane, tri->planes)) return false;
|
|
|
|
} //end for
|
|
|
|
return true;
|
|
|
|
} //end of the function TH_TryEdge
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
int TH_TryTriangle(int verts[3])
|
|
|
|
{
|
|
|
|
th_plane_t planes[3], triplane;
|
|
|
|
vec3_t t1, t2;
|
|
|
|
float *p0, *p1, *p2;
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
p0 = thworld.vertexes[verts[0]].v;
|
|
|
|
p1 = thworld.vertexes[verts[1]].v;
|
|
|
|
p2 = thworld.vertexes[verts[2]].v;
|
|
|
|
|
|
|
|
VectorSubtract(p0, p1, t1);
|
|
|
|
VectorSubtract(p2, p1, t2);
|
|
|
|
CrossProduct(t1, t2, triplane.normal);
|
|
|
|
VectorNormalize(triplane.normal);
|
|
|
|
triplane.dist = DotProduct(p0, triplane.normal);
|
|
|
|
//
|
|
|
|
TH_CreateTrianglePlanes(verts, &triplane, planes);
|
|
|
|
//test if any existing edge intersects with this triangle
|
|
|
|
for (i = 1; i < thworld.numedges; i++)
|
|
|
|
{
|
|
|
|
//if the edge is only used by triangles with tetrahedrons at both sides
|
|
|
|
if (!thworld.edges[i].usercount) continue;
|
|
|
|
//if one of the triangle vertexes is used by this edge
|
|
|
|
for (j = 0; j < 3; j++)
|
|
|
|
{
|
|
|
|
if (verts[j] == thworld.edges[j].v[0] ||
|
|
|
|
verts[j] == thworld.edges[j].v[1]) break;
|
|
|
|
} //end for
|
|
|
|
if (j < 3) continue;
|
|
|
|
//if this edge intersects with the triangle
|
|
|
|
if (TH_IntersectTrianglePlanes(thworld.edges[i].v[0], thworld.edges[i].v[1], &triplane, planes)) return false;
|
|
|
|
} //end for
|
|
|
|
return true;
|
|
|
|
} //end of the function TH_TryTriangle
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
void TH_AddTriangleToList(th_triangle_t **trianglelist, th_triangle_t *tri)
|
|
|
|
{
|
|
|
|
tri->prev = NULL;
|
|
|
|
tri->next = *trianglelist;
|
|
|
|
if (*trianglelist) (*trianglelist)->prev = tri;
|
|
|
|
*trianglelist = tri;
|
|
|
|
} //end of the function TH_AddTriangleToList
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
void TH_RemoveTriangleFromList(th_triangle_t **trianglelist, th_triangle_t *tri)
|
|
|
|
{
|
|
|
|
if (tri->next) tri->next->prev = tri->prev;
|
|
|
|
if (tri->prev) tri->prev->next = tri->next;
|
|
|
|
else *trianglelist = tri->next;
|
|
|
|
} //end of the function TH_RemoveTriangleFromList
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
int TH_FindTetrahedron1(th_triangle_t *tri, int *triangles)
|
|
|
|
{
|
|
|
|
int i, j, edgenum, side, v1, v2, v3, v4;
|
|
|
|
int verts1[3], verts2[3];
|
|
|
|
th_triangle_t *tri2;
|
|
|
|
|
|
|
|
//find another triangle with a shared edge
|
|
|
|
for (tri2 = tri->next; tri2; tri2 = tri2->next)
|
|
|
|
{
|
|
|
|
//if the triangles are in the same plane
|
|
|
|
if ((tri->planenum & ~1) == (tri2->planenum & ~1)) continue;
|
|
|
|
//try to find a shared edge
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
{
|
|
|
|
edgenum = abs(tri->edges[i]);
|
|
|
|
for (j = 0; j < 3; j++)
|
|
|
|
{
|
|
|
|
if (edgenum == abs(tri2->edges[j])) break;
|
|
|
|
} //end for
|
|
|
|
if (j < 3) break;
|
|
|
|
} //end for
|
|
|
|
//if the triangles have a shared edge
|
|
|
|
if (i < 3)
|
|
|
|
{
|
|
|
|
edgenum = tri->edges[(i+1)%3];
|
|
|
|
if (edgenum < 0) v1 = thworld.edges[abs(edgenum)].v[0];
|
|
|
|
else v1 = thworld.edges[edgenum].v[1];
|
|
|
|
edgenum = tri2->edges[(j+1)%3];
|
|
|
|
if (edgenum < 0) v2 = thworld.edges[abs(edgenum)].v[0];
|
|
|
|
else v2 = thworld.edges[edgenum].v[1];
|
|
|
|
//try the new edge
|
|
|
|
if (TH_TryEdge(v1, v2))
|
|
|
|
{
|
|
|
|
edgenum = tri->edges[i];
|
|
|
|
side = edgenum < 0;
|
|
|
|
//get the vertexes of the shared edge
|
|
|
|
v3 = thworld.edges[abs(edgenum)].v[side];
|
|
|
|
v4 = thworld.edges[abs(edgenum)].v[!side];
|
|
|
|
//try the two new triangles
|
|
|
|
verts1[0] = v1;
|
|
|
|
verts1[1] = v2;
|
|
|
|
verts1[2] = v3;
|
|
|
|
triangles[2] = TH_FindTriangle(verts1);
|
|
|
|
if (triangles[2] || TH_TryTriangle(verts1))
|
|
|
|
{
|
|
|
|
verts2[0] = v2;
|
|
|
|
verts2[1] = v1;
|
|
|
|
verts2[2] = v4;
|
|
|
|
triangles[3] = TH_FindTriangle(verts2);
|
|
|
|
if (triangles[3] || TH_TryTriangle(verts2))
|
|
|
|
{
|
|
|
|
triangles[0] = tri - thworld.triangles;
|
|
|
|
triangles[1] = tri2 - thworld.triangles;
|
|
|
|
if (!triangles[2]) triangles[2] = TH_CreateTriangle(verts1);
|
|
|
|
if (!triangles[3]) triangles[3] = TH_CreateTriangle(verts2);
|
|
|
|
return true;
|
|
|
|
} //end if
|
|
|
|
} //end if
|
|
|
|
} //end if
|
|
|
|
} //end if
|
|
|
|
} //end for
|
|
|
|
return false;
|
|
|
|
} //end of the function TH_FindTetrahedron
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
int TH_FindTetrahedron2(th_triangle_t *tri, int *triangles)
|
|
|
|
{
|
|
|
|
int i, edgenum, v1, verts[3], triverts[3];
|
|
|
|
float d;
|
|
|
|
th_plane_t *plane;
|
|
|
|
|
|
|
|
//get the verts of this triangle
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
{
|
|
|
|
edgenum = tri->edges[i];
|
|
|
|
if (edgenum < 0) verts[i] = thworld.edges[abs(edgenum)].v[1];
|
|
|
|
else verts[i] = thworld.edges[edgenum].v[0];
|
|
|
|
} //end for
|
|
|
|
//
|
|
|
|
plane = &thworld.planes[tri->planenum];
|
|
|
|
for (v1 = 0; v1 < thworld.numvertexes; v1++)
|
|
|
|
{
|
|
|
|
//if the vertex is only used by triangles with tetrahedrons at both sides
|
|
|
|
if (!thworld.vertexes[v1].usercount) continue;
|
|
|
|
//check if the vertex is not coplanar with the triangle
|
|
|
|
d = DotProduct(thworld.vertexes[v1].v, plane->normal) - plane->dist;
|
|
|
|
if (fabs(d) < 1) continue;
|
|
|
|
//check if we can create edges from the triangle towards this new vertex
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
{
|
|
|
|
if (v1 == verts[i]) break;
|
|
|
|
if (!TH_TryEdge(v1, verts[i])) break;
|
|
|
|
} //end for
|
|
|
|
if (i < 3) continue;
|
|
|
|
//check if the triangles are valid
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
{
|
|
|
|
triverts[0] = v1;
|
|
|
|
triverts[1] = verts[i];
|
|
|
|
triverts[2] = verts[(i+1)%3];
|
|
|
|
//if the triangle already exists then it is valid
|
|
|
|
triangles[i] = TH_FindTriangle(triverts);
|
|
|
|
if (!triangles[i])
|
|
|
|
{
|
|
|
|
if (!TH_TryTriangle(triverts)) break;
|
|
|
|
} //end if
|
|
|
|
} //end for
|
|
|
|
if (i < 3) continue;
|
|
|
|
//create the tetrahedron triangles using the new vertex
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
{
|
|
|
|
if (!triangles[i])
|
|
|
|
{
|
|
|
|
triverts[0] = v1;
|
|
|
|
triverts[1] = verts[i];
|
|
|
|
triverts[2] = verts[(i+1)%3];
|
|
|
|
triangles[i] = TH_CreateTriangle(triverts);
|
|
|
|
} //end if
|
|
|
|
} //end for
|
|
|
|
//add the existing triangle
|
|
|
|
triangles[3] = tri - thworld.triangles;
|
|
|
|
//
|
|
|
|
return true;
|
|
|
|
} //end for
|
|
|
|
return false;
|
|
|
|
} //end of the function TH_FindTetrahedron2
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
void TH_TetrahedralDecomposition(th_triangle_t *triangles)
|
|
|
|
{
|
|
|
|
int i, thtriangles[4], numtriangles;
|
|
|
|
th_triangle_t *donetriangles, *tri;
|
|
|
|
|
|
|
|
donetriangles = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
numtriangles = 0;
|
|
|
|
qprintf("%6d triangles", numtriangles);
|
|
|
|
for (tri = triangles; tri; tri = triangles)
|
|
|
|
{
|
|
|
|
qprintf("\r%6d", numtriangles++);
|
|
|
|
if (!TH_FindTetrahedron1(tri, thtriangles))
|
|
|
|
{
|
|
|
|
// if (!TH_FindTetrahedron2(tri, thtriangles))
|
|
|
|
{
|
|
|
|
// Error("triangle without tetrahedron");
|
|
|
|
TH_RemoveTriangleFromList(&triangles, tri);
|
|
|
|
continue;
|
|
|
|
} //end if
|
|
|
|
} //end if
|
|
|
|
//create a tetrahedron from the triangles
|
|
|
|
TH_CreateTetrahedron(thtriangles);
|
|
|
|
//
|
|
|
|
for (i = 0; i < 4; i++)
|
|
|
|
{
|
|
|
|
if (thworld.triangles[abs(thtriangles[i])].front &&
|
|
|
|
thworld.triangles[abs(thtriangles[i])].back)
|
|
|
|
{
|
|
|
|
TH_RemoveTriangleFromList(&triangles, &thworld.triangles[abs(thtriangles[i])]);
|
|
|
|
TH_AddTriangleToList(&donetriangles, &thworld.triangles[abs(thtriangles[i])]);
|
|
|
|
TH_FreeTriangleEdges(&thworld.triangles[abs(thtriangles[i])]);
|
|
|
|
} //end if
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TH_AddTriangleToList(&triangles, &thworld.triangles[abs(thtriangles[i])]);
|
|
|
|
} //end else
|
|
|
|
} //end for
|
|
|
|
} //end for*/
|
|
|
|
qprintf("%6d tetrahedrons", thworld.numtetrahedrons);
|
|
|
|
do
|
|
|
|
{
|
|
|
|
do
|
|
|
|
{
|
|
|
|
numtriangles = 0;
|
|
|
|
for (i = 1; i < thworld.numtriangles; i++)
|
|
|
|
{
|
|
|
|
tri = &thworld.triangles[i];
|
|
|
|
if (tri->front && tri->back) continue;
|
|
|
|
//qprintf("\r%6d", numtriangles++);
|
|
|
|
if (!TH_FindTetrahedron1(tri, thtriangles))
|
|
|
|
{
|
|
|
|
// if (!TH_FindTetrahedron2(tri, thtriangles))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
} //end if
|
|
|
|
} //end if
|
|
|
|
numtriangles++;
|
|
|
|
//create a tetrahedron from the triangles
|
|
|
|
TH_CreateTetrahedron(thtriangles);
|
|
|
|
qprintf("\r%6d", thworld.numtetrahedrons);
|
|
|
|
} //end for
|
|
|
|
} while(numtriangles);
|
|
|
|
for (i = 1; i < thworld.numtriangles; i++)
|
|
|
|
{
|
|
|
|
tri = &thworld.triangles[i];
|
|
|
|
if (tri->front && tri->back) continue;
|
|
|
|
//qprintf("\r%6d", numtriangles++);
|
|
|
|
// if (!TH_FindTetrahedron1(tri, thtriangles))
|
|
|
|
{
|
|
|
|
if (!TH_FindTetrahedron2(tri, thtriangles))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
} //end if
|
|
|
|
} //end if
|
|
|
|
numtriangles++;
|
|
|
|
//create a tetrahedron from the triangles
|
|
|
|
TH_CreateTetrahedron(thtriangles);
|
|
|
|
qprintf("\r%6d", thworld.numtetrahedrons);
|
|
|
|
} //end for
|
|
|
|
} while(numtriangles);
|
|
|
|
//
|
|
|
|
numtriangles = 0;
|
|
|
|
for (i = 1; i < thworld.numtriangles; i++)
|
|
|
|
{
|
|
|
|
tri = &thworld.triangles[i];
|
|
|
|
if (!tri->front && !tri->back) numtriangles++;
|
|
|
|
} //end for
|
|
|
|
Log_Print("\r%6d triangles with front only\n", numtriangles);
|
|
|
|
Log_Print("\r%6d tetrahedrons\n", thworld.numtetrahedrons-1);
|
|
|
|
} //end of the function TH_TetrahedralDecomposition
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
void TH_AASFaceVertex(aas_face_t *face, int index, vec3_t vertex)
|
|
|
|
{
|
|
|
|
int edgenum, side;
|
|
|
|
|
|
|
|
edgenum = aasworld.edgeindex[face->firstedge + index];
|
|
|
|
side = edgenum < 0;
|
|
|
|
VectorCopy(aasworld.vertexes[aasworld.edges[abs(edgenum)].v[side]], vertex);
|
|
|
|
} //end of the function TH_AASFaceVertex
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
int TH_Colinear(float *v0, float *v1, float *v2)
|
|
|
|
{
|
|
|
|
vec3_t t1, t2, vcross;
|
|
|
|
float d;
|
|
|
|
|
|
|
|
VectorSubtract(v1, v0, t1);
|
|
|
|
VectorSubtract(v2, v0, t2);
|
|
|
|
CrossProduct (t1, t2, vcross);
|
|
|
|
d = VectorLength( vcross );
|
|
|
|
|
|
|
|
// if cross product is zero point is colinear
|
|
|
|
if (d < 10)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
} //end if
|
|
|
|
return false;
|
|
|
|
} //end of the function TH_Colinear
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
void TH_FaceCenter(aas_face_t *face, vec3_t center)
|
|
|
|
{
|
|
|
|
int i, edgenum, side;
|
|
|
|
aas_edge_t *edge;
|
|
|
|
|
|
|
|
VectorClear(center);
|
|
|
|
for (i = 0; i < face->numedges; i++)
|
|
|
|
{
|
|
|
|
edgenum = abs(aasworld.edgeindex[face->firstedge + i]);
|
|
|
|
side = edgenum < 0;
|
|
|
|
edge = &aasworld.edges[abs(edgenum)];
|
|
|
|
VectorAdd(aasworld.vertexes[edge->v[side]], center, center);
|
|
|
|
} //end for
|
|
|
|
VectorScale(center, 1.0 / face->numedges, center);
|
|
|
|
} //end of the function TH_FaceCenter
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
th_triangle_t *TH_CreateAASFaceTriangles(aas_face_t *face)
|
|
|
|
{
|
|
|
|
int i, first, verts[3], trinum;
|
|
|
|
vec3_t p0, p1, p2, p3, p4, center;
|
|
|
|
th_triangle_t *tri, *triangles;
|
|
|
|
|
|
|
|
triangles = NULL;
|
|
|
|
//find three points that are not colinear
|
|
|
|
for (i = 0; i < face->numedges; i++)
|
|
|
|
{
|
|
|
|
TH_AASFaceVertex(face, (face->numedges + i-2)%face->numedges, p0);
|
|
|
|
TH_AASFaceVertex(face, (face->numedges + i-1)%face->numedges, p1);
|
|
|
|
TH_AASFaceVertex(face, (i )%face->numedges, p2);
|
|
|
|
if (TH_Colinear(p2, p0, p1)) continue;
|
|
|
|
TH_AASFaceVertex(face, (i+1)%face->numedges, p3);
|
|
|
|
TH_AASFaceVertex(face, (i+2)%face->numedges, p4);
|
|
|
|
if (TH_Colinear(p2, p3, p4)) continue;
|
|
|
|
break;
|
|
|
|
} //end for
|
|
|
|
//if there are three points that are not colinear
|
|
|
|
if (i < face->numedges)
|
|
|
|
{
|
|
|
|
//normal triangulation
|
|
|
|
first = i; //left and right most point of three non-colinear points
|
|
|
|
TH_AASFaceVertex(face, first, p0);
|
|
|
|
verts[0] = TH_FindOrCreateVertex(p0);
|
|
|
|
for (i = 1; i < face->numedges-1; i++)
|
|
|
|
{
|
|
|
|
TH_AASFaceVertex(face, (first+i )%face->numedges, p1);
|
|
|
|
TH_AASFaceVertex(face, (first+i+1)%face->numedges, p2);
|
|
|
|
verts[1] = TH_FindOrCreateVertex(p1);
|
|
|
|
verts[2] = TH_FindOrCreateVertex(p2);
|
|
|
|
trinum = TH_CreateTriangle(verts);
|
|
|
|
tri = &thworld.triangles[trinum];
|
|
|
|
tri->front = -1;
|
|
|
|
TH_AddTriangleToList(&triangles, tri);
|
|
|
|
} //end for
|
|
|
|
} //end if
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//fan triangulation
|
|
|
|
TH_FaceCenter(face, center);
|
|
|
|
//
|
|
|
|
verts[0] = TH_FindOrCreateVertex(center);
|
|
|
|
for (i = 0; i < face->numedges; i++)
|
|
|
|
{
|
|
|
|
TH_AASFaceVertex(face, (i )%face->numedges, p1);
|
|
|
|
TH_AASFaceVertex(face, (i+1)%face->numedges, p2);
|
|
|
|
if (TH_Colinear(center, p1, p2)) continue;
|
|
|
|
verts[1] = TH_FindOrCreateVertex(p1);
|
|
|
|
verts[2] = TH_FindOrCreateVertex(p2);
|
|
|
|
trinum = TH_CreateTriangle(verts);
|
|
|
|
tri = &thworld.triangles[trinum];
|
|
|
|
tri->front = -1;
|
|
|
|
TH_AddTriangleToList(&triangles, tri);
|
|
|
|
} //end for
|
|
|
|
} //end else
|
|
|
|
return triangles;
|
|
|
|
} //end of the function TH_CreateAASFaceTriangles
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
th_triangle_t *TH_AASToTriangleMesh(void)
|
|
|
|
{
|
|
|
|
int i, j, facenum, otherareanum;
|
|
|
|
aas_face_t *face;
|
|
|
|
th_triangle_t *tri, *nexttri, *triangles;
|
|
|
|
|
|
|
|
triangles = NULL;
|
|
|
|
for (i = 1; i < aasworld.numareas; i++)
|
|
|
|
{
|
|
|
|
//if (!(aasworld.areasettings[i].presencetype & PRESENCE_NORMAL)) continue;
|
|
|
|
for (j = 0; j < aasworld.areas[i].numfaces; j++)
|
|
|
|
{
|
|
|
|
facenum = abs(aasworld.faceindex[aasworld.areas[i].firstface + j]);
|
|
|
|
face = &aasworld.faces[facenum];
|
|
|
|
//only convert solid faces into triangles
|
|
|
|
if (!(face->faceflags & FACE_SOLID))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
if (face->frontarea == i) otherareanum = face->backarea;
|
|
|
|
else otherareanum = face->frontarea;
|
|
|
|
if (aasworld.areasettings[otherareanum].presencetype & PRESENCE_NORMAL) continue;
|
|
|
|
*/
|
|
|
|
continue;
|
|
|
|
} //end if
|
|
|
|
//
|
|
|
|
tri = TH_CreateAASFaceTriangles(face);
|
|
|
|
for (; tri; tri = nexttri)
|
|
|
|
{
|
|
|
|
nexttri = tri->next;
|
|
|
|
TH_AddTriangleToList(&triangles, tri);
|
|
|
|
} //end for
|
|
|
|
} //end if
|
|
|
|
} //end for
|
|
|
|
return triangles;
|
|
|
|
} //end of the function TH_AASToTriangleMesh
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Parameter: -
|
|
|
|
// Returns: -
|
|
|
|
// Changes Globals: -
|
|
|
|
//===========================================================================
|
|
|
|
void TH_AASToTetrahedrons(char *filename)
|
|
|
|
{
|
|
|
|
th_triangle_t *triangles, *tri, *lasttri;
|
|
|
|
int cnt;
|
|
|
|
|
|
|
|
if (!AAS_LoadAASFile(filename, 0, 0))
|
|
|
|
Error("couldn't load %s\n", filename);
|
|
|
|
|
|
|
|
//
|
|
|
|
TH_InitMaxTH();
|
|
|
|
//create a triangle mesh from the solid faces in the AAS file
|
|
|
|
triangles = TH_AASToTriangleMesh();
|
|
|
|
//
|
|
|
|
cnt = 0;
|
|
|
|
lasttri = NULL;
|
|
|
|
for (tri = triangles; tri; tri = tri->next)
|
|
|
|
{
|
|
|
|
cnt++;
|
|
|
|
if (tri->prev != lasttri) Log_Print("BAH\n");
|
|
|
|
lasttri = tri;
|
|
|
|
} //end for
|
|
|
|
Log_Print("%6d triangles\n", cnt);
|
|
|
|
//create a tetrahedral decomposition of the world bounded by triangles
|
|
|
|
TH_TetrahedralDecomposition(triangles);
|
|
|
|
//
|
|
|
|
TH_FreeMaxTH();
|
|
|
|
} //end of the function TH_AASToTetrahedrons
|