amxmodx/hlsdk/utils/qbsp2/surfaces.c
2006-08-27 02:22:59 +00:00

475 lines
9.1 KiB
C

/***
*
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
****/
// divide.h
#include "bsp5.h"
int TexelDelta( face_t *f, dplane_t *plane );
int TexelSize( face_t *f );
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;
dplane_t plane;
face_t *front, *back, *next;
texinfo_t *tex;
vec3_t temp;
// special (non-surface cached) faces don't need subdivision
tex = &texinfo[f->texturenum];
if ( tex->flags & TEX_SPECIAL)
return;
for (axis = 0 ; axis < 2 ; axis++)
{
while (1)
{
mins = 999999;
maxs = -999999;
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], temp);
v = VectorNormalize (temp);
VectorCopy (temp, plane.normal);
plane.dist = (mins + subdivide_size - 16)/v;
next = f->next;
SplitFace (f, &plane, &front, &back);
if (!front || !back)
{
//PrintMemory();
printf("SubdivideFace: didn't split the %d-sided polygon @(%.0f,%.0f,%.0f)",
f->numpoints, f->pts[0][0], f->pts[0][1], f->pts[0][2] );
break;
}
*prevptr = back;
back->next = front;
front->next = next;
f = back;
}
}
}
int TexelDelta( face_t *f, dplane_t *plane )
{
int current, delta;
current = delta = 0;
// Does the plane split the face?
if ( FaceSide (f, plane) == SIDE_ON )
{
face_t *front, *back;
// Compute the change in texture cache from splitting the face
current = TexelSize( f );
// Speculatively split the face
SplitFaceTmp(f, plane, &front, &back);
if (!front || !back) // Didn't actually split the face
delta = 0;
else
{
delta = 0;//TexelSize( front ) + TexelSize( back ) - current; // Change in texel size
FreeFace( front ); // Free new faces
FreeFace( back );
}
}
return delta;
}
int TexelSize( face_t *f )
{
float mins, maxs;
vec_t v;
int i, smax, tmax;
dplane_t plane;
face_t *front, *back, *next;
texinfo_t *tex;
vec3_t temp;
// special (non-surface cached) faces don't need subdivision
if ( f->texturenum > numtexinfo )
{
printf("Error on face\n" );
return 0;
}
tex = &texinfo[f->texturenum];
if ( tex->flags & TEX_SPECIAL)
return 0;
mins = 999999;
maxs = -999999;
for (i=0 ; i<f->numpoints ; i++)
{
v = DotProduct (f->pts[i], tex->vecs[0]);
if (v < mins)
mins = v;
if (v > maxs)
maxs = v;
}
smax = maxs - mins;
mins = 999999;
maxs = -999999;
for (i=0 ; i<f->numpoints ; i++)
{
v = DotProduct (f->pts[i], tex->vecs[1]);
if (v < mins)
mins = v;
if (v > maxs)
maxs = v;
}
tmax = maxs - mins;
return smax * tmax;
}
//===========================================================================
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
#define POINT_EPSILON ON_EPSILON
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] - 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++;
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 = numvertexes;
if (hv->num==MAX_MAP_VERTS)
Error ("GetVertex: MAX_MAP_VERTS");
hvert_p++;
// emit a vertex
if (numvertexes == MAX_MAP_VERTS)
Error ("numvertexes == MAX_MAP_VERTS");
dvertexes[numvertexes].point[0] = vert[0];
dvertexes[numvertexes].point[1] = vert[1];
dvertexes[numvertexes].point[2] = vert[2];
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)
Error ("GetEdge: 0 contents");
c_tryedges++;
v1 = GetVertex (p1, f->planenum);
v2 = GetVertex (p2, f->planenum);
for (i=firstmodeledge ; i < numedges ; i++)
{
edge = &dedges[i];
if (v1 == edge->v[1] && v2 == edge->v[0]
&& !edgefaces[i][1]
&& edgefaces[i][0]->contents == f->contents)
{
edgefaces[i][1] = f;
return -i;
}
}
// emit an edge
if (numedges >= MAX_MAP_EDGES)
Error ("numedges == MAX_MAP_EDGES");
edge = &dedges[numedges];
numedges++;
edge->v[0] = v1;
edge->v[1] = v2;
edgefaces[i][0] = f;
return i;
}
/*
=============
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 < numedges ; i++)
{
edge = &dedges[i];
if (!edgefaces[i][1])
{
d1 = &dvertexes[edge->v[0]];
d2 = &dvertexes[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->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
================
*/
void MakeFaceEdges (node_t *headnode)
{
InitHash ();
c_tryedges = 0;
c_cornerverts = 0;
firstmodeledge = numedges;
firstmodelface = numfaces;
}