mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-05 20:50:43 +00:00
afa0b21336
`ported' to the new bspfile stuff when it comes to creating/editing a bsp.
538 lines
10 KiB
C
538 lines
10 KiB
C
/* Copyright (C) 1996-1997 Id Software, Inc.
|
|
|
|
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
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
#include <stdlib.h>
|
|
|
|
#include "QF/sys.h"
|
|
|
|
#include "bsp5.h"
|
|
|
|
|
|
surface_t newcopy_t;
|
|
|
|
/*
|
|
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
|
|
|
|
*/
|
|
|
|
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
|
|
===============
|
|
*/
|
|
void SubdivideFace (face_t *f, face_t **prevptr)
|
|
{
|
|
float mins, maxs;
|
|
vec_t v;
|
|
int axis, i;
|
|
plane_t plane;
|
|
face_t *front, *back, *next;
|
|
texinfo_t *tex;
|
|
|
|
// special (non-surface cached) faces don't need subdivision
|
|
tex = &bsp->texinfo[f->texturenum];
|
|
|
|
if ( tex->flags & TEX_SPECIAL)
|
|
return;
|
|
|
|
|
|
for (axis = 0 ; axis < 2 ; axis++)
|
|
{
|
|
while (1)
|
|
{
|
|
mins = 9999;
|
|
maxs = -9999;
|
|
|
|
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;
|
|
}
|
|
|
|
if (maxs - mins <= subdivide_size)
|
|
break;
|
|
|
|
// split it
|
|
subdivides++;
|
|
|
|
VectorCopy (tex->vecs[axis], plane.normal);
|
|
v = VectorLength (plane.normal);
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
SubdivideFaces
|
|
================
|
|
*/
|
|
void SubdivideFaces (surface_t *surfhead)
|
|
{
|
|
surface_t *surf;
|
|
face_t *f , **prevptr;
|
|
|
|
qprintf ("--- SubdivideFaces ---\n");
|
|
|
|
subdivides = 0;
|
|
|
|
for (surf = surfhead ; surf ; surf=surf->next)
|
|
{
|
|
prevptr = &surf->faces;
|
|
while (1)
|
|
{
|
|
f = *prevptr;
|
|
if (!f)
|
|
break;
|
|
SubdivideFace (f, prevptr);
|
|
f = *prevptr;
|
|
prevptr = &f->next;
|
|
}
|
|
}
|
|
|
|
qprintf ("%i faces added by subdivision\n", subdivides);
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
=============================================================================
|
|
|
|
GatherNodeFaces
|
|
|
|
Frees the current node tree and returns a new chain of the surfaces that
|
|
have inside faces.
|
|
=============================================================================
|
|
*/
|
|
|
|
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
|
|
FreeFace (f);
|
|
}
|
|
else
|
|
{
|
|
f->next = validfaces[f->planenum];
|
|
validfaces[f->planenum] = f;
|
|
}
|
|
}
|
|
|
|
GatherNodeFaces_r (node->children[0]);
|
|
GatherNodeFaces_r (node->children[1]);
|
|
|
|
free (node);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// leaf node
|
|
//
|
|
free (node);
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
GatherNodeFaces
|
|
|
|
================
|
|
*/
|
|
surface_t *GatherNodeFaces (node_t *headnode)
|
|
{
|
|
memset (validfaces, 0, sizeof(validfaces));
|
|
GatherNodeFaces_r (headnode);
|
|
return BuildSurfaces ();
|
|
}
|
|
|
|
//===========================================================================
|
|
|
|
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
|
|
|
|
int c_cornerverts;
|
|
|
|
hashvert_t hvertex[MAX_MAP_VERTS];
|
|
hashvert_t *hvert_p;
|
|
|
|
face_t *edgefaces[MAX_MAP_EDGES][2];
|
|
int firstmodeledge = 1;
|
|
int firstmodelface;
|
|
|
|
//============================================================================
|
|
|
|
#define NUM_HASH 4096
|
|
|
|
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] = newsize[1];
|
|
|
|
hvert_p = hvertex;
|
|
}
|
|
|
|
static unsigned HashVec (vec3_t vec)
|
|
{
|
|
unsigned h;
|
|
|
|
h = 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
|
|
=============
|
|
*/
|
|
int GetVertex (vec3_t in, int planenum)
|
|
{
|
|
int h;
|
|
int i;
|
|
hashvert_t *hv;
|
|
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];
|
|
}
|
|
|
|
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++;
|
|
if (hv->numplanes == 3)
|
|
return hv->num; // allready known to be a corner
|
|
for (i=0 ; i<hv->numplanes ; i++)
|
|
if (hv->planenums[i] == planenum)
|
|
return hv->num; // allready know this plane
|
|
if (hv->numplanes == 2)
|
|
c_cornerverts++;
|
|
else
|
|
hv->planenums[hv->numplanes] = planenum;
|
|
hv->numplanes++;
|
|
return hv->num;
|
|
}
|
|
}
|
|
|
|
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;
|
|
if (hv->num==MAX_MAP_VERTS)
|
|
Sys_Error ("GetVertex: MAX_MAP_VERTS");
|
|
hvert_p++;
|
|
|
|
// 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;
|
|
}
|
|
|
|
//===========================================================================
|
|
|
|
|
|
/*
|
|
==================
|
|
GetEdge
|
|
|
|
Don't allow four way edges
|
|
==================
|
|
*/
|
|
int c_tryedges;
|
|
|
|
int GetEdge (vec3_t p1, vec3_t p2, face_t *f)
|
|
{
|
|
int v1, v2;
|
|
dedge_t *edge;
|
|
int i;
|
|
|
|
if (!f->contents[0])
|
|
Sys_Error ("GetEdge: 0 contents");
|
|
|
|
c_tryedges++;
|
|
v1 = GetVertex (p1, f->planenum);
|
|
v2 = GetVertex (p2, f->planenum);
|
|
for (i=firstmodeledge ; i < bsp->numedges ; i++)
|
|
{
|
|
edge = &bsp->edges[i];
|
|
if (v1 == edge->v[1] && v2 == edge->v[0]
|
|
&& !edgefaces[i][1]
|
|
&& edgefaces[i][0]->contents[0] == f->contents[0])
|
|
{
|
|
edgefaces[i][1] = f;
|
|
return -i;
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
|
|
return i;
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
FindFaceEdges
|
|
==================
|
|
*/
|
|
void FindFaceEdges (face_t *face)
|
|
{
|
|
int i;
|
|
|
|
face->outputnumber = -1;
|
|
if (face->numpoints > MAXEDGES)
|
|
Sys_Error ("WriteFace: %i points", face->numpoints);
|
|
|
|
for (i=0; i<face->numpoints ; i++)
|
|
face->edges[i] = GetEdge
|
|
(face->pts[i], face->pts[(i+1)%face->numpoints], face);
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CheckVertexes
|
|
// debugging
|
|
=============
|
|
*/
|
|
void CheckVertexes (void)
|
|
{
|
|
int cb, c0, c1, c2, c3;
|
|
hashvert_t *hv;
|
|
|
|
cb = c0 = c1 = c2 = c3 = 0;
|
|
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++;
|
|
}
|
|
|
|
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
|
|
=============
|
|
*/
|
|
void CheckEdges (void)
|
|
{
|
|
dedge_t *edge;
|
|
int i;
|
|
dvertex_t *d1, *d2;
|
|
face_t *f1, *f2;
|
|
int c_nonconvex;
|
|
int c_multitexture;
|
|
|
|
c_nonconvex = c_multitexture = 0;
|
|
|
|
// CheckVertexes ();
|
|
|
|
for (i=1 ; i < bsp->numedges ; i++)
|
|
{
|
|
edge = &bsp->edges[i];
|
|
if (!edgefaces[i][1])
|
|
{
|
|
d1 = &bsp->vertexes[edge->v[0]];
|
|
d2 = &bsp->vertexes[edge->v[1]];
|
|
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]);
|
|
}
|
|
else
|
|
{
|
|
f1 = edgefaces[i][0];
|
|
f2 = edgefaces[i][1];
|
|
if (f1->planeside != f2->planeside)
|
|
continue;
|
|
if (f1->planenum != f2->planenum)
|
|
continue;
|
|
|
|
// on the same plane, might be discardable
|
|
if (f1->texturenum == f2->texturenum)
|
|
{
|
|
hvertex[edge->v[0]].numedges-=2;
|
|
hvertex[edge->v[1]].numedges-=2;
|
|
c_nonconvex++;
|
|
}
|
|
else
|
|
c_multitexture++;
|
|
}
|
|
}
|
|
|
|
// qprintf ("%5i edges\n", i);
|
|
// qprintf ("%5i c_nonconvex\n", c_nonconvex);
|
|
// qprintf ("%5i c_multitexture\n", c_multitexture);
|
|
|
|
// CheckVertexes ();
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
MakeFaceEdges_r
|
|
================
|
|
*/
|
|
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);
|
|
|
|
MakeFaceEdges_r (node->children[0]);
|
|
MakeFaceEdges_r (node->children[1]);
|
|
}
|
|
|
|
/*
|
|
================
|
|
MakeFaceEdges
|
|
================
|
|
*/
|
|
void MakeFaceEdges (node_t *headnode)
|
|
{
|
|
qprintf ("----- MakeFaceEdges -----\n");
|
|
|
|
InitHash ();
|
|
c_tryedges = 0;
|
|
c_cornerverts = 0;
|
|
|
|
MakeFaceEdges_r (headnode);
|
|
|
|
// CheckEdges ();
|
|
|
|
GrowNodeRegions (headnode);
|
|
|
|
firstmodeledge = bsp->numedges;
|
|
firstmodelface = bsp->numfaces;
|
|
}
|
|
|