+
+colbrushf_t *Collision_NewBrushFromPlanes(mempool_t *mempool, int numoriginalplanes, const mplane_t *originalplanes, int supercontents)
+{
+ int j, k, m, w;
+ int numpointsbuf = 0, maxpointsbuf = 256, numplanesbuf = 0, maxplanesbuf = 256, numelementsbuf = 0, maxelementsbuf = 256;
+ colbrushf_t *brush;
+ colpointf_t pointsbuf[256];
+ colplanef_t planesbuf[256];
+ int elementsbuf[1024];
+ int polypointbuf[256];
+ int pmaxpoints = 64;
+ int pnumpoints;
+ double p[2][3*64];
+#if 0
+ // enable these if debugging to avoid seeing garbage in unused data
+ memset(pointsbuf, 0, sizeof(pointsbuf));
+ memset(planesbuf, 0, sizeof(planesbuf));
+ memset(elementsbuf, 0, sizeof(elementsbuf));
+ memset(polypointbuf, 0, sizeof(polypointbuf));
+ memset(p, 0, sizeof(p));
+#endif
+ // construct a collision brush (points, planes, and renderable mesh) from
+ // a set of planes, this also optimizes out any unnecessary planes (ones
+ // whose polygon is clipped away by the other planes)
+ for (j = 0;j < numoriginalplanes;j++)
+ {
+ // add the plane uniquely (no duplicates)
+ for (k = 0;k < numplanesbuf;k++)
+ if (VectorCompare(planesbuf[k].normal, originalplanes[j].normal) && planesbuf[k].dist == originalplanes[j].dist)
+ break;
+ // if the plane is a duplicate, skip it
+ if (k < numplanesbuf)
+ continue;
+ // check if there are too many and skip the brush
+ if (numplanesbuf >= maxplanesbuf)
+ {
+ Con_Print("Mod_Q3BSP_LoadBrushes: failed to build collision brush: too many planes for buffer\n");
+ return NULL;
+ }
+
+ // create a large polygon from the plane
+ w = 0;
+ PolygonD_QuadForPlane(p[w], originalplanes[j].normal[0], originalplanes[j].normal[1], originalplanes[j].normal[2], originalplanes[j].dist, 1024.0*1024.0*1024.0);
+ pnumpoints = 4;
+ // clip it by all other planes
+ for (k = 0;k < numoriginalplanes && pnumpoints && pnumpoints <= pmaxpoints;k++)
+ {
+ if (k != j)
+ {
+ // we want to keep the inside of the brush plane so we flip
+ // the cutting plane
+ PolygonD_Divide(pnumpoints, p[w], -originalplanes[k].normal[0], -originalplanes[k].normal[1], -originalplanes[k].normal[2], -originalplanes[k].dist, 1.0/32.0, pmaxpoints, p[!w], &pnumpoints, 0, NULL, NULL);
+ w = !w;
+ }
+ }
+ // if nothing is left, skip it
+ if (pnumpoints < 3)
+ {
+ //Con_Printf("Collision_NewBrushFromPlanes: warning: polygon for plane %f %f %f %f clipped away\n", originalplanes[j].normal[0], originalplanes[j].normal[1], originalplanes[j].normal[2], originalplanes[j].dist);
+ continue;
+ }
+
+ for (k = 0;k < pnumpoints;k++)
+ {
+ int l, m;
+ m = 0;
+ for (l = 0;l < numoriginalplanes;l++)
+ if (fabs(DotProduct(&p[w][k*3], originalplanes[l].normal) - originalplanes[l].dist) < 1.0/8.0)
+ m++;
+ if (m < 3)
+ break;
+ }
+ if (k < pnumpoints)
+ {
+ Con_Printf("Collision_NewBrushFromPlanes: warning: polygon point does not lie on at least 3 planes\n");
+ //return NULL;
+ }
+
+ // check if there are too many polygon vertices for buffer
+ if (pnumpoints > pmaxpoints)
+ {
+ Con_Print("Collision_NewBrushFromPlanes: failed to build collision brush: too many points for buffer\n");
+ return NULL;
+ }
+
+ // check if there are too many triangle elements for buffer
+ if (numelementsbuf + (pnumpoints - 2) * 3 > maxelementsbuf)
+ {
+ Con_Print("Collision_NewBrushFromPlanes: failed to build collision brush: too many triangle elements for buffer\n");
+ return NULL;
+ }
+
+ for (k = 0;k < pnumpoints;k++)
+ {
+ // check if there is already a matching point (no duplicates)
+ for (m = 0;m < numpointsbuf;m++)
+ if (VectorDistance2(&p[w][k*3], pointsbuf[m].v) < COLLISION_SNAP)
+ break;
+
+ // if there is no match, add a new one
+ if (m == numpointsbuf)
+ {
+ // check if there are too many and skip the brush
+ if (numpointsbuf >= maxpointsbuf)
+ {
+ Con_Print("Collision_NewBrushFromPlanes: failed to build collision brush: too many points for buffer\n");
+ return NULL;
+ }
+ // add the new one
+ VectorCopy(&p[w][k*3], pointsbuf[numpointsbuf].v);
+ numpointsbuf++;
+ }
+
+ // store the index into a buffer
+ polypointbuf[k] = m;
+ }
+
+ // add the triangles for the polygon
+ // (this particular code makes a triangle fan)
+ for (k = 0;k < pnumpoints - 2;k++)
+ {
+ elementsbuf[numelementsbuf++] = polypointbuf[0];
+ elementsbuf[numelementsbuf++] = polypointbuf[k + 1];
+ elementsbuf[numelementsbuf++] = polypointbuf[k + 2];
+ }
+
+ // add the new plane
+ VectorCopy(originalplanes[j].normal, planesbuf[numplanesbuf].normal);
+ planesbuf[numplanesbuf].dist = originalplanes[j].dist;
+ numplanesbuf++;
+ }
+
+ // validate plane distances
+ for (j = 0;j < numplanesbuf;j++)
+ {
+ float d = furthestplanedist_float(planesbuf[j].normal, pointsbuf, numpointsbuf);
+ if (fabs(planesbuf[j].dist - d) > (1.0f/32.0f))
+ Con_Printf("plane %f %f %f %f mismatches dist %f\n", planesbuf[j].normal[0], planesbuf[j].normal[1], planesbuf[j].normal[2], planesbuf[j].dist, d);
+ }
+
+ // if nothing is left, there's nothing to allocate
+ if (numelementsbuf < 12 || numplanesbuf < 4 || numpointsbuf < 4)
+ {
+ Con_Printf("Collision_NewBrushFromPlanes: failed to build collision brush: %i triangles, %i planes (input was %i planes), %i vertices\n", numelementsbuf / 3, numplanesbuf, numoriginalplanes, numpointsbuf);
+ return NULL;
+ }
+
+ // allocate the brush and copy to it
+ brush = Collision_AllocBrushFloat(mempool, numpointsbuf, numplanesbuf, numelementsbuf / 3, supercontents);
+ for (j = 0;j < brush->numpoints;j++)
+ {
+ brush->points[j].v[0] = pointsbuf[j].v[0];
+ brush->points[j].v[1] = pointsbuf[j].v[1];
+ brush->points[j].v[2] = pointsbuf[j].v[2];
+ }
+ for (j = 0;j < brush->numplanes;j++)
+ {
+ brush->planes[j].normal[0] = planesbuf[j].normal[0];
+ brush->planes[j].normal[1] = planesbuf[j].normal[1];
+ brush->planes[j].normal[2] = planesbuf[j].normal[2];
+ brush->planes[j].dist = planesbuf[j].dist;
+ }
+ for (j = 0;j < brush->numtriangles * 3;j++)
+ brush->elements[j] = elementsbuf[j];
+ VectorCopy(brush->points[0].v, brush->mins);
+ VectorCopy(brush->points[0].v, brush->maxs);
+ for (j = 1;j < brush->numpoints;j++)
+ {
+ brush->mins[0] = min(brush->mins[0], brush->points[j].v[0]);
+ brush->mins[1] = min(brush->mins[1], brush->points[j].v[1]);
+ brush->mins[2] = min(brush->mins[2], brush->points[j].v[2]);
+ brush->maxs[0] = max(brush->maxs[0], brush->points[j].v[0]);
+ brush->maxs[1] = max(brush->maxs[1], brush->points[j].v[1]);
+ brush->maxs[2] = max(brush->maxs[2], brush->points[j].v[2]);
+ }
+ brush->mins[0] -= 1;
+ brush->mins[1] -= 1;
+ brush->mins[2] -= 1;
+ brush->maxs[0] += 1;
+ brush->maxs[1] += 1;
+ brush->maxs[2] += 1;
+ Collision_ValidateBrush(brush);
+ return brush;
+}
+
+
+
+colbrushf_t *Collision_AllocBrushFloat(mempool_t *mempool, int numpoints, int numplanes, int numtriangles, int supercontents)
+{
+ colbrushf_t *brush;
+ brush = Mem_Alloc(mempool, sizeof(colbrushf_t) + sizeof(colpointf_t) * numpoints + sizeof(colplanef_t) * numplanes + sizeof(int[3]) * numtriangles);
+ brush->supercontents = supercontents;
+ brush->numplanes = numplanes;
+ brush->numpoints = numpoints;
+ brush->numtriangles = numtriangles;
+ brush->planes = (void *)(brush + 1);
+ brush->points = (void *)(brush->planes + brush->numplanes);
+ brush->elements = (void *)(brush->points + brush->numpoints);
+ return brush;
+}
+
+void Collision_CalcPlanesForPolygonBrushFloat(colbrushf_t *brush)
+{
+ int i;
+ float edge0[3], edge1[3], edge2[3], normal[3], dist, bestdist;
+ colpointf_t *p, *p2;
+
+ if (brush->numpoints == 3)
+ {
+ // optimized triangle case
+ TriangleNormal(brush->points[0].v, brush->points[1].v, brush->points[2].v, brush->planes[0].normal);
+ if (DotProduct(brush->planes[0].normal, brush->planes[0].normal) < 0.0001f)
+ {
+ // there's no point in processing a degenerate triangle (GIGO - Garbage In, Garbage Out)
+ brush->numplanes = 0;
+ return;
+ }
+ else
+ {
+ brush->numplanes = 5;
+ VectorNormalize(brush->planes[0].normal);
+ brush->planes[0].dist = DotProduct(brush->points->v, brush->planes[0].normal);
+ VectorNegate(brush->planes[0].normal, brush->planes[1].normal);
+ brush->planes[1].dist = -brush->planes[0].dist;
+ VectorSubtract(brush->points[2].v, brush->points[0].v, edge0);
+ VectorSubtract(brush->points[0].v, brush->points[1].v, edge1);
+ VectorSubtract(brush->points[1].v, brush->points[2].v, edge2);
+#if 1
+ {
+ float projectionnormal[3], projectionedge0[3], projectionedge1[3], projectionedge2[3];
+ int i, best;
+ float dist, bestdist;
+ bestdist = fabs(brush->planes[0].normal[0]);
+ best = 0;
+ for (i = 1;i < 3;i++)
+ {
+ dist = fabs(brush->planes[0].normal[i]);
+ if (bestdist < dist)
+ {
+ bestdist = dist;
+ best = i;
+ }
+ }
+ VectorClear(projectionnormal);
+ if (brush->planes[0].normal[best] < 0)
+ projectionnormal[best] = -1;
+ else
+ projectionnormal[best] = 1;
+ VectorCopy(edge0, projectionedge0);
+ VectorCopy(edge1, projectionedge1);
+ VectorCopy(edge2, projectionedge2);
+ projectionedge0[best] = 0;
+ projectionedge1[best] = 0;
+ projectionedge2[best] = 0;
+ CrossProduct(projectionedge0, projectionnormal, brush->planes[2].normal);
+ CrossProduct(projectionedge1, projectionnormal, brush->planes[3].normal);
+ CrossProduct(projectionedge2, projectionnormal, brush->planes[4].normal);
+ }
+#else
+ CrossProduct(edge0, brush->planes->normal, brush->planes[2].normal);
+ CrossProduct(edge1, brush->planes->normal, brush->planes[3].normal);
+ CrossProduct(edge2, brush->planes->normal, brush->planes[4].normal);
+#endif
+ VectorNormalize(brush->planes[2].normal);
+ VectorNormalize(brush->planes[3].normal);
+ VectorNormalize(brush->planes[4].normal);
+ brush->planes[2].dist = DotProduct(brush->points[2].v, brush->planes[2].normal);
+ brush->planes[3].dist = DotProduct(brush->points[0].v, brush->planes[3].normal);
+ brush->planes[4].dist = DotProduct(brush->points[1].v, brush->planes[4].normal);
+
+ if (developer.integer)
+ {
+ // validation code
+#if 0
+ float temp[3];
+
+ VectorSubtract(brush->points[0].v, brush->points[1].v, edge0);
+ VectorSubtract(brush->points[2].v, brush->points[1].v, edge1);
+ CrossProduct(edge0, edge1, normal);
+ VectorNormalize(normal);
+ VectorSubtract(normal, brush->planes[0].normal, temp);
+ if (VectorLength(temp) > 0.01f)
+ Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: TriangleNormal gave wrong answer (%f %f %f != correct answer %f %f %f)\n", brush->planes->normal[0], brush->planes->normal[1], brush->planes->normal[2], normal[0], normal[1], normal[2]);
+ if (fabs(DotProduct(brush->planes[1].normal, brush->planes[0].normal) - -1.0f) > 0.01f || fabs(brush->planes[1].dist - -brush->planes[0].dist) > 0.01f)
+ Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 1 (%f %f %f %f) is not opposite plane 0 (%f %f %f %f)\n", brush->planes[1].normal[0], brush->planes[1].normal[1], brush->planes[1].normal[2], brush->planes[1].dist, brush->planes[0].normal[0], brush->planes[0].normal[1], brush->planes[0].normal[2], brush->planes[0].dist);
+#if 0
+ if (fabs(DotProduct(brush->planes[2].normal, brush->planes[0].normal)) > 0.01f)
+ Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 2 (%f %f %f %f) is not perpendicular to plane 0 (%f %f %f %f)\n", brush->planes[2].normal[0], brush->planes[2].normal[1], brush->planes[2].normal[2], brush->planes[2].dist, brush->planes[0].normal[0], brush->planes[0].normal[1], brush->planes[0].normal[2], brush->planes[2].dist);
+ if (fabs(DotProduct(brush->planes[3].normal, brush->planes[0].normal)) > 0.01f)
+ Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 3 (%f %f %f %f) is not perpendicular to plane 0 (%f %f %f %f)\n", brush->planes[3].normal[0], brush->planes[3].normal[1], brush->planes[3].normal[2], brush->planes[3].dist, brush->planes[0].normal[0], brush->planes[0].normal[1], brush->planes[0].normal[2], brush->planes[3].dist);
+ if (fabs(DotProduct(brush->planes[4].normal, brush->planes[0].normal)) > 0.01f)
+ Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 4 (%f %f %f %f) is not perpendicular to plane 0 (%f %f %f %f)\n", brush->planes[4].normal[0], brush->planes[4].normal[1], brush->planes[4].normal[2], brush->planes[4].dist, brush->planes[0].normal[0], brush->planes[0].normal[1], brush->planes[0].normal[2], brush->planes[4].dist);
+ if (fabs(DotProduct(brush->planes[2].normal, edge0)) > 0.01f)
+ Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 2 (%f %f %f %f) is not perpendicular to edge 0 (%f %f %f to %f %f %f)\n", brush->planes[2].normal[0], brush->planes[2].normal[1], brush->planes[2].normal[2], brush->planes[2].dist, brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2], brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2]);
+ if (fabs(DotProduct(brush->planes[3].normal, edge1)) > 0.01f)
+ Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 3 (%f %f %f %f) is not perpendicular to edge 1 (%f %f %f to %f %f %f)\n", brush->planes[3].normal[0], brush->planes[3].normal[1], brush->planes[3].normal[2], brush->planes[3].dist, brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2], brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2]);
+ if (fabs(DotProduct(brush->planes[4].normal, edge2)) > 0.01f)
+ Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 4 (%f %f %f %f) is not perpendicular to edge 2 (%f %f %f to %f %f %f)\n", brush->planes[4].normal[0], brush->planes[4].normal[1], brush->planes[4].normal[2], brush->planes[4].dist, brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2], brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2]);
+#endif
+#endif
+ if (fabs(DotProduct(brush->points[0].v, brush->planes[0].normal) - brush->planes[0].dist) > 0.01f || fabs(DotProduct(brush->points[1].v, brush->planes[0].normal) - brush->planes[0].dist) > 0.01f || fabs(DotProduct(brush->points[2].v, brush->planes[0].normal) - brush->planes[0].dist) > 0.01f)
+ Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: edges (%f %f %f to %f %f %f to %f %f %f) off front plane 0 (%f %f %f %f)\n", brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2], brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2], brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2], brush->planes[0].normal[0], brush->planes[0].normal[1], brush->planes[0].normal[2], brush->planes[0].dist);
+ if (fabs(DotProduct(brush->points[0].v, brush->planes[1].normal) - brush->planes[1].dist) > 0.01f || fabs(DotProduct(brush->points[1].v, brush->planes[1].normal) - brush->planes[1].dist) > 0.01f || fabs(DotProduct(brush->points[2].v, brush->planes[1].normal) - brush->planes[1].dist) > 0.01f)
+ Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: edges (%f %f %f to %f %f %f to %f %f %f) off back plane 1 (%f %f %f %f)\n", brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2], brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2], brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2], brush->planes[1].normal[0], brush->planes[1].normal[1], brush->planes[1].normal[2], brush->planes[1].dist);
+ if (fabs(DotProduct(brush->points[2].v, brush->planes[2].normal) - brush->planes[2].dist) > 0.01f || fabs(DotProduct(brush->points[0].v, brush->planes[2].normal) - brush->planes[2].dist) > 0.01f)
+ Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: edge 0 (%f %f %f to %f %f %f) off front plane 2 (%f %f %f %f)\n", brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2], brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2], brush->planes[2].normal[0], brush->planes[2].normal[1], brush->planes[2].normal[2], brush->planes[2].dist);
+ if (fabs(DotProduct(brush->points[0].v, brush->planes[3].normal) - brush->planes[3].dist) > 0.01f || fabs(DotProduct(brush->points[1].v, brush->planes[3].normal) - brush->planes[3].dist) > 0.01f)
+ Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: edge 0 (%f %f %f to %f %f %f) off front plane 2 (%f %f %f %f)\n", brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2], brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2], brush->planes[3].normal[0], brush->planes[3].normal[1], brush->planes[3].normal[2], brush->planes[3].dist);
+ if (fabs(DotProduct(brush->points[1].v, brush->planes[4].normal) - brush->planes[4].dist) > 0.01f || fabs(DotProduct(brush->points[2].v, brush->planes[4].normal) - brush->planes[4].dist) > 0.01f)
+ Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: edge 0 (%f %f %f to %f %f %f) off front plane 2 (%f %f %f %f)\n", brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2], brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2], brush->planes[4].normal[0], brush->planes[4].normal[1], brush->planes[4].normal[2], brush->planes[4].dist);
+ }
+ }
+ }
+ else
+ {
+ // choose best surface normal for polygon's plane
+ bestdist = 0;
+ for (i = 0, p = brush->points + 1;i < brush->numpoints - 2;i++, p++)
+ {
+ VectorSubtract(p[-1].v, p[0].v, edge0);
+ VectorSubtract(p[1].v, p[0].v, edge1);
+ CrossProduct(edge0, edge1, normal);
+ //TriangleNormal(p[-1].v, p[0].v, p[1].v, normal);
+ dist = DotProduct(normal, normal);
+ if (i == 0 || bestdist < dist)
+ {
+ bestdist = dist;
+ VectorCopy(normal, brush->planes->normal);
+ }
+ }
+ if (bestdist < 0.0001f)
+ {
+ // there's no point in processing a degenerate triangle (GIGO - Garbage In, Garbage Out)
+ brush->numplanes = 0;
+ return;
+ }
+ else
+ {
+ brush->numplanes = brush->numpoints + 2;
+ VectorNormalize(brush->planes->normal);
+ brush->planes->dist = DotProduct(brush->points->v, brush->planes->normal);
+
+ // negate plane to create other side
+ VectorNegate(brush->planes[0].normal, brush->planes[1].normal);
+ brush->planes[1].dist = -brush->planes[0].dist;
+ for (i = 0, p = brush->points + (brush->numpoints - 1), p2 = brush->points;i < brush->numpoints;i++, p = p2, p2++)
+ {
+ VectorSubtract(p->v, p2->v, edge0);
+ CrossProduct(edge0, brush->planes->normal, brush->planes[i + 2].normal);
+ VectorNormalize(brush->planes[i + 2].normal);
+ brush->planes[i + 2].dist = DotProduct(p->v, brush->planes[i + 2].normal);
+ }
+ }
+ }
+
+ if (developer.integer)
+ {
+ // validity check - will be disabled later
+ Collision_ValidateBrush(brush);
+ for (i = 0;i < brush->numplanes;i++)
+ {
+ int j;
+ for (j = 0, p = brush->points;j < brush->numpoints;j++, p++)
+ if (DotProduct(p->v, brush->planes[i].normal) > brush->planes[i].dist + (1.0 / 32.0))
+ Con_Printf("Error in brush plane generation, plane %i\n", i);
+ }
+ }
+}
+
+colbrushf_t *Collision_AllocBrushFromPermanentPolygonFloat(mempool_t *mempool, int numpoints, float *points, int supercontents)
+{
+ colbrushf_t *brush;
+ brush = Mem_Alloc(mempool, sizeof(colbrushf_t) + sizeof(colplanef_t) * (numpoints + 2));
+ brush->supercontents = supercontents;
+ brush->numpoints = numpoints;
+ brush->numplanes = numpoints + 2;
+ brush->planes = (void *)(brush + 1);
+ brush->points = (colpointf_t *)points;
+ Host_Error("Collision_AllocBrushFromPermanentPolygonFloat: FIXME: this code needs to be updated to generate a mesh...\n");
+ return brush;
+}