+ if (sides[i] == SIDE_FRONT)
+ {
+ VectorCopy (p1, neww->points[neww->numpoints]);
+ neww->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, neww->points[neww->numpoints]);
+ neww->numpoints++;
+ }
+
+ // free the original winding
+ FreeWinding (in);
+
+ 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++;
+ }
+}
+
+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));
+ 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;
+
+ // 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);
+
+ // 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 *) ((qbyte *) 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;
+ }
+}
+
+/*
+=============
+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;
+
+ // 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);
+ 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);
+}
+
+
+static void Mod_MakePortals(void)
+{
+ portalchain = NULL;
+ Mod_RecursiveNodePortals (loadmodel->nodes);
+ Mod_FinalizePortals();
+}
+
+static void Mod_BuildSurfaceNeighbors (msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
+{
+#if 0
+ int surfnum, vertnum, vertnum2, snum, vnum, vnum2;
+ msurface_t *surf, *s;
+ float *v0, *v1, *v2, *v3;
+ for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
+ surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
+ for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
+ {
+ for (vertnum = surf->poly_numverts - 1, vertnum2 = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = surf->poly_verts;vertnum2 < surf->poly_numverts;vertnum = vertnum2, vertnum2++, v0 = v1, v1 += 3)
+ {
+ if (surf->neighborsurfaces[vertnum])
+ continue;
+ surf->neighborsurfaces[vertnum] = NULL;
+ for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
+ {
+ if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1)
+ || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1)
+ || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
+ || s == surf)
+ continue;
+ for (vnum = 0;vnum < s->poly_numverts;vnum++)
+ if (s->neighborsurfaces[vnum] == surf)
+ break;
+ if (vnum < s->poly_numverts)
+ continue;
+ for (vnum = s->poly_numverts - 1, vnum2 = 0, v2 = s->poly_verts + (s->poly_numverts - 1) * 3, v3 = s->poly_verts;vnum2 < s->poly_numverts;vnum = vnum2, vnum2++, v2 = v3, v3 += 3)
+ {
+ if (s->neighborsurfaces[vnum] == NULL
+ && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
+ || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2])))
+ {
+ surf->neighborsurfaces[vertnum] = s;
+ s->neighborsurfaces[vnum] = surf;
+ break;
+ }
+ }
+ if (vnum < s->poly_numverts)
+ break;
+ }
+ }
+ }
+#endif
+}
+
+void Mod_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
+{
+ int i, j, stylecounts[256], totalcount, remapstyles[256];
+ msurface_t *surf;
+ memset(stylecounts, 0, sizeof(stylecounts));
+ for (i = 0;i < model->nummodelsurfaces;i++)
+ {
+ surf = model->surfaces + model->firstmodelsurface + i;
+ for (j = 0;j < MAXLIGHTMAPS;j++)
+ stylecounts[surf->styles[j]]++;
+ }
+ totalcount = 0;
+ model->light_styles = 0;
+ for (i = 0;i < 255;i++)
+ {
+ if (stylecounts[i])
+ {
+ remapstyles[i] = model->light_styles++;
+ totalcount += stylecounts[i] + 1;
+ }
+ }
+ if (!totalcount)
+ return;
+ model->light_style = Mem_Alloc(mempool, model->light_styles * sizeof(qbyte));
+ model->light_stylevalue = Mem_Alloc(mempool, model->light_styles * sizeof(int));
+ model->light_styleupdatechains = Mem_Alloc(mempool, model->light_styles * sizeof(msurface_t **));
+ model->light_styleupdatechainsbuffer = Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
+ model->light_styles = 0;
+ for (i = 0;i < 255;i++)
+ if (stylecounts[i])
+ model->light_style[model->light_styles++] = i;
+ j = 0;
+ for (i = 0;i < model->light_styles;i++)
+ {
+ model->light_styleupdatechains[i] = model->light_styleupdatechainsbuffer + j;
+ j += stylecounts[model->light_style[i]] + 1;
+ }
+ for (i = 0;i < model->nummodelsurfaces;i++)
+ {
+ surf = model->surfaces + model->firstmodelsurface + i;
+ for (j = 0;j < MAXLIGHTMAPS;j++)
+ if (surf->styles[j] != 255)
+ *model->light_styleupdatechains[remapstyles[surf->styles[j]]]++ = surf;
+ }
+ j = 0;
+ for (i = 0;i < model->light_styles;i++)
+ {
+ *model->light_styleupdatechains[i] = NULL;
+ model->light_styleupdatechains[i] = model->light_styleupdatechainsbuffer + j;
+ j += stylecounts[model->light_style[i]] + 1;
+ }
+}
+
+void Mod_BuildPVSTextureChains(model_t *model)
+{
+ int i, j;
+ for (i = 0;i < model->numtextures;i++)
+ model->pvstexturechainslength[i] = 0;
+ for (i = 0, j = model->firstmodelsurface;i < model->nummodelsurfaces;i++, j++)
+ {
+ if (model->surfacepvsframes[j] == model->pvsframecount)
+ {
+ model->pvssurflist[model->pvssurflistlength++] = j;
+ model->pvstexturechainslength[model->surfaces[j].texinfo->texture->number]++;
+ }
+ }
+ for (i = 0, j = 0;i < model->numtextures;i++)
+ {
+ if (model->pvstexturechainslength[i])
+ {
+ model->pvstexturechains[i] = model->pvstexturechainsbuffer + j;
+ j += model->pvstexturechainslength[i] + 1;
+ }
+ else
+ model->pvstexturechains[i] = NULL;
+ }
+ for (i = 0, j = model->firstmodelsurface;i < model->nummodelsurfaces;i++, j++)
+ if (model->surfacepvsframes[j] == model->pvsframecount)
+ *model->pvstexturechains[model->surfaces[j].texinfo->texture->number]++ = model->surfaces + j;
+ for (i = 0;i < model->numtextures;i++)
+ {
+ if (model->pvstexturechainslength[i])
+ {
+ *model->pvstexturechains[i] = NULL;
+ model->pvstexturechains[i] -= model->pvstexturechainslength[i];
+ }