/* 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 #include "DPoint.h" #include "DPlane.h" ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// DWinding::DWinding() { numpoints = 0; p = NULL; } DWinding::~DWinding() { if (p) { delete[] p; } } ////////////////////////////////////////////////////////////////////// // Implementation ////////////////////////////////////////////////////////////////////// const int 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; }