ericw-tools/qbsp/surfaces.c

494 lines
11 KiB
C

/*
Copyright (C) 1996-1997 Id Software, Inc.
Copyright (C) 1997 Greg Lewis
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 2 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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
See file, 'COPYING', for details.
*/
// divide.h
#include "qbsp.h"
static hashvert_t *pHashverts;
static int iNodes;
/*
a surface has all of the faces that could be drawn on a given plane
the outside filling stage can remove some of them so a better bsp can be generated
*/
/*
===============
SubdivideFace
If the face is >256 in either texture direction, carve a valid sized
piece off and insert the remainder in the next link
===============
*/
void
SubdivideFace(face_t *f, face_t **prevptr)
{
vec_t mins, maxs;
vec_t v;
int axis, i;
plane_t plane;
face_t *front, *back, *next;
texinfo_t *tex;
vec3_t tmp;
// special (non-surface cached) faces don't need subdivision
tex = &pWorldEnt->pTexinfo[f->texturenum];
if (tex->flags & TEX_SPECIAL)
return;
for (axis = 0; axis < 2; axis++) {
while (1) {
mins = 9999;
maxs = -9999;
tmp[0] = tex->vecs[axis][0];
tmp[1] = tex->vecs[axis][1];
tmp[2] = tex->vecs[axis][2];
for (i = 0; i < f->numpoints; i++) {
v = DotProduct(f->pts[i], tmp);
if (v < mins)
mins = v;
if (v > maxs)
maxs = v;
}
if (maxs - mins <= options.dxSubdivide)
break;
// split it
VectorCopy(tmp, plane.normal);
v = VectorLength(plane.normal);
VectorNormalize(plane.normal);
plane.dist = (mins + options.dxSubdivide - 16) / v;
next = f->next;
SplitFace(f, &plane, &front, &back);
if (!front || !back)
Message(msgError, errNoPolygonSplit);
*prevptr = back;
back->next = front;
front->next = next;
f = back;
}
}
}
/*
=============================================================================
GatherNodeFaces
Frees the current node tree and returns a new chain of the surfaces that
have inside faces.
=============================================================================
*/
static void
GatherNodeFaces_r(node_t *node)
{
face_t *f, *next;
if (node->planenum != PLANENUM_LEAF) {
// decision node
for (f = node->faces; f; f = next) {
next = f->next;
if (!f->numpoints) { // face was removed outside
FreeMem(f, FACE, 1);
} else {
f->next = validfaces[f->planenum];
validfaces[f->planenum] = f;
}
}
GatherNodeFaces_r(node->children[0]);
GatherNodeFaces_r(node->children[1]);
FreeMem(node, NODE, 1);
} else {
// leaf node
FreeMem(node, NODE, 1);
}
}
/*
================
GatherNodeFaces
================
*/
surface_t *
GatherNodeFaces(node_t *headnode)
{
memset(validfaces, 0, sizeof(face_t *) * cPlanes);
GatherNodeFaces_r(headnode);
return BuildSurfaces();
}
//===========================================================================
#define POINT_EPSILON 0.01
static hashvert_t *hvert_p;
// This is a kludge. Should be pEdgeFaces[2].
static face_t **pEdgeFaces0;
static face_t **pEdgeFaces1;
static int cStartEdge;
//============================================================================
#define NUM_HASH 4096
static hashvert_t *hashverts[NUM_HASH];
static vec3_t hash_min, hash_scale;
static void
InitHash(void)
{
vec3_t size;
vec_t volume;
vec_t scale;
int newsize[2];
int i;
memset(hashverts, 0, sizeof(hashverts));
for (i = 0; i < 3; i++) {
hash_min[i] = -8000;
size[i] = 16000;
}
volume = size[0] * size[1];
scale = sqrt(volume / NUM_HASH);
newsize[0] = size[0] / scale;
newsize[1] = size[1] / scale;
hash_scale[0] = newsize[0] / size[0];
hash_scale[1] = newsize[1] / size[1];
hash_scale[2] = (vec_t)newsize[1];
hvert_p = pHashverts;
}
static unsigned
HashVec(vec3_t vec)
{
unsigned h;
h = (unsigned)(hash_scale[0] * (vec[0] - hash_min[0]) * hash_scale[2] +
hash_scale[1] * (vec[1] - hash_min[1]));
if (h >= NUM_HASH)
return NUM_HASH - 1;
return h;
}
/*
=============
GetVertex
=============
*/
static int
GetVertex(vec3_t in)
{
int h;
int i;
hashvert_t *hv;
vec3_t vert;
for (i = 0; i < 3; i++) {
if (fabs(in[i] - Q_rint(in[i])) < 0.001)
vert[i] = Q_rint(in[i]);
else
vert[i] = in[i];
}
h = HashVec(vert);
for (hv = hashverts[h]; hv; hv = hv->next) {
if (fabs(hv->point[0] - vert[0]) < POINT_EPSILON &&
fabs(hv->point[1] - vert[1]) < POINT_EPSILON &&
fabs(hv->point[2] - vert[2]) < POINT_EPSILON) {
hv->numedges++;
return hv->num;
}
}
hv = hvert_p;
hv->numedges = 1;
hv->next = hashverts[h];
hashverts[h] = hv;
VectorCopy(vert, hv->point);
hv->num = map.cTotal[BSPVERTEX];
hvert_p++;
// emit a vertex
pCurEnt->pVertices[pCurEnt->iVertices].point[0] = vert[0];
pCurEnt->pVertices[pCurEnt->iVertices].point[1] = vert[1];
pCurEnt->pVertices[pCurEnt->iVertices].point[2] = vert[2];
pCurEnt->iVertices++;
map.cTotal[BSPVERTEX]++;
if (pCurEnt->iVertices > pCurEnt->cVertices)
Message(msgError, errLowVertexCount);
return hv->num;
}
//===========================================================================
/*
==================
GetEdge
Don't allow four way edges
==================
*/
static int c_tryedges;
static int
GetEdge(vec3_t p1, vec3_t p2, face_t *f)
{
int v1, v2;
dedge_t *edge;
int i;
if (!f->contents[0])
Message(msgError, errZeroContents);
c_tryedges++;
v1 = GetVertex(p1);
v2 = GetVertex(p2);
for (i = 0; i < pCurEnt->iEdges; i++) {
edge = pCurEnt->pEdges + i;
if (v1 == edge->v[1] && v2 == edge->v[0]
&& pEdgeFaces1[i] == NULL
&& pEdgeFaces0[i]->contents[0] == f->contents[0]) {
pEdgeFaces1[i] = f;
return -(i + cStartEdge);
}
}
// emit an edge
if (pCurEnt->iEdges >= pCurEnt->cEdges)
Message(msgError, errLowEdgeCount);
edge = pCurEnt->pEdges + pCurEnt->iEdges;
pCurEnt->iEdges++;
map.cTotal[BSPEDGE]++;
edge->v[0] = v1;
edge->v[1] = v2;
pEdgeFaces0[i] = f;
return i + cStartEdge;
}
/*
==================
FindFaceEdges
==================
*/
static void
FindFaceEdges(face_t *face)
{
int i;
face->outputnumber = -1;
if (face->numpoints > MAXEDGES)
Message(msgError, errLowFacePointCount);
face->edges = AllocMem(OTHER, face->numpoints * sizeof(int), true);
for (i = 0; i < face->numpoints; i++)
face->edges[i] = GetEdge
(face->pts[i], face->pts[(i + 1) % face->numpoints], face);
}
/*
================
MakeFaceEdges_r
================
*/
static void
MakeFaceEdges_r(node_t *node)
{
face_t *f;
if (node->planenum == PLANENUM_LEAF)
return;
for (f = node->faces; f; f = f->next)
FindFaceEdges(f);
// Print progress
iNodes++;
Message(msgPercent, iNodes, splitnodes);
MakeFaceEdges_r(node->children[0]);
MakeFaceEdges_r(node->children[1]);
}
/*
==============
GrowNodeRegion_r
==============
*/
static void
GrowNodeRegion_r(node_t *node)
{
dface_t *r;
face_t *f;
int i;
if (node->planenum == PLANENUM_LEAF)
return;
node->firstface = map.cTotal[BSPFACE];
for (f = node->faces; f; f = f->next) {
// if (f->outputnumber != -1)
// continue; // allready grown into an earlier region
// emit a region
f->outputnumber = map.cTotal[BSPFACE];
r = pCurEnt->pFaces + pCurEnt->iFaces;
r->planenum = node->outputplanenum;
r->side = f->planeside;
r->texinfo = f->texturenum;
for (i = 0; i < MAXLIGHTMAPS; i++)
r->styles[i] = 255;
r->lightofs = -1;
r->firstedge = map.cTotal[BSPSURFEDGE];
for (i = 0; i < f->numpoints; i++) {
pCurEnt->pSurfedges[pCurEnt->iSurfedges] = f->edges[i];
pCurEnt->iSurfedges++;
map.cTotal[BSPSURFEDGE]++;
}
FreeMem(f->edges, OTHER, f->numpoints * sizeof(int));
r->numedges = map.cTotal[BSPSURFEDGE] - r->firstedge;
map.cTotal[BSPFACE]++;
pCurEnt->iFaces++;
}
node->numfaces = map.cTotal[BSPFACE] - node->firstface;
GrowNodeRegion_r(node->children[0]);
GrowNodeRegion_r(node->children[1]);
}
/*
==============
CountData_r
==============
*/
static void
CountData_r(node_t *node)
{
face_t *f;
if (node->planenum == PLANENUM_LEAF)
return;
for (f = node->faces; f; f = f->next) {
pCurEnt->cFaces++;
pCurEnt->cVertices += f->numpoints;
}
CountData_r(node->children[0]);
CountData_r(node->children[1]);
}
/*
================
MakeFaceEdges
================
*/
void
MakeFaceEdges(node_t *headnode)
{
int i;
void *pTemp;
Message(msgProgress, "MakeFaceEdges");
cStartEdge = 0;
for (i = 0; i < map.iEntities; i++)
cStartEdge += map.rgEntities[i].cEdges;
CountData_r(headnode);
// Guess: less than half vertices actually are unique. Add one to round up odd
// values. Remember edges are +1 in BeginBSPFile.
pCurEnt->cSurfedges = pCurEnt->cVertices;
pCurEnt->cVertices++;
pCurEnt->cVertices /= 2;
// pCurEnt->cEdges = pCurEnt->cVertices;
pCurEnt->cEdges += pCurEnt->cSurfedges;
pCurEnt->pVertices = AllocMem(BSPVERTEX, pCurEnt->cVertices, true);
pCurEnt->pEdges = AllocMem(BSPEDGE, pCurEnt->cEdges, true);
// Accessory data
pHashverts = AllocMem(HASHVERT, pCurEnt->cVertices, true);
pEdgeFaces0 = AllocMem(OTHER, sizeof(face_t *) * pCurEnt->cEdges, true);
pEdgeFaces1 = AllocMem(OTHER, sizeof(face_t *) * pCurEnt->cEdges, true);
InitHash();
c_tryedges = 0;
iNodes = 0;
MakeFaceEdges_r(headnode);
FreeMem(pHashverts, HASHVERT, pCurEnt->cVertices);
FreeMem(pEdgeFaces0, OTHER, sizeof(face_t *) * pCurEnt->cEdges);
FreeMem(pEdgeFaces1, OTHER, sizeof(face_t *) * pCurEnt->cEdges);
// Swap these...
if (pCurEnt->iVertices < pCurEnt->cVertices) {
pTemp = AllocMem(BSPVERTEX, pCurEnt->iVertices, true);
memcpy(pTemp, pCurEnt->pVertices,
rgcMemSize[BSPVERTEX] * pCurEnt->iVertices);
FreeMem(pCurEnt->pVertices, BSPVERTEX, pCurEnt->cVertices);
pCurEnt->pVertices = (dvertex_t *)pTemp;
pCurEnt->cVertices = pCurEnt->iVertices;
}
if (pCurEnt->iEdges < pCurEnt->cEdges) {
pTemp = AllocMem(BSPEDGE, pCurEnt->iEdges, true);
memcpy(pTemp, pCurEnt->pEdges, rgcMemSize[BSPEDGE] * pCurEnt->iEdges);
FreeMem(pCurEnt->pEdges, BSPEDGE, pCurEnt->cEdges);
pCurEnt->pEdges = (dedge_t *)pTemp;
pCurEnt->cEdges = pCurEnt->iEdges;
}
pCurEnt->pSurfedges = AllocMem(BSPSURFEDGE, pCurEnt->cSurfedges, true);
pCurEnt->pFaces = AllocMem(BSPFACE, pCurEnt->cFaces, true);
Message(msgProgress, "GrowRegions");
GrowNodeRegion_r(headnode);
}