+
+ // free the original winding
+ FreeWinding (in);
+
+ // debugging
+ //Mem_CheckSentinels(neww);
+
+ return neww;
+}
+
+
+/*
+==================
+DivideWinding
+
+Divides a winding by a plane, producing one or two windings. The
+original winding is not damaged or freed. If only on one side, the
+returned winding will be the input winding. If on both sides, two
+new windings will be created.
+==================
+*/
+static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
+{
+ double dists[MAX_POINTS_ON_WINDING + 1];
+ int sides[MAX_POINTS_ON_WINDING + 1];
+ int counts[3];
+ double dot;
+ int i, j;
+ double *p1, *p2;
+ double mid[3];
+ winding_t *f, *b;
+ int maxpts;
+
+ counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
+
+ // determine sides for each point
+ for (i = 0;i < in->numpoints;i++)
+ {
+ dot = DotProduct (in->points[i], split->normal);
+ dot -= split->dist;
+ dists[i] = dot;
+ if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
+ else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
+ else sides[i] = SIDE_ON;
+ counts[sides[i]]++;
+ }
+ sides[i] = sides[0];
+ dists[i] = dists[0];
+
+ *front = *back = NULL;
+
+ if (!counts[0])
+ {
+ *back = in;
+ return;
+ }
+ if (!counts[1])
+ {
+ *front = in;
+ return;
+ }
+
+ maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
+
+ if (maxpts > MAX_POINTS_ON_WINDING)
+ Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
+
+ *front = f = NewWinding (maxpts);
+ *back = b = NewWinding (maxpts);
+
+ for (i = 0;i < in->numpoints;i++)
+ {
+ if (f->numpoints >= maxpts || b->numpoints >= maxpts)
+ Sys_Error ("DivideWinding: points exceeded estimate");
+
+ p1 = in->points[i];
+
+ if (sides[i] == SIDE_ON)
+ {
+ VectorCopy (p1, f->points[f->numpoints]);
+ f->numpoints++;
+ VectorCopy (p1, b->points[b->numpoints]);
+ b->numpoints++;
+ continue;
+ }
+
+ if (sides[i] == SIDE_FRONT)
+ {
+ VectorCopy (p1, f->points[f->numpoints]);
+ f->numpoints++;
+ }
+ else if (sides[i] == SIDE_BACK)
+ {
+ VectorCopy (p1, b->points[b->numpoints]);
+ b->numpoints++;
+ }
+
+ if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
+ continue;
+
+ // generate a split point
+ p2 = in->points[(i+1)%in->numpoints];
+
+ dot = dists[i] / (dists[i]-dists[i+1]);
+ for (j = 0;j < 3;j++)
+ { // avoid round off error when possible
+ if (split->normal[j] == 1)
+ mid[j] = split->dist;
+ else if (split->normal[j] == -1)
+ mid[j] = -split->dist;
+ else
+ mid[j] = p1[j] + dot*(p2[j]-p1[j]);
+ }
+
+ VectorCopy (mid, f->points[f->numpoints]);
+ f->numpoints++;
+ VectorCopy (mid, b->points[b->numpoints]);
+ b->numpoints++;
+ }
+
+ // debugging
+ //Mem_CheckSentinels(f);
+ //Mem_CheckSentinels(b);
+}
+
+typedef struct portal_s
+{
+ mplane_t plane;
+ mnode_t *nodes[2]; // [0] = front side of plane
+ struct portal_s *next[2];
+ winding_t *winding;
+ struct portal_s *chain; // all portals are linked into a list
+}
+portal_t;
+
+static portal_t *portalchain;
+
+/*
+===========
+AllocPortal
+===========
+*/
+static portal_t *AllocPortal (void)
+{
+ portal_t *p;
+ p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
+ //memset(p, 0, sizeof(portal_t));
+ p->chain = portalchain;
+ portalchain = p;
+ return p;
+}
+
+static void FreePortal(portal_t *p)
+{
+ Mem_Free(p);
+}
+
+static void Mod_RecursiveRecalcNodeBBox(mnode_t *node)
+{
+ // calculate children first
+ if (node->children[0]->contents >= 0)
+ Mod_RecursiveRecalcNodeBBox(node->children[0]);
+ if (node->children[1]->contents >= 0)
+ Mod_RecursiveRecalcNodeBBox(node->children[1]);
+
+ // make combined bounding box from children
+ node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
+ node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
+ node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
+ node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
+ node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
+ node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
+}
+
+static void Mod_FinalizePortals(void)
+{
+ int i, j, numportals, numpoints;
+ portal_t *p, *pnext;
+ mportal_t *portal;
+ mvertex_t *point;
+ mleaf_t *leaf, *endleaf;
+ winding_t *w;
+
+ //Mem_CheckSentinelsGlobal();
+
+ // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
+ leaf = loadmodel->leafs;
+ endleaf = leaf + loadmodel->numleafs;
+ for (;leaf < endleaf;leaf++)
+ {
+ VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
+ VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
+ }
+ p = portalchain;
+ while(p)
+ {
+ if (p->winding)
+ {
+ for (i = 0;i < 2;i++)
+ {
+ leaf = (mleaf_t *)p->nodes[i];
+ w = p->winding;
+ for (j = 0;j < w->numpoints;j++)
+ {
+ if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
+ if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
+ if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
+ if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
+ if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
+ if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
+ }
+ }
+ }
+ p = p->chain;
+ }
+
+ Mod_RecursiveRecalcNodeBBox(loadmodel->nodes);
+
+ //Mem_CheckSentinelsGlobal();
+
+ // tally up portal and point counts
+ p = portalchain;
+ numportals = 0;
+ numpoints = 0;
+ while(p)
+ {
+ // note: this check must match the one below or it will usually corrupt memory
+ // the nodes[0] != nodes[1] check is because leaf 0 is the shared solid leaf, it can have many portals inside with leaf 0 on both sides
+ if (p->winding && p->nodes[0] != p->nodes[1]
+ && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
+ && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
+ {
+ numportals += 2;
+ numpoints += p->winding->numpoints * 2;
+ }
+ p = p->chain;
+ }
+ loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
+ loadmodel->numportals = numportals;
+ loadmodel->portalpoints = (void *) ((long) loadmodel->portals + numportals * sizeof(mportal_t));
+ loadmodel->numportalpoints = numpoints;
+ // clear all leaf portal chains
+ for (i = 0;i < loadmodel->numleafs;i++)
+ loadmodel->leafs[i].portals = NULL;
+ // process all portals in the global portal chain, while freeing them
+ portal = loadmodel->portals;
+ point = loadmodel->portalpoints;
+ p = portalchain;
+ portalchain = NULL;
+ while (p)
+ {
+ pnext = p->chain;
+
+ if (p->winding)
+ {
+ // note: this check must match the one above or it will usually corrupt memory
+ // the nodes[0] != nodes[1] check is because leaf 0 is the shared solid leaf, it can have many portals inside with leaf 0 on both sides
+ if (p->nodes[0] != p->nodes[1]
+ && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
+ && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
+ {
+ // first make the back to front portal (forward portal)
+ portal->points = point;
+ portal->numpoints = p->winding->numpoints;
+ portal->plane.dist = p->plane.dist;
+ VectorCopy(p->plane.normal, portal->plane.normal);
+ portal->here = (mleaf_t *)p->nodes[1];
+ portal->past = (mleaf_t *)p->nodes[0];
+ // copy points
+ for (j = 0;j < portal->numpoints;j++)
+ {
+ VectorCopy(p->winding->points[j], point->position);
+ point++;
+ }
+ PlaneClassify(&portal->plane);
+
+ // link into leaf's portal chain
+ portal->next = portal->here->portals;
+ portal->here->portals = portal;
+
+ // advance to next portal
+ portal++;
+
+ // then make the front to back portal (backward portal)
+ portal->points = point;
+ portal->numpoints = p->winding->numpoints;
+ portal->plane.dist = -p->plane.dist;
+ VectorNegate(p->plane.normal, portal->plane.normal);
+ portal->here = (mleaf_t *)p->nodes[0];
+ portal->past = (mleaf_t *)p->nodes[1];
+ // copy points
+ for (j = portal->numpoints - 1;j >= 0;j--)
+ {
+ VectorCopy(p->winding->points[j], point->position);
+ point++;
+ }
+ PlaneClassify(&portal->plane);
+
+ // link into leaf's portal chain
+ portal->next = portal->here->portals;
+ portal->here->portals = portal;
+
+ // advance to next portal
+ portal++;
+ }
+ FreeWinding(p->winding);
+ }
+ FreePortal(p);
+ p = pnext;
+ }
+
+ //Mem_CheckSentinelsGlobal();
+}
+
+/*
+=============
+AddPortalToNodes
+=============
+*/
+static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
+{
+ if (!front)
+ Host_Error ("AddPortalToNodes: NULL front node");
+ if (!back)
+ Host_Error ("AddPortalToNodes: NULL back node");
+ if (p->nodes[0] || p->nodes[1])
+ Host_Error ("AddPortalToNodes: already included");
+ // note: front == back is handled gracefully, because leaf 0 is the shared solid leaf, it can often have portals with the same leaf on both sides
+
+ p->nodes[0] = front;
+ p->next[0] = (portal_t *)front->portals;
+ front->portals = (mportal_t *)p;
+
+ p->nodes[1] = back;
+ p->next[1] = (portal_t *)back->portals;
+ back->portals = (mportal_t *)p;
+}
+
+/*
+=============
+RemovePortalFromNode
+=============
+*/
+static void RemovePortalFromNodes(portal_t *portal)
+{
+ int i;
+ mnode_t *node;
+ void **portalpointer;
+ portal_t *t;
+ for (i = 0;i < 2;i++)
+ {
+ node = portal->nodes[i];
+
+ portalpointer = (void **) &node->portals;
+ while (1)
+ {
+ t = *portalpointer;
+ if (!t)
+ Host_Error ("RemovePortalFromNodes: portal not in leaf");
+
+ if (t == portal)
+ {
+ if (portal->nodes[0] == node)
+ {
+ *portalpointer = portal->next[0];
+ portal->nodes[0] = NULL;
+ }
+ else if (portal->nodes[1] == node)
+ {
+ *portalpointer = portal->next[1];
+ portal->nodes[1] = NULL;
+ }
+ else
+ Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
+ break;
+ }
+
+ if (t->nodes[0] == node)
+ portalpointer = (void **) &t->next[0];
+ else if (t->nodes[1] == node)
+ portalpointer = (void **) &t->next[1];
+ else
+ Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
+ }
+ }
+}
+
+static void Mod_RecursiveNodePortals (mnode_t *node)
+{
+ int side;
+ mnode_t *front, *back, *other_node;
+ mplane_t clipplane, *plane;
+ portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
+ winding_t *nodeportalwinding, *frontwinding, *backwinding;
+
+ // CheckLeafPortalConsistancy (node);
+
+ // if a leaf, we're done
+ if (node->contents)
+ return;
+
+ plane = node->plane;
+
+ front = node->children[0];
+ back = node->children[1];
+ if (front == back)
+ Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
+
+ // create the new portal by generating a polygon for the node plane,
+ // and clipping it by all of the other portals (which came from nodes above this one)
+ nodeportal = AllocPortal ();
+ nodeportal->plane = *node->plane;
+
+ nodeportalwinding = BaseWindingForPlane (node->plane);
+ //Mem_CheckSentinels(nodeportalwinding);
+ side = 0; // shut up compiler warning
+ for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
+ {
+ clipplane = portal->plane;
+ if (portal->nodes[0] == portal->nodes[1])
+ Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
+ if (portal->nodes[0] == node)
+ side = 0;
+ else if (portal->nodes[1] == node)
+ {
+ clipplane.dist = -clipplane.dist;
+ VectorNegate (clipplane.normal, clipplane.normal);
+ side = 1;
+ }
+ else
+ Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
+
+ nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
+ if (!nodeportalwinding)
+ {
+ printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
+ break;
+ }
+ }
+
+ if (nodeportalwinding)
+ {
+ // if the plane was not clipped on all sides, there was an error
+ nodeportal->winding = nodeportalwinding;
+ AddPortalToNodes (nodeportal, front, back);
+ }
+
+ // split the portals of this node along this node's plane and assign them to the children of this node
+ // (migrating the portals downward through the tree)
+ for (portal = (portal_t *)node->portals;portal;portal = nextportal)
+ {
+ if (portal->nodes[0] == portal->nodes[1])
+ Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
+ if (portal->nodes[0] == node)
+ side = 0;
+ else if (portal->nodes[1] == node)
+ side = 1;
+ else
+ Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
+ nextportal = portal->next[side];
+
+ other_node = portal->nodes[!side];
+ RemovePortalFromNodes (portal);
+
+ // cut the portal into two portals, one on each side of the node plane
+ DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
+
+ if (!frontwinding)
+ {
+ if (side == 0)
+ AddPortalToNodes (portal, back, other_node);
+ else
+ AddPortalToNodes (portal, other_node, back);
+ continue;
+ }
+ if (!backwinding)
+ {
+ if (side == 0)
+ AddPortalToNodes (portal, front, other_node);
+ else
+ AddPortalToNodes (portal, other_node, front);
+ continue;
+ }
+
+ // the winding is split
+ splitportal = AllocPortal ();
+ temp = splitportal->chain;
+ *splitportal = *portal;
+ splitportal->chain = temp;
+ splitportal->winding = backwinding;
+ FreeWinding (portal->winding);
+ portal->winding = frontwinding;
+
+ if (side == 0)
+ {
+ AddPortalToNodes (portal, front, other_node);
+ AddPortalToNodes (splitportal, back, other_node);
+ }
+ else
+ {
+ AddPortalToNodes (portal, other_node, front);
+ AddPortalToNodes (splitportal, other_node, back);
+ }
+ }
+
+ Mod_RecursiveNodePortals(front);
+ Mod_RecursiveNodePortals(back);
+}
+
+/*
+void Mod_MakeOutsidePortals(mnode_t *node)
+{
+ int i, j;
+ portal_t *p, *portals[6];
+ mnode_t *outside_node;
+
+ outside_node = Mem_Alloc(loadmodel->mempool, sizeof(mnode_t));
+ outside_node->contents = CONTENTS_SOLID;
+ outside_node->portals = NULL;
+
+ for (i = 0;i < 3;i++)
+ {
+ for (j = 0;j < 2;j++)
+ {
+ portals[j*3 + i] = p = AllocPortal ();
+ memset (&p->plane, 0, sizeof(mplane_t));
+ p->plane.normal[i] = j ? -1 : 1;
+ p->plane.dist = -65536;
+ p->winding = BaseWindingForPlane (&p->plane);
+ if (j)
+ AddPortalToNodes (p, outside_node, node);
+ else
+ AddPortalToNodes (p, node, outside_node);
+ }
+ }
+
+ // clip the basewindings by all the other planes
+ for (i = 0;i < 6;i++)
+ {
+ for (j = 0;j < 6;j++)
+ {
+ if (j == i)
+ continue;
+ portals[i]->winding = ClipWinding (portals[i]->winding, &portals[j]->plane, true);
+ }
+ }
+}
+*/
+
+static void Mod_MakePortals(void)
+{
+// Con_Printf("building portals for %s\n", loadmodel->name);
+
+ portalchain = NULL;
+// Mod_MakeOutsidePortals (loadmodel->nodes);
+ Mod_RecursiveNodePortals (loadmodel->nodes);
+ Mod_FinalizePortals();