gtkradiant/contrib/bobtoolz/DWinding.cpp

486 lines
9.4 KiB
C++
Raw Normal View History

/*
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;
}