// divide.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 = &texinfo[f->texturenum]; if ( tex->flags & tex_special) return; for (axis = 0 ; axis < 2 ; axis++) { while (1) { mins = 9999; maxs = -9999; 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], 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) 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] - 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[0]) 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[0] == f->contents[0]) { 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; } /* ================== findfaceedges ================== */ void findfaceedges (face_t *face) { int i; face->outputnumber = -1; if (face->numpoints > maxedges) error ("writeface: %i points", face->numpoints); for (i=0; inumpoints ; 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 < 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->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 = numedges; firstmodelface = numfaces; }