123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164 |
- #pragma warning(disable:4018) // '<' : signed/unsigned mismatch
- #include "bsp5.h"
- // FaceSide
- // ChooseMidPlaneFromList
- // ChoosePlaneFromList
- // SelectPartition
- // CalcSurfaceInfo
- // DivideSurface
- // SplitNodeSurfaces
- // RankForContents
- // ContentsForRank
- // FreeLeafSurfs
- // LinkLeafFaces
- // MakeNodePortal
- // SplitNodePortals
- // CalcNodeBounds
- // CopyFacesToNode
- // BuildBspTree_r
- // SolidBSP
- // Each node or leaf will have a set of portals that completely enclose
- // the volume of the node and pass into an adjacent node.
- int g_maxnode_size = DEFAULT_MAXNODE_SIZE;
- static bool g_reportProgress = false;
- static int g_numProcessed = 0;
- static int g_numReported = 0;
- static void ResetStatus(bool report_progress)
- {
- g_reportProgress = report_progress;
- g_numProcessed = g_numReported = 0;
- }
- static void UpdateStatus(void)
- {
- if(g_reportProgress)
- {
- ++g_numProcessed;
- if((g_numProcessed / 500) > g_numReported)
- {
- g_numReported = (g_numProcessed / 500);
- Log("%d...",g_numProcessed);
- }
- }
- }
- // =====================================================================================
- // FaceSide
- // For BSP hueristic
- // =====================================================================================
- static int FaceSide(face_t* in, const dplane_t* const split)
- {
- int frontcount, backcount;
- vec_t dot;
- int i;
- vec_t* p;
- frontcount = backcount = 0;
- // axial planes are fast
- if (split->type <= last_axial)
- {
- vec_t splitGtEp = split->dist + ON_EPSILON; // Invariant moved out of loop
- vec_t splitLtEp = split->dist - ON_EPSILON; // Invariant moved out of loop
- for (i = 0, p = in->pts[0] + split->type; i < in->numpoints; i++, p += 3)
- {
- if (*p > splitGtEp)
- {
- if (backcount)
- {
- return SIDE_ON;
- }
- frontcount = 1;
- }
- else if (*p < splitLtEp)
- {
- if (frontcount)
- {
- return SIDE_ON;
- }
- backcount = 1;
- }
- }
- }
- else
- {
- // sloping planes take longer
- for (i = 0, p = in->pts[0]; i < in->numpoints; i++, p += 3)
- {
- dot = DotProduct(p, split->normal);
- dot -= split->dist;
- if (dot > ON_EPSILON)
- {
- if (backcount)
- {
- return SIDE_ON;
- }
- frontcount = 1;
- }
- else if (dot < -ON_EPSILON)
- {
- if (frontcount)
- {
- return SIDE_ON;
- }
- backcount = 1;
- }
- }
- }
- if (!frontcount)
- {
- return SIDE_BACK;
- }
- if (!backcount)
- {
- return SIDE_FRONT;
- }
- return SIDE_ON;
- }
- // =====================================================================================
- // ChooseMidPlaneFromList
- // When there are a huge number of planes, just choose one closest
- // to the middle.
- // =====================================================================================
- static surface_t* ChooseMidPlaneFromList(surface_t* surfaces, const vec3_t mins, const vec3_t maxs)
- {
- int j, l;
- surface_t* p;
- surface_t* bestsurface;
- vec_t bestvalue;
- vec_t value;
- vec_t dist;
- dplane_t* plane;
- //
- // pick the plane that splits the least
- //
- bestvalue = 6 * 8192 * 8192;
- bestsurface = NULL;
- for (p = surfaces; p; p = p->next)
- {
- if (p->onnode)
- {
- continue;
- }
- plane = &g_dplanes[p->planenum];
- // check for axis aligned surfaces
- l = plane->type;
- if (l > last_axial)
- {
- continue;
- }
- //
- // calculate the split metric along axis l, smaller values are better
- //
- value = 0;
- dist = plane->dist * plane->normal[l];
- for (j = 0; j < 3; j++)
- {
- if (j == l)
- {
- value += (maxs[l] - dist) * (maxs[l] - dist);
- value += (dist - mins[l]) * (dist - mins[l]);
- }
- else
- {
- value += 2 * (maxs[j] - mins[j]) * (maxs[j] - mins[j]);
- }
- }
- if (value > bestvalue)
- {
- continue;
- }
- //
- // currently the best!
- //
- bestvalue = value;
- bestsurface = p;
- }
- if (!bestsurface)
- {
- for (p = surfaces; p; p = p->next)
- {
- if (!p->onnode)
- {
- return p; // first valid surface
- }
- }
- Error("ChooseMidPlaneFromList: no valid planes");
- }
- return bestsurface;
- }
- // =====================================================================================
- // ChoosePlaneFromList
- // Choose the plane that splits the least faces
- // =====================================================================================
- static surface_t* ChoosePlaneFromList(surface_t* surfaces, const vec3_t mins, const vec3_t maxs)
- {
- int j;
- int k;
- int l;
- surface_t* p;
- surface_t* p2;
- surface_t* bestsurface;
- vec_t bestvalue;
- vec_t bestdistribution;
- vec_t value;
- vec_t dist;
- dplane_t* plane;
- face_t* f;
- //
- // pick the plane that splits the least
- //
- #define UNDESIREABLE_HINT_FACTOR 10000
- #define WORST_VALUE 100000000
- bestvalue = WORST_VALUE;
- bestsurface = NULL;
- bestdistribution = 9e30;
- for (p = surfaces; p; p = p->next)
- {
- if (p->onnode)
- {
- continue;
- }
- #ifdef ZHLT_DETAIL
- if (g_bDetailBrushes)
- {
- // AJM: cycle though all faces, and make sure none of them are detail
- // if any of them are, this surface isnt to cause a bsp split
- for (face_t* f = p->faces; f; f = f->next)
- {
- if (f->contents == CONTENTS_DETAIL)
- {
- //Log("ChoosePlaneFromList::got a detial surface, skipping...\n");
- continue;
- }
- }
- }
- #endif
-
- plane = &g_dplanes[p->planenum];
- k = 0;
- for (p2 = surfaces; p2; p2 = p2->next)
- {
- if (p2 == p)
- {
- continue;
- }
- if (p2->onnode)
- {
- continue;
- }
- for (f = p2->faces; f; f = f->next)
- {
- // Give this face (a hint brush fragment) a large 'undesireable' value, only split when we have to)
- if (f->facestyle == face_hint)
- {
- k += UNDESIREABLE_HINT_FACTOR;
- hlassert(k < WORST_VALUE);
- if (k >= WORST_VALUE)
- {
- Warning("::ChoosePlaneFromList() surface fragmentation undesireability exceeded WORST_VALUE");
- k = WORST_VALUE - 1;
- }
- }
- if (FaceSide(f, plane) == SIDE_ON)
- {
- k++;
- if (k >= bestvalue)
- {
- break;
- }
- }
- }
- if (k > bestvalue)
- {
- break;
- }
- }
- if (k > bestvalue)
- {
- continue;
- }
- // if equal numbers, axial planes win, then decide on spatial subdivision
- if (k < bestvalue || (k == bestvalue && (plane->type <= last_axial)))
- {
- // check for axis aligned surfaces
- l = plane->type;
- if (l <= last_axial)
- { // axial aligned
- //
- // calculate the split metric along axis l
- //
- value = 0;
- for (j = 0; j < 3; j++)
- {
- if (j == l)
- {
- dist = plane->dist * plane->normal[l];
- value += (maxs[l] - dist) * (maxs[l] - dist);
- value += (dist - mins[l]) * (dist - mins[l]);
- }
- else
- {
- value += 2 * (maxs[j] - mins[j]) * (maxs[j] - mins[j]);
- }
- }
- if (value > bestdistribution && k == bestvalue)
- {
- continue;
- }
- bestdistribution = value;
- }
- //
- // currently the best!
- //
- bestvalue = k;
- bestsurface = p;
- }
- }
- return bestsurface;
- }
- // =====================================================================================
- // SelectPartition
- // Selects a surface from a linked list of surfaces to split the group on
- // returns NULL if the surface list can not be divided any more (a leaf)
- // =====================================================================================
- static surface_t* SelectPartition(surface_t* surfaces, const node_t* const node, const bool usemidsplit)
- {
- int i;
- surface_t* p;
- surface_t* bestsurface;
- //
- // count surface choices
- //
- i = 0;
- bestsurface = NULL;
- for (p = surfaces; p; p = p->next)
- {
- if (!p->onnode)
- {
- #ifdef ZHLT_DETAIL
- if (g_bDetailBrushes)
- {
- // AJM: cycle though all faces, and make sure none of them are detail
- // if any of them are, this surface isnt to cause a bsp split
- for (face_t* f = p->faces; f; f = f->next)
- {
- if (f->contents == CONTENTS_DETAIL)
- {
- //Log("SelectPartition::got a detial surface, skipping...\n");
- continue;
- }
- }
- }
- #endif
- i++;
- bestsurface = p;
- }
- }
- if (i == 0)
- {
- return NULL; // this is a leafnode
- }
- if (i == 1)
- {
- return bestsurface; // this is a final split
- }
- if (usemidsplit)
- {
- // do fast way for clipping hull
- return ChooseMidPlaneFromList(surfaces, node->mins, node->maxs);
- }
- else
- {
- // do slow way to save poly splits for drawing hull
- return ChoosePlaneFromList(surfaces, node->mins, node->maxs);
- }
- }
- // =====================================================================================
- // CalcSurfaceInfo
- // Calculates the bounding box
- // =====================================================================================
- static void CalcSurfaceInfo(surface_t* surf)
- {
- int i;
- int j;
- face_t* f;
- hlassume(surf->faces != NULL, assume_ValidPointer); // "CalcSurfaceInfo() surface without a face"
- //
- // calculate a bounding box
- //
- for (i = 0; i < 3; i++)
- {
- surf->mins[i] = 99999;
- surf->maxs[i] = -99999;
- }
- for (f = surf->faces; f; f = f->next)
- {
- if (f->contents >= 0)
- {
- Error("Bad contents");
- }
- for (i = 0; i < f->numpoints; i++)
- {
- for (j = 0; j < 3; j++)
- {
- if (f->pts[i][j] < surf->mins[j])
- {
- surf->mins[j] = f->pts[i][j];
- }
- if (f->pts[i][j] > surf->maxs[j])
- {
- surf->maxs[j] = f->pts[i][j];
- }
- }
- }
- }
- }
- // =====================================================================================
- // DivideSurface
- // =====================================================================================
- static void DivideSurface(surface_t* in, const dplane_t* const split, surface_t** front, surface_t** back)
- {
- face_t* facet;
- face_t* next;
- face_t* frontlist;
- face_t* backlist;
- face_t* frontfrag;
- face_t* backfrag;
- surface_t* news;
- dplane_t* inplane;
- inplane = &g_dplanes[in->planenum];
- // parallel case is easy
- if (inplane->normal[0] == split->normal[0]
- && inplane->normal[1] == split->normal[1]
- && inplane->normal[2] == split->normal[2])
- {
- if (inplane->dist > split->dist)
- {
- *front = in;
- *back = NULL;
- }
- else if (inplane->dist < split->dist)
- {
- *front = NULL;
- *back = in;
- }
- else
- { // split the surface into front and back
- frontlist = NULL;
- backlist = NULL;
- for (facet = in->faces; facet; facet = next)
- {
- next = facet->next;
- if (facet->planenum & 1)
- {
- facet->next = backlist;
- backlist = facet;
- }
- else
- {
- facet->next = frontlist;
- frontlist = facet;
- }
- }
- goto makesurfs;
- }
- return;
- }
- // do a real split. may still end up entirely on one side
- // OPTIMIZE: use bounding box for fast test
- frontlist = NULL;
- backlist = NULL;
- for (facet = in->faces; facet; facet = next)
- {
- next = facet->next;
- SplitFace(facet, split, &frontfrag, &backfrag);
- if (frontfrag)
- {
- frontfrag->next = frontlist;
- frontlist = frontfrag;
- }
- if (backfrag)
- {
- backfrag->next = backlist;
- backlist = backfrag;
- }
- }
- // if nothing actually got split, just move the in plane
- makesurfs:
- if (frontlist == NULL)
- {
- *front = NULL;
- *back = in;
- in->faces = backlist;
- return;
- }
- if (backlist == NULL)
- {
- *front = in;
- *back = NULL;
- in->faces = frontlist;
- return;
- }
- // stuff got split, so allocate one new surface and reuse in
- news = AllocSurface();
- *news = *in;
- news->faces = backlist;
- *back = news;
- in->faces = frontlist;
- *front = in;
- // recalc bboxes and flags
- CalcSurfaceInfo(news);
- CalcSurfaceInfo(in);
- }
- // =====================================================================================
- // SplitNodeSurfaces
- // =====================================================================================
- static void SplitNodeSurfaces(surface_t* surfaces, const node_t* const node)
- {
- surface_t* p;
- surface_t* next;
- surface_t* frontlist;
- surface_t* backlist;
- surface_t* frontfrag;
- surface_t* backfrag;
- dplane_t* splitplane;
- splitplane = &g_dplanes[node->planenum];
- frontlist = NULL;
- backlist = NULL;
- for (p = surfaces; p; p = next)
- {
- next = p->next;
- DivideSurface(p, splitplane, &frontfrag, &backfrag);
- if (frontfrag)
- {
- if (!frontfrag->faces)
- {
- Error("surface with no faces");
- }
- frontfrag->next = frontlist;
- frontlist = frontfrag;
- }
- if (backfrag)
- {
- if (!backfrag->faces)
- {
- Error("surface with no faces");
- }
- backfrag->next = backlist;
- backlist = backfrag;
- }
- }
- node->children[0]->surfaces = frontlist;
- node->children[1]->surfaces = backlist;
- }
- // =====================================================================================
- // RankForContents
- // =====================================================================================
- static int RankForContents(const int contents)
- {
- //Log("SolidBSP::RankForContents - contents type is %i ",contents);
- switch (contents)
- {
- #ifdef ZHLT_NULLTEX // AJM
- case CONTENTS_NULL:
- //Log("(null)\n");
- //return 13;
- return -2;
- #endif
- case CONTENTS_EMPTY:
- //Log("(empty)\n");
- return 0;
- case CONTENTS_WATER:
- //Log("(water)\n");
- return 1;
- case CONTENTS_TRANSLUCENT:
- //Log("(traslucent)\n");
- return 2;
- case CONTENTS_CURRENT_0:
- //Log("(current_0)\n");
- return 3;
- case CONTENTS_CURRENT_90:
- //Log("(current_90)\n");
- return 4;
- case CONTENTS_CURRENT_180:
- //Log("(current_180)\n");
- return 5;
- case CONTENTS_CURRENT_270:
- //Log("(current_270)\n");
- return 6;
- case CONTENTS_CURRENT_UP:
- //Log("(current_up)\n");
- return 7;
- case CONTENTS_CURRENT_DOWN:
- //Log("(current_down)\n");
- return 8;
- case CONTENTS_SLIME:
- //Log("(slime)\n");
- return 9;
- case CONTENTS_LAVA:
- //Log("(lava)\n");
- return 10;
- case CONTENTS_SKY:
- //Log("(sky)\n");
- return 11;
- case CONTENTS_SOLID:
- //Log("(solid)\n");
- return 12;
- #ifdef ZHLT_DETAIL
- case CONTENTS_DETAIL:
- return 13;
- //Log("(detail)\n");
- #endif
- default:
- hlassert(false);
- Error("RankForContents: bad contents %i (Possibly non-solid entity with clip-brush)", contents);
- }
- return -1;
- }
- // =====================================================================================
- // ContentsForRank
- // =====================================================================================
- static int ContentsForRank(const int rank)
- {
- switch (rank)
- {
- #ifdef ZHLT_NULLTEX // AJM
- case -2:
- return CONTENTS_NULL; // has at leat one face with null
- #endif
- case -1:
- return CONTENTS_SOLID; // no faces at all
- case 0:
- return CONTENTS_EMPTY;
- case 1:
- return CONTENTS_WATER;
- case 2:
- return CONTENTS_TRANSLUCENT;
- case 3:
- return CONTENTS_CURRENT_0;
- case 4:
- return CONTENTS_CURRENT_90;
- case 5:
- return CONTENTS_CURRENT_180;
- case 6:
- return CONTENTS_CURRENT_270;
- case 7:
- return CONTENTS_CURRENT_UP;
- case 8:
- return CONTENTS_CURRENT_DOWN;
- case 9:
- return CONTENTS_SLIME;
- case 10:
- return CONTENTS_LAVA;
- case 11:
- return CONTENTS_SKY;
- case 12:
- return CONTENTS_SOLID;
- #ifdef ZHLT_DETAIL // AJM
- case 13:
- return CONTENTS_DETAIL;
- #endif
- default:
- hlassert(false);
- Error("ContentsForRank: bad rank %i", rank);
- }
- return -1;
- }
- // =====================================================================================
- // FreeLeafSurfs
- // =====================================================================================
- static void FreeLeafSurfs(node_t* leaf)
- {
- surface_t* surf;
- surface_t* snext;
- face_t* f;
- face_t* fnext;
- for (surf = leaf->surfaces; surf; surf = snext)
- {
- snext = surf->next;
- for (f = surf->faces; f; f = fnext)
- {
- fnext = f->next;
- FreeFace(f);
- }
- FreeSurface(surf);
- }
- leaf->surfaces = NULL;
- }
- // =====================================================================================
- // LinkLeafFaces
- // Determines the contents of the leaf and creates the final list of original faces
- // that have some fragment inside this leaf
- // =====================================================================================
- #define MAX_LEAF_FACES 1024
- static void LinkLeafFaces(surface_t* planelist, node_t* leafnode)
- {
- face_t* f;
- surface_t* surf;
- int rank, r;
- int nummarkfaces;
- face_t* markfaces[MAX_LEAF_FACES];
- leafnode->faces = NULL;
- leafnode->planenum = -1;
- rank = -1;
- for (surf = planelist; surf; surf = surf->next)
- {
- for (f = surf->faces; f; f = f->next)
- {
- if ((f->contents == CONTENTS_HINT))
- {
- f->contents = CONTENTS_EMPTY;
- }
- r = RankForContents(f->contents);
- if (r > rank)
- {
- rank = r;
- }
- }
- }
- leafnode->contents = ContentsForRank(rank);
- if (leafnode->contents != CONTENTS_SOLID)
- {
- nummarkfaces = 0;
- for (surf = leafnode->surfaces; surf; surf = surf->next)
- {
- for (f = surf->faces; f; f = f->next)
- {
- hlassume(nummarkfaces < MAX_LEAF_FACES, assume_MAX_LEAF_FACES);
- markfaces[nummarkfaces++] = f->original;
- }
- }
- markfaces[nummarkfaces] = NULL; // end marker
- nummarkfaces++;
- leafnode->markfaces = (face_t**)malloc(nummarkfaces * sizeof(*leafnode->markfaces));
- memcpy(leafnode->markfaces, markfaces, nummarkfaces * sizeof(*leafnode->markfaces));
- }
- FreeLeafSurfs(leafnode);
- leafnode->surfaces = NULL;
- }
- // =====================================================================================
- // MakeNodePortal
- // Create the new portal by taking the full plane winding for the cutting plane and
- // clipping it by all of the planes from the other portals.
- // Each portal tracks the node that created it, so unused nodes can be removed later.
- // =====================================================================================
- static void MakeNodePortal(node_t* node)
- {
- portal_t* new_portal;
- portal_t* p;
- dplane_t* plane;
- dplane_t clipplane;
- Winding * w;
- int side = 0;
- plane = &g_dplanes[node->planenum];
- w = new Winding(*plane);
- new_portal = AllocPortal();
- new_portal->plane = *plane;
- new_portal->onnode = node;
- for (p = node->portals; p; p = p->next[side])
- {
- clipplane = p->plane;
- if (p->nodes[0] == node)
- {
- side = 0;
- }
- else if (p->nodes[1] == node)
- {
- clipplane.dist = -clipplane.dist;
- VectorSubtract(vec3_origin, clipplane.normal, clipplane.normal);
- side = 1;
- }
- else
- {
- Error("MakeNodePortal: mislinked portal");
- }
- w->Clip(clipplane, true);
- if (!w)
- {
- Warning("MakeNodePortal:new portal was clipped away from node@(%.0f,%.0f,%.0f)-(%.0f,%.0f,%.0f)",
- node->mins[0], node->mins[1], node->mins[2], node->maxs[0], node->maxs[1], node->maxs[2]);
- FreePortal(new_portal);
- return;
- }
- }
- new_portal->winding = w;
- AddPortalToNodes(new_portal, node->children[0], node->children[1]);
- }
- // =====================================================================================
- // SplitNodePortals
- // Move or split the portals that bound node so that the node's children have portals instead of node.
- // =====================================================================================
- static void SplitNodePortals(node_t *node)
- {
- portal_t* p;
- portal_t* next_portal;
- portal_t* new_portal;
- node_t* f;
- node_t* b;
- node_t* other_node;
- int side = 0;
- dplane_t* plane;
- Winding* frontwinding;
- Winding* backwinding;
- plane = &g_dplanes[node->planenum];
- f = node->children[0];
- b = node->children[1];
- for (p = node->portals; p; p = next_portal)
- {
- if (p->nodes[0] == node)
- {
- side = 0;
- }
- else if (p->nodes[1] == node)
- {
- side = 1;
- }
- else
- {
- Error("SplitNodePortals: mislinked portal");
- }
- next_portal = p->next[side];
- other_node = p->nodes[!side];
- RemovePortalFromNode(p, p->nodes[0]);
- RemovePortalFromNode(p, p->nodes[1]);
- // cut the portal into two portals, one on each side of the cut plane
- p->winding->Divide(*plane, &frontwinding, &backwinding);
- if (!frontwinding)
- {
- if (side == 0)
- {
- AddPortalToNodes(p, b, other_node);
- }
- else
- {
- AddPortalToNodes(p, other_node, b);
- }
- continue;
- }
- if (!backwinding)
- {
- if (side == 0)
- {
- AddPortalToNodes(p, f, other_node);
- }
- else
- {
- AddPortalToNodes(p, other_node, f);
- }
- continue;
- }
- // the winding is split
- new_portal = AllocPortal();
- *new_portal = *p;
- new_portal->winding = backwinding;
- delete p->winding;
- p->winding = frontwinding;
- if (side == 0)
- {
- AddPortalToNodes(p, f, other_node);
- AddPortalToNodes(new_portal, b, other_node);
- }
- else
- {
- AddPortalToNodes(p, other_node, f);
- AddPortalToNodes(new_portal, other_node, b);
- }
- }
- node->portals = NULL;
- }
- // =====================================================================================
- // CalcNodeBounds
- // Determines the boundaries of a node by minmaxing all the portal points, whcih
- // completely enclose the node.
- // Returns true if the node should be midsplit.(very large)
- // =====================================================================================
- static bool CalcNodeBounds(node_t* node)
- {
- int i;
- int j;
- vec_t v;
- portal_t* p;
- portal_t* next_portal;
- int side = 0;
- node->mins[0] = node->mins[1] = node->mins[2] = 9999;
- node->maxs[0] = node->maxs[1] = node->maxs[2] = -9999;
- for (p = node->portals; p; p = next_portal)
- {
- if (p->nodes[0] == node)
- {
- side = 0;
- }
- else if (p->nodes[1] == node)
- {
- side = 1;
- }
- else
- {
- Error("CalcNodeBounds: mislinked portal");
- }
- next_portal = p->next[side];
- for (i = 0; i < p->winding->m_NumPoints; i++)
- {
- for (j = 0; j < 3; j++)
- {
- v = p->winding->m_Points[i][j];
- if (v < node->mins[j])
- {
- node->mins[j] = v;
- }
- if (v > node->maxs[j])
- {
- node->maxs[j] = v;
- }
- }
- }
- }
- for (i = 0; i < 3; i++)
- {
- if (node->maxs[i] - node->mins[i] > g_maxnode_size)
- {
- return true;
- }
- }
- return false;
- }
- // =====================================================================================
- // CopyFacesToNode
- // Do a final merge attempt, then subdivide the faces to surface cache size if needed.
- // These are final faces that will be drawable in the game.
- // Copies of these faces are further chopped up into the leafs, but they will reference these originals.
- // =====================================================================================
- static void CopyFacesToNode(node_t* node, surface_t* surf)
- {
- face_t** prevptr;
- face_t* f;
- face_t* newf;
- // merge as much as possible
- MergePlaneFaces(surf);
- // subdivide large faces
- prevptr = &surf->faces;
- while (1)
- {
- f = *prevptr;
- if (!f)
- {
- break;
- }
- SubdivideFace(f, prevptr);
- f = *prevptr;
- prevptr = &f->next;
- }
- // copy the faces to the node, and consider them the originals
- node->surfaces = NULL;
- node->faces = NULL;
- for (f = surf->faces; f; f = f->next)
- {
- if (f->contents != CONTENTS_SOLID)
- {
- newf = AllocFace();
- *newf = *f;
- f->original = newf;
- newf->next = node->faces;
- node->faces = newf;
- }
- }
- }
- // =====================================================================================
- // BuildBspTree_r
- // =====================================================================================
- static void BuildBspTree_r(node_t* node)
- {
- surface_t* split;
- bool midsplit;
- surface_t* allsurfs;
- midsplit = CalcNodeBounds(node);
- split = SelectPartition(node->surfaces, node, midsplit);
- if (!split)
- { // this is a leaf node
- node->planenum = PLANENUM_LEAF;
- LinkLeafFaces(node->surfaces, node);
- return;
- }
- // these are final polygons
- split->onnode = node; // can't use again
- allsurfs = node->surfaces;
- node->planenum = split->planenum;
- node->faces = NULL;
- CopyFacesToNode(node, split);
- node->children[0] = AllocNode();
- node->children[1] = AllocNode();
- // split all the polysurfaces into front and back lists
- SplitNodeSurfaces(allsurfs, node);
- // create the portal that seperates the two children
- MakeNodePortal(node);
- // carve the portals on the boundaries of the node
- SplitNodePortals(node);
- // recursively do the children
- BuildBspTree_r(node->children[0]);
- BuildBspTree_r(node->children[1]);
- UpdateStatus();
- }
- // =====================================================================================
- // SolidBSP
- // Takes a chain of surfaces plus a split type, and returns a bsp tree with faces
- // off the nodes.
- // The original surface chain will be completely freed.
- // =====================================================================================
- node_t* SolidBSP(const surfchain_t* const surfhead, bool report_progress)
- {
- node_t* headnode;
- ResetStatus(report_progress);
- double start_time = I_FloatTime();
- if(report_progress)
- {
- Log("SolidBSP [hull %d] ",g_hullnum);
- }
- else
- {
- Verbose("----- SolidBSP -----\n");
- }
- headnode = AllocNode();
- headnode->surfaces = surfhead->surfaces;
- if (!surfhead->surfaces)
- {
- // nothing at all to build
- headnode->planenum = -1;
- headnode->contents = CONTENTS_EMPTY;
- return headnode;
- }
- // generate six portals that enclose the entire world
- MakeHeadnodePortals(headnode, surfhead->mins, surfhead->maxs);
- // recursively partition everything
- BuildBspTree_r(headnode);
- double end_time = I_FloatTime();
- if(report_progress)
- {
- Log("%d (%.2f seconds)\n",++g_numProcessed,(end_time - start_time));
- }
- return headnode;
- }
|