/*** * * 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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; 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[1]-vert[1])point[2]-vert[2])numedges++; if (hv->numplanes == 3) return hv->num; // allready known to be a corner for (i=0 ; inumplanes ; 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; }