--- /dev/null
+/*
+BobToolz plugin for GtkRadiant
+Copyright (C) 2001 Gordon Biggans
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+// DWinding.cpp: implementation of the DWinding class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#include "DWinding.h"
+
+#include <list>
+
+#include "DPoint.h"
+#include "DPlane.h"
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+DWinding::DWinding()
+{
+ numpoints = 0;
+ p = NULL;
+}
+
+DWinding::~DWinding()
+{
+ if(p)
+ delete[] p;
+}
+
+//////////////////////////////////////////////////////////////////////
+// Implementation
+//////////////////////////////////////////////////////////////////////
+
+#define BOGUS_RANGE 4096
+
+void DWinding::AllocWinding(int points)
+{
+ numpoints = points;
+ if(p)
+ delete[] p;
+ p = new vec3_t[points];
+}
+
+vec_t DWinding::WindingArea()
+{
+ vec3_t d1, d2, cross;
+ vec_t total;
+
+ total = 0;
+ for (int i = 2; i < numpoints ; i++)
+ {
+ VectorSubtract (p[i-1], p[0], d1);
+ VectorSubtract (p[i], p[0], d2);
+
+ CrossProduct (d1, d2, cross);
+
+ total += 0.5f * VectorLength ( cross );
+ }
+
+ return total;
+}
+
+void DWinding::RemoveColinearPoints()
+{
+ vec3_t p2[MAX_POINTS_ON_WINDING];
+
+ int nump = 0;
+ for (int i = 0; i < numpoints; i++)
+ {
+ int j = (i+1)%numpoints;
+ int k = (i+numpoints-1)%numpoints;
+
+ vec3_t v1, v2;
+ VectorSubtract (p[j], p[i], v1);
+ VectorSubtract (p[i], p[k], v2);
+ VectorNormalize(v1, v1);
+ VectorNormalize(v2, v2);
+
+ if (DotProduct(v1, v2) < 0.999)
+ {
+ VectorCopy (p[i], p2[nump]);
+ nump++;
+ }
+ }
+
+ if (nump == numpoints)
+ return;
+
+ AllocWinding(nump);
+ memcpy (p, p2, nump*sizeof(vec3_t));
+}
+
+DPlane* DWinding::WindingPlane()
+{
+ DPlane* newPlane = new DPlane(p[0], p[1], p[2], NULL);
+ return newPlane;
+}
+
+void DWinding::WindingBounds(vec3_t mins, vec3_t maxs)
+{
+ if(numpoints == 0)
+ return;
+
+ VectorCopy(mins, p[0]);
+ VectorCopy(maxs, p[0]);
+
+ for (int i = 1; i < numpoints ;i++)
+ {
+ for (int j = 0; j < 3; j++)
+ {
+ vec_t v = p[i][j];
+ if (v < mins[j])
+ mins[j] = v;
+ if (v > maxs[j])
+ maxs[j] = v;
+ }
+ }
+}
+
+void DWinding::WindingCentre(vec3_t centre)
+{
+ VectorCopy (vec3_origin, centre);
+ for (int i = 0; i < numpoints; i++)
+ VectorAdd (p[i], centre, centre);
+
+ float scale = 1.0f/numpoints;
+ VectorScale (centre, scale, centre);
+}
+
+
+DWinding* DWinding::CopyWinding()
+{
+ DWinding* c = new DWinding;
+ c->AllocWinding(numpoints);
+ memcpy (c->p, p, numpoints*sizeof(vec3_t));
+ return c;
+}
+
+
+int DWinding::WindingOnPlaneSide(vec3_t normal, vec_t dist)
+{
+ bool front = false;
+ bool back = false;
+
+ for (int i = 0; i < numpoints; i++)
+ {
+ vec_t d = DotProduct (p[i], normal) - dist;
+ if (d < -ON_EPSILON)
+ {
+ if (front)
+ return SIDE_CROSS;
+ back = true;
+ continue;
+ }
+ if (d > ON_EPSILON)
+ {
+ if (back)
+ return SIDE_CROSS;
+ front = true;
+ continue;
+ }
+ }
+
+ if (back)
+ return SIDE_BACK;
+ if (front)
+ return SIDE_FRONT;
+ return SIDE_ON;
+}
+
+void DWinding::CheckWinding()
+{
+ vec_t *p1, *p2;
+ vec_t edgedist;
+ vec3_t dir, edgenormal;
+
+ if (numpoints < 3)
+ globalOutputStream() << "CheckWinding: " << numpoints << " points\n";
+
+ vec_t area = WindingArea();
+ if (area < 1)
+ globalOutputStream() << "CheckWinding: " << area << " area\n";
+
+ DPlane* wPlane = WindingPlane ();
+ int i;
+ for (i = 0; i < numpoints; i++)
+ {
+ p1 = p[i];
+
+ int j;
+ for (j = 0; j < 3; j++)
+ if (p1[j] > BOGUS_RANGE || p1[j] < -BOGUS_RANGE)
+ globalOutputStream() << "CheckFace: BOGUS_RANGE: " << p1[j] << "\n";
+
+ j = i + 1 == numpoints ? 0 : i + 1;
+
+ // check the point is on the face plane
+ vec_t d = DotProduct (p1, wPlane->normal) - wPlane->_d;
+ if (d < -ON_EPSILON || d > ON_EPSILON)
+ globalOutputStream() << "CheckWinding: point off plane\n";
+
+ // check the edge isnt degenerate
+ p2 = p[j];
+ VectorSubtract (p2, p1, dir);
+
+ if (VectorLength (dir) < ON_EPSILON)
+ globalOutputStream() << "CheckWinding: degenerate edge\n";
+
+ CrossProduct (wPlane->normal, dir, edgenormal);
+ VectorNormalize (edgenormal, edgenormal);
+ edgedist = DotProduct (p1, edgenormal);
+
+ // all other points must be on front side
+ for (j = 0 ; j < numpoints ; j++)
+ {
+ if (j == i)
+ continue;
+
+ d = DotProduct (p[j], edgenormal);
+ if (d > (edgedist + ON_EPSILON))
+ globalOutputStream() << "CheckWinding: non-convex\n";
+ }
+ }
+
+ delete wPlane;
+}
+
+DWinding* DWinding::ReverseWinding()
+{
+ DWinding* c = new DWinding;
+ c->AllocWinding(numpoints);
+
+ for (int i = 0; i < numpoints ; i++)
+ VectorCopy (p[numpoints-1-i], c->p[i]);
+
+ return c;
+}
+
+bool DWinding::ChopWindingInPlace(DPlane* chopPlane, vec_t epsilon)
+{
+ vec_t dists[MAX_POINTS_ON_WINDING+4];
+ int sides[MAX_POINTS_ON_WINDING+4];
+ int counts[3];
+ vec_t *p1, *p2;
+ vec3_t mid;
+
+ counts[0] = counts[1] = counts[2] = 0;
+
+// determine sides for each point
+ int i;
+ for (i = 0; i < numpoints; i++)
+ {
+ vec_t dot = DotProduct (p[i], chopPlane->normal);
+ dot -= chopPlane->_d;
+ dists[i] = dot;
+
+ if (dot > epsilon)
+ sides[i] = SIDE_FRONT;
+ else if (dot < -epsilon)
+ sides[i] = SIDE_BACK;
+ else
+ sides[i] = SIDE_ON;
+
+ counts[sides[i]]++;
+ }
+ sides[i] = sides[0];
+ dists[i] = dists[0];
+
+ if (!counts[0])
+ {
+ delete this;
+ return false;
+ }
+
+ if (!counts[1])
+ return true;
+
+ int maxpts = numpoints+4; // cant use counts[0]+2 because
+ // of fp grouping errors
+
+ DWinding* f = new DWinding;
+ f->AllocWinding(maxpts);
+ f->numpoints = 0;
+
+ for (i = 0; i < numpoints; i++)
+ {
+ p1 = p[i];
+
+ if (sides[i] == SIDE_ON)
+ {
+ VectorCopy (p1, f->p[f->numpoints]);
+ f->numpoints++;
+ continue;
+ }
+
+ if (sides[i] == SIDE_FRONT)
+ {
+ VectorCopy (p1, f->p[f->numpoints]);
+ f->numpoints++;
+ }
+
+ if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
+ continue;
+
+ // generate a split point
+ p2 = p[(i+1)%numpoints];
+
+ vec_t dot = dists[i] / (dists[i]-dists[i+1]);
+ for (int j = 0; j < 3; j++)
+ {
+ if (chopPlane->normal[j] == 1)
+ mid[j] = chopPlane->_d;
+ else if (chopPlane->normal[j] == -1)
+ mid[j] = -chopPlane->_d;
+ else
+ mid[j] = p1[j] + dot*(p2[j]-p1[j]);
+ }
+
+ VectorCopy (mid, f->p[f->numpoints]);
+ f->numpoints++;
+ }
+
+ if (f->numpoints > maxpts)
+ globalOutputStream() << "ClipWinding: points exceeded estimate\n";
+ if (f->numpoints > MAX_POINTS_ON_WINDING)
+ globalOutputStream() << "ClipWinding: MAX_POINTS_ON_WINDING\n";
+
+ delete[] p;
+ p = f->p;
+ f->p = NULL;
+ delete f;
+ return true;
+}
+
+void DWinding::ClipWindingEpsilon(DPlane* chopPlane, vec_t epsilon, DWinding **front, DWinding **back)
+{
+ vec_t dists[MAX_POINTS_ON_WINDING+4];
+ int sides[MAX_POINTS_ON_WINDING+4];
+ int counts[3];
+ vec_t *p1, *p2;
+ vec3_t mid;
+
+ counts[0] = counts[1] = counts[2] = 0;
+
+// determine sides for each point
+ int i;
+ for (i = 0; i < numpoints; i++)
+ {
+ vec_t dot = -chopPlane->DistanceToPoint(p[i]);
+ dists[i] = dot;
+
+ if (dot > epsilon)
+ sides[i] = SIDE_FRONT;
+ else if (dot < -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 = CopyWinding();
+ return;
+ }
+ if (!counts[1])
+ {
+ *front = CopyWinding();
+ return;
+ }
+
+ int maxpts = numpoints+4; // cant use counts[0]+2 because
+ // of fp grouping errors
+
+ DWinding* f = new DWinding;
+ DWinding* b = new DWinding;
+
+ f->AllocWinding(maxpts);
+ f->numpoints = 0;
+
+ b->AllocWinding(maxpts);
+ b->numpoints = 0;
+
+ *front = f;
+ *back = b;
+
+ for (i = 0; i < numpoints ; i++)
+ {
+ p1 = p[i];
+
+ if (sides[i] == SIDE_ON)
+ {
+ VectorCopy (p1, f->p[f->numpoints]);
+ f->numpoints++;
+ VectorCopy (p1, b->p[b->numpoints]);
+ b->numpoints++;
+ continue;
+ }
+
+ if (sides[i] == SIDE_FRONT)
+ {
+ VectorCopy (p1, f->p[f->numpoints]);
+ f->numpoints++;
+ }
+ if (sides[i] == SIDE_BACK)
+ {
+ VectorCopy (p1, b->p[b->numpoints]);
+ b->numpoints++;
+ }
+
+ if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
+ continue;
+
+ // generate a split point
+ p2 = p[(i+1)%numpoints];
+
+ vec_t dot = dists[i] / (dists[i]-dists[i+1]);
+ for (int j = 0; j < 3; j++)
+ {
+ if (chopPlane->normal[j] == 1)
+ mid[j] = chopPlane->_d;
+ else if (chopPlane->normal[j] == -1)
+ mid[j] = -chopPlane->_d;
+ else
+ mid[j] = p1[j] + dot*(p2[j]-p1[j]);
+ }
+
+ VectorCopy (mid, f->p[f->numpoints]);
+ f->numpoints++;
+ VectorCopy (mid, b->p[b->numpoints]);
+ b->numpoints++;
+ }
+
+ if (f->numpoints > maxpts || b->numpoints > maxpts)
+ globalOutputStream() << "ClipWinding: points exceeded estimate\n";
+ if (f->numpoints > MAX_POINTS_ON_WINDING || b->numpoints > MAX_POINTS_ON_WINDING)
+ globalOutputStream() << "ClipWinding: MAX_POINTS_ON_WINDING\n";
+}
+
+bool DWinding::ChopWinding(DPlane* chopPlane)
+{
+ DWinding *f, *b;
+
+ ClipWindingEpsilon (chopPlane, (float)ON_EPSILON, &f, &b);
+
+ if (b)
+ delete (b);
+
+
+ if(!f)
+ {
+ delete this;
+ return false;
+ }
+
+ delete[] p;
+ p = f->p;
+ f->p = NULL;
+ numpoints = f->numpoints;
+ delete f;
+
+ return true;
+}