quakeforge/tools/qfbsp/source/surfaces.c

477 lines
9.7 KiB
C
Raw Normal View History

2002-09-19 18:51:19 +00:00
/*
Copyright (C) 1996-1997 Id Software, Inc.
2002-09-19 18:51:19 +00:00
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.
2002-09-19 18:51:19 +00:00
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.
2002-09-19 18:51:19 +00:00
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
2002-09-19 18:51:19 +00:00
See file, 'COPYING', for details.
*/
// divide.h
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdlib.h>
#include "QF/sys.h"
#include "bsp5.h"
/*
a surface has all of the faces that could be drawn on a given plane
2002-09-19 18:51:19 +00:00
the outside filling stage can remove some of them so a better bsp can be
generated
*/
2002-09-19 18:51:19 +00:00
surface_t newcopy_t;
2002-09-19 17:14:23 +00:00
int subdivides;
/*
===============
SubdivideFace
If the face is >256 in either texture direction, carve a valid sized
piece off and insert the remainder in the next link
===============
*/
2002-09-19 17:14:23 +00:00
void
2002-09-19 18:51:19 +00:00
SubdivideFace (face_t *f, face_t **prevptr)
{
2002-09-19 18:51:19 +00:00
face_t *front, *back, *next;
2002-09-19 17:14:23 +00:00
float mins, maxs;
int axis, i;
plane_t plane;
texinfo_t *tex;
2002-09-19 18:51:19 +00:00
vec_t v;
// special (non-surface cached) faces don't need subdivision
tex = &bsp->texinfo[f->texturenum];
2002-09-19 17:14:23 +00:00
if (tex->flags & TEX_SPECIAL)
return;
2002-09-19 17:14:23 +00:00
for (axis = 0; axis < 2; axis++) {
while (1) {
mins = 9999;
maxs = -9999;
2002-09-19 17:14:23 +00:00
for (i = 0; i < f->numpoints; i++) {
v = DotProduct (f->pts[i], tex->vecs[axis]);
if (v < mins)
mins = v;
if (v > maxs)
maxs = v;
}
2002-09-19 17:14:23 +00:00
if (maxs - mins <= subdivide_size)
break;
2002-09-19 17:14:23 +00:00
// split it
subdivides++;
2002-09-19 17:14:23 +00:00
VectorCopy (tex->vecs[axis], plane.normal);
v = VectorLength (plane.normal);
2002-09-19 17:14:23 +00:00
VectorNormalize (plane.normal);
plane.dist = (mins + subdivide_size - 16) / v;
next = f->next;
SplitFace (f, &plane, &front, &back);
if (!front || !back)
Sys_Error ("SubdivideFace: didn't split the polygon");
*prevptr = back;
back->next = front;
front->next = next;
f = back;
}
}
}
2002-09-19 17:14:23 +00:00
void
2002-09-19 18:51:19 +00:00
SubdivideFaces (surface_t *surfhead)
{
2002-09-19 17:14:23 +00:00
face_t *f, **prevptr;
2002-09-19 18:51:19 +00:00
surface_t *surf;
qprintf ("--- SubdivideFaces ---\n");
subdivides = 0;
2002-09-19 17:14:23 +00:00
for (surf = surfhead; surf; surf = surf->next) {
prevptr = &surf->faces;
2002-09-19 17:14:23 +00:00
while (1) {
f = *prevptr;
if (!f)
break;
SubdivideFace (f, prevptr);
f = *prevptr;
prevptr = &f->next;
}
}
qprintf ("%i faces added by subdivision\n", subdivides);
2002-09-19 17:14:23 +00:00
}
/*
=============================================================================
GatherNodeFaces
Frees the current node tree and returns a new chain of the surfaces that
have inside faces.
=============================================================================
*/
2002-09-19 17:14:23 +00:00
void
2002-09-19 18:51:19 +00:00
GatherNodeFaces_r (node_t *node)
{
2002-09-19 18:51:19 +00:00
face_t *next, *f;
2002-09-19 17:14:23 +00:00
if (node->planenum != PLANENUM_LEAF) {
// decision node
2002-09-19 17:14:23 +00:00
for (f = node->faces; f; f = next) {
next = f->next;
2002-09-19 17:14:23 +00:00
if (!f->numpoints) { // face was removed outside
FreeFace (f);
2002-09-19 17:14:23 +00:00
} else {
f->next = validfaces[f->planenum];
validfaces[f->planenum] = f;
}
}
2002-09-19 17:14:23 +00:00
GatherNodeFaces_r (node->children[0]);
GatherNodeFaces_r (node->children[1]);
2002-09-19 17:14:23 +00:00
free (node);
2002-09-19 17:14:23 +00:00
} else {
// leaf node
free (node);
}
}
2002-09-19 17:14:23 +00:00
surface_t *
2002-09-19 18:51:19 +00:00
GatherNodeFaces (node_t *headnode)
{
2002-09-19 17:14:23 +00:00
memset (validfaces, 0, sizeof (validfaces));
GatherNodeFaces_r (headnode);
2002-09-19 17:14:23 +00:00
return BuildSurfaces ();
}
//===========================================================================
2002-09-19 17:14:23 +00:00
typedef struct hashvert_s {
struct hashvert_s *next;
vec3_t point;
int num;
int numplanes; // for corner determination
int planenums[2];
int numedges;
} hashvert_t;
#define POINT_EPSILON 0.01
2002-09-19 17:14:23 +00:00
int c_cornerverts;
2002-09-19 17:14:23 +00:00
hashvert_t hvertex[MAX_MAP_VERTS];
hashvert_t *hvert_p;
2002-09-19 17:14:23 +00:00
face_t *edgefaces[MAX_MAP_EDGES][2];
int firstmodeledge = 1;
int firstmodelface;
//============================================================================
#define NUM_HASH 4096
2002-09-19 17:14:23 +00:00
hashvert_t *hashverts[NUM_HASH];
2002-09-19 17:14:23 +00:00
static vec3_t hash_min, hash_scale;
2002-09-19 17:14:23 +00:00
static void
InitHash (void)
{
2002-09-19 17:14:23 +00:00
int i;
2002-09-19 18:51:19 +00:00
int newsize[2];
vec3_t size;
vec_t scale, volume;
2002-09-19 17:14:23 +00:00
memset (hashverts, 0, sizeof (hashverts));
for (i = 0; i < 3; i++) {
hash_min[i] = -8000;
size[i] = 16000;
}
2002-09-19 17:14:23 +00:00
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] = newsize[1];
2002-09-19 17:14:23 +00:00
hvert_p = hvertex;
}
2002-09-19 17:14:23 +00:00
static unsigned
HashVec (vec3_t vec)
{
2002-09-19 17:14:23 +00:00
unsigned h;
h = hash_scale[0] * (vec[0] - hash_min[0]) * hash_scale[2]
+ hash_scale[1] * (vec[1] - hash_min[1]);
2002-09-19 17:14:23 +00:00
if (h >= NUM_HASH)
return NUM_HASH - 1;
return h;
}
2002-09-19 17:14:23 +00:00
int
GetVertex (vec3_t in, int planenum)
{
2002-09-19 17:14:23 +00:00
hashvert_t *hv;
2002-09-19 18:51:19 +00:00
int h, i;
2002-09-19 17:14:23 +00:00
vec3_t vert;
for (i = 0; i < 3; i++) {
if (fabs (in[i] - (int) (in[i] + 0.5)) < 0.001)
vert[i] = (int) (in[i] + 0.5);
else
vert[i] = in[i];
}
2002-09-19 17:14:23 +00:00
h = HashVec (vert);
2002-09-19 17:14:23 +00:00
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++;
if (hv->numplanes == 3)
2002-09-19 18:51:19 +00:00
return hv->num; // already known to be a corner
2002-09-19 17:14:23 +00:00
for (i = 0; i < hv->numplanes; i++)
if (hv->planenums[i] == planenum)
2002-09-19 18:51:19 +00:00
return hv->num; // already know this plane
if (hv->numplanes == 2)
c_cornerverts++;
else
hv->planenums[hv->numplanes] = planenum;
hv->numplanes++;
return hv->num;
}
}
2002-09-19 17:14:23 +00:00
hv = hvert_p;
hv->numedges = 1;
hv->numplanes = 1;
hv->planenums[0] = planenum;
hv->next = hashverts[h];
hashverts[h] = hv;
VectorCopy (vert, hv->point);
hv->num = bsp->numvertexes;
2002-09-19 17:14:23 +00:00
if (hv->num == MAX_MAP_VERTS)
Sys_Error ("GetVertex: MAX_MAP_VERTS");
hvert_p++;
2002-09-19 17:14:23 +00:00
// emit a vertex
if (bsp->numvertexes == MAX_MAP_VERTS)
Sys_Error ("numvertexes == MAX_MAP_VERTS");
bsp->vertexes[bsp->numvertexes].point[0] = vert[0];
bsp->vertexes[bsp->numvertexes].point[1] = vert[1];
bsp->vertexes[bsp->numvertexes].point[2] = vert[2];
bsp->numvertexes++;
return hv->num;
}
//===========================================================================
2002-09-19 18:51:19 +00:00
int c_tryedges;
/*
==================
GetEdge
Don't allow four way edges
==================
*/
2002-09-19 17:14:23 +00:00
int
2002-09-19 18:51:19 +00:00
GetEdge (vec3_t p1, vec3_t p2, face_t *f)
{
2002-09-19 17:14:23 +00:00
dedge_t *edge;
2002-09-19 18:51:19 +00:00
int v1, v2, i;
if (!f->contents[0])
Sys_Error ("GetEdge: 0 contents");
2002-09-19 17:14:23 +00:00
c_tryedges++;
v1 = GetVertex (p1, f->planenum);
v2 = GetVertex (p2, f->planenum);
2002-09-19 17:14:23 +00:00
for (i = firstmodeledge; i < bsp->numedges; i++) {
edge = &bsp->edges[i];
if (v1 == edge->v[1] && v2 == edge->v[0]
2002-09-19 17:14:23 +00:00
&& !edgefaces[i][1]
&& edgefaces[i][0]->contents[0] == f->contents[0]) {
edgefaces[i][1] = f;
return -i;
}
}
2002-09-19 17:14:23 +00:00
// emit an edge
if (bsp->numedges == MAX_MAP_EDGES)
Sys_Error ("numedges == MAX_MAP_EDGES");
edge = &bsp->edges[bsp->numedges];
bsp->numedges++;
edge->v[0] = v1;
edge->v[1] = v2;
edgefaces[i][0] = f;
2002-09-19 17:14:23 +00:00
return i;
}
2002-09-19 17:14:23 +00:00
void
2002-09-19 18:51:19 +00:00
FindFaceEdges (face_t *face)
{
2002-09-19 17:14:23 +00:00
int i;
2002-09-19 17:14:23 +00:00
face->outputnumber = -1;
if (face->numpoints > MAXEDGES)
Sys_Error ("WriteFace: %i points", face->numpoints);
2002-09-19 17:14:23 +00:00
for (i = 0; i < face->numpoints; i++)
face->edges[i] = GetEdge
(face->pts[i], face->pts[(i + 1) % face->numpoints], face);
}
/*
=============
CheckVertexes
// debugging
=============
*/
2002-09-19 17:14:23 +00:00
void
CheckVertexes (void)
{
2002-09-19 17:14:23 +00:00
hashvert_t *hv;
2002-09-19 18:51:19 +00:00
int cb, c0, c1, c2, c3;
2002-09-19 17:14:23 +00:00
cb = c0 = c1 = c2 = c3 = 0;
2002-09-19 17:14:23 +00:00
for (hv = hvertex; hv != hvert_p; hv++) {
if (hv->numedges < 0 || hv->numedges & 1)
cb++;
else if (!hv->numedges)
c0++;
else if (hv->numedges == 2)
c1++;
else if (hv->numedges == 4)
c2++;
else
c3++;
}
2002-09-19 17:14:23 +00:00
qprintf ("%5i bad edge points\n", cb);
qprintf ("%5i 0 edge points\n", c0);
qprintf ("%5i 2 edge points\n", c1);
qprintf ("%5i 4 edge points\n", c2);
qprintf ("%5i 6+ edge points\n", c3);
}
/*
=============
CheckEdges
// debugging
=============
*/
2002-09-19 17:14:23 +00:00
void
CheckEdges (void)
{
2002-09-19 17:14:23 +00:00
dedge_t *edge;
dvertex_t *d1, *d2;
face_t *f1, *f2;
2002-09-19 18:51:19 +00:00
int c_multitexture, c_nonconvex, i;
2002-09-19 17:14:23 +00:00
c_nonconvex = c_multitexture = 0;
2002-09-19 17:14:23 +00:00
2002-09-19 18:51:19 +00:00
// CheckVertexes ();
2002-09-19 17:14:23 +00:00
for (i = 1; i < bsp->numedges; i++) {
edge = &bsp->edges[i];
2002-09-19 17:14:23 +00:00
if (!edgefaces[i][1]) {
d1 = &bsp->vertexes[edge->v[0]];
d2 = &bsp->vertexes[edge->v[1]];
2002-09-19 18:51:19 +00:00
qprintf ("unshared edge at: (%8.2f, %8.2f, %8.2f) (%8.2f, %8.2f, "
"%8.2f)\n", d1->point[0], d1->point[1], d1->point[2],
d2->point[0], d2->point[1], d2->point[2]);
2002-09-19 17:14:23 +00:00
} else {
f1 = edgefaces[i][0];
f2 = edgefaces[i][1];
if (f1->planeside != f2->planeside)
continue;
if (f1->planenum != f2->planenum)
continue;
2002-09-19 17:14:23 +00:00
// on the same plane, might be discardable
2002-09-19 17:14:23 +00:00
if (f1->texturenum == f2->texturenum) {
hvertex[edge->v[0]].numedges -= 2;
hvertex[edge->v[1]].numedges -= 2;
c_nonconvex++;
2002-09-19 17:14:23 +00:00
} else
c_multitexture++;
}
}
2002-09-19 18:51:19 +00:00
// qprintf ("%5i edges\n", i);
// qprintf ("%5i c_nonconvex\n", c_nonconvex);
// qprintf ("%5i c_multitexture\n", c_multitexture);
2002-09-19 18:51:19 +00:00
// CheckVertexes ();
}
2002-09-19 17:14:23 +00:00
void
2002-09-19 18:51:19 +00:00
MakeFaceEdges_r (node_t *node)
{
2002-09-19 17:14:23 +00:00
face_t *f;
if (node->planenum == PLANENUM_LEAF)
return;
2002-09-19 17:14:23 +00:00
for (f = node->faces; f; f = f->next)
FindFaceEdges (f);
2002-09-19 17:14:23 +00:00
MakeFaceEdges_r (node->children[0]);
MakeFaceEdges_r (node->children[1]);
}
2002-09-19 17:14:23 +00:00
void
2002-09-19 18:51:19 +00:00
MakeFaceEdges (node_t *headnode)
{
qprintf ("----- MakeFaceEdges -----\n");
2002-09-19 17:14:23 +00:00
InitHash ();
c_tryedges = 0;
c_cornerverts = 0;
2002-09-19 17:14:23 +00:00
MakeFaceEdges_r (headnode);
2002-09-19 18:51:19 +00:00
// CheckEdges ();
GrowNodeRegions (headnode);
firstmodeledge = bsp->numedges;
firstmodelface = bsp->numfaces;
}