#pragma warning(disable:4018) //amckern - 64bit - '<' Singed/Unsigned Mismatch #include "winding.h" #include "cmdlib.h" #include "log.h" #include "mathlib.h" #include "hlassert.h" #undef BOGUS_RANGE #undef ON_EPSILON #define BOGUS_RANGE 10000.0 #define ON_EPSILON 0.01 // // Winding Public Methods // void Winding::Print() const { UINT32 x; for (x=0; x= 3); if (m_NumPoints >= 3) { VectorSubtract(m_Points[2], m_Points[1], v1); VectorSubtract(m_Points[0], m_Points[1], v2); CrossProduct(v2, v1, plane_normal); VectorNormalize(plane_normal); VectorCopy(plane_normal, plane.normal); // change from vec_t plane.dist = DotProduct(m_Points[0], plane.normal); } else { VectorClear(plane.normal); plane.dist = 0.0; } } void Winding::getPlane(vec3_t& normal, vec_t& dist) const { vec3_t v1, v2; //hlassert(m_NumPoints >= 3); if (m_NumPoints >= 3) { VectorSubtract(m_Points[1], m_Points[0], v1); VectorSubtract(m_Points[2], m_Points[0], v2); CrossProduct(v2, v1, normal); VectorNormalize(normal); dist = DotProduct(m_Points[0], normal); } else { VectorClear(normal); dist = 0.0; } } vec_t Winding::getArea() const { unsigned int i; vec3_t d1, d2, cross; vec_t total; //hlassert(m_NumPoints >= 3); total = 0.0; if (m_NumPoints >= 3) { for (i = 2; i < m_NumPoints; i++) { VectorSubtract(m_Points[i - 1], m_Points[0], d1); VectorSubtract(m_Points[i], m_Points[0], d2); CrossProduct(d1, d2, cross); total += 0.5 * VectorLength(cross); } } return total; } void Winding::getBounds(vec3_t& mins, vec3_t& maxs) const { BoundingBox bounds; unsigned x; for (x=0; x 0) { VectorCopy(vec3_origin, center); for (i = 0; i < m_NumPoints; i++) { VectorAdd(m_Points[i], center, center); } scale = 1.0 / m_NumPoints; VectorScale(center, scale, center); } else { VectorClear(center); } } Winding* Winding::Copy() const { Winding* newWinding = new Winding(*this); return newWinding; } void Winding::Check() const { unsigned int i, j; vec_t* p1; vec_t* p2; vec_t d, edgedist; vec3_t dir, edgenormal, facenormal; vec_t area; vec_t facedist; if (m_NumPoints < 3) { Error("Winding::Check : %i points", m_NumPoints); } area = getArea(); if (area < 1) { Error("Winding::Check : %f area", area); } getPlane(facenormal, facedist); for (i = 0; i < m_NumPoints; i++) { p1 = m_Points[i]; for (j = 0; j < 3; j++) { if (p1[j] > BOGUS_RANGE || p1[j] < -BOGUS_RANGE) { Error("Winding::Check : BOGUS_RANGE: %f", p1[j]); } } j = i + 1 == m_NumPoints ? 0 : i + 1; // check the point is on the face plane d = DotProduct(p1, facenormal) - facedist; if (d < -ON_EPSILON || d > ON_EPSILON) { Error("Winding::Check : point off plane"); } // check the edge isn't degenerate p2 = m_Points[j]; VectorSubtract(p2, p1, dir); if (VectorLength(dir) < ON_EPSILON) { Error("Winding::Check : degenerate edge"); } CrossProduct(facenormal, dir, edgenormal); VectorNormalize(edgenormal); edgedist = DotProduct(p1, edgenormal); edgedist += ON_EPSILON; // all other points must be on front side for (j = 0; j < m_NumPoints; j++) { if (j == i) { continue; } d = DotProduct(m_Points[j], edgenormal); if (d > edgedist) { Error("Winding::Check : non-convex"); } } } } bool Winding::Valid() const { // TODO: Check to ensure there are 3 non-colinear points if ((m_NumPoints < 3) || (!m_Points)) { return false; } return true; } // // Construction // Winding::Winding() { m_Points = NULL; m_NumPoints = m_MaxPoints = 0; } Winding::Winding(vec3_t *points, UINT32 numpoints) { hlassert(numpoints >= 3); m_NumPoints = numpoints; m_MaxPoints = (m_NumPoints + 3) & ~3; // groups of 4 m_Points = new vec3_t[m_MaxPoints]; memcpy(m_Points, points, sizeof(vec3_t) * m_NumPoints); } void Winding::initFromPoints(vec3_t *points, UINT32 numpoints) { hlassert(numpoints >= 3); Reset(); m_NumPoints = numpoints; m_MaxPoints = (m_NumPoints + 3) & ~3; // groups of 4 m_Points = new vec3_t[m_MaxPoints]; memcpy(m_Points, points, sizeof(vec3_t) * m_NumPoints); } Winding& Winding::operator=(const Winding& other) { delete[] m_Points; m_NumPoints = other.m_NumPoints; m_MaxPoints = (m_NumPoints + 3) & ~3; // groups of 4 m_Points = new vec3_t[m_MaxPoints]; memcpy(m_Points, other.m_Points, sizeof(vec3_t) * m_NumPoints); return *this; } Winding::Winding(UINT32 numpoints) { hlassert(numpoints >= 3); m_NumPoints = numpoints; m_MaxPoints = (m_NumPoints + 3) & ~3; // groups of 4 m_Points = new vec3_t[m_MaxPoints]; memset(m_Points, 0, sizeof(vec3_t) * m_NumPoints); } Winding::Winding(const Winding& other) { m_NumPoints = other.m_NumPoints; m_MaxPoints = (m_NumPoints + 3) & ~3; // groups of 4 m_Points = new vec3_t[m_MaxPoints]; memcpy(m_Points, other.m_Points, sizeof(vec3_t) * m_NumPoints); } Winding::~Winding() { delete[] m_Points; } void Winding::initFromPlane(const vec3_t normal, const vec_t dist) { int i; vec_t max, v; vec3_t org, vright, vup; // find the major axis max = -BOGUS_RANGE; int x = -1; for (i = 0; i < 3; i++) { v = fabs(normal[i]); if (v > max) { max = v; x = i; } } if (x == -1) { Error("Winding::initFromPlane no major axis found\n"); } VectorCopy(vec3_origin, vup); switch (x) { case 0: case 1: vup[2] = 1; break; case 2: vup[0] = 1; break; } v = DotProduct(vup, normal); VectorMA(vup, -v, normal, vup); VectorNormalize(vup); VectorScale(normal, dist, org); CrossProduct(vup, normal, vright); VectorScale(vup, BOGUS_RANGE, vup); VectorScale(vright, BOGUS_RANGE, vright); // project a really big axis aligned box onto the plane m_NumPoints = 4; m_Points = new vec3_t[m_NumPoints]; VectorSubtract(org, vright, m_Points[0]); VectorAdd(m_Points[0], vup, m_Points[0]); VectorAdd(org, vright, m_Points[1]); VectorAdd(m_Points[1], vup, m_Points[1]); VectorAdd(org, vright, m_Points[2]); VectorSubtract(m_Points[2], vup, m_Points[2]); VectorSubtract(org, vright, m_Points[3]); VectorSubtract(m_Points[3], vup, m_Points[3]); } Winding::Winding(const vec3_t normal, const vec_t dist) { initFromPlane(normal, dist); } Winding::Winding(const dface_t& face) { int se; dvertex_t* dv; int v; m_NumPoints = face.numedges; m_Points = new vec3_t[m_NumPoints]; unsigned i; for (i = 0; i < face.numedges; i++) { se = g_dsurfedges[face.firstedge + i]; if (se < 0) { v = g_dedges[-se].v[1]; } else { v = g_dedges[se].v[0]; } dv = &g_dvertexes[v]; VectorCopy(dv->point, m_Points[i]); } RemoveColinearPoints(); } Winding::Winding(const dplane_t& plane) { vec3_t normal; vec_t dist; VectorCopy(plane.normal, normal); dist = plane.dist; initFromPlane(normal, dist); } // // Specialized Functions // #ifdef COMMON_HULLU void Winding::RemoveColinearPoints() { unsigned int i; unsigned int nump; int j; vec3_t v1, v2; vec3_t p[128]; //JK: Did the optimizations... if (m_NumPoints>1) { VectorSubtract(m_Points[0], m_Points[m_NumPoints - 1], v2); VectorNormalize(v2); } nump=0; for (i = 0; i < m_NumPoints; i++) { j = (i + 1) % m_NumPoints; // i + 1 VectorSubtract(m_Points[i], m_Points[j], v1); VectorNormalize(v1); VectorAdd(v1, v2, v2); if (!VectorCompare(v2, vec3_origin)) { VectorCopy(m_Points[i], p[nump]); nump++; } #if 0 else { Log("v3 was (%4.3f %4.3f %4.3f)\n", v2[0], v2[1], v2[2]); } #endif //Set v2 for next round v2[0]=-v1[0]; v2[1]=-v1[1]; v2[2]=-v1[2]; } if (nump == m_NumPoints) { return; } #if 0 Warning("RemoveColinearPoints: Removed %u points, from %u to %u\n", m_NumPoints - nump, m_NumPoints, nump); Warning("Before :\n"); Print(); #endif m_NumPoints = nump; memcpy(m_Points, p, nump * sizeof(vec3_t)); #if 0 Warning("After :\n"); Print(); Warning("==========\n"); #endif } #else /*COMMON_HULLU*/ void Winding::RemoveColinearPoints() { unsigned int i; unsigned int nump; int j, k; vec3_t v1, v2, v3; vec3_t p[128]; // TODO: OPTIMIZE: this could be 1/2 the number of vectornormalize calls by caching one of the previous values through the loop // TODO: OPTIMIZE: Remove the modulo operations inside the loop and replace with compares nump = 0; for (i = 0; i < m_NumPoints; i++) { j = (i + 1) % m_NumPoints; // i + 1 k = (i + m_NumPoints - 1) % m_NumPoints; // i - 1 VectorSubtract(m_Points[i], m_Points[j], v1); VectorSubtract(m_Points[i], m_Points[k], v2); VectorNormalize(v1); VectorNormalize(v2); VectorAdd(v1, v2, v3); if (!VectorCompare(v3, vec3_origin)) { VectorCopy(m_Points[i], p[nump]); nump++; } #if 0 else { Log("v3 was (%4.3f %4.3f %4.3f)\n", v3[0], v3[1], v3[2]); } #endif } if (nump == m_NumPoints) { return; } #if 0 Warning("RemoveColinearPoints: Removed %u points, from %u to %u\n", m_NumPoints - nump, m_NumPoints, nump); Warning("Before :\n"); Print(); #endif m_NumPoints = nump; memcpy(m_Points, p, nump * sizeof(vec3_t)); #if 0 Warning("After :\n"); Print(); Warning("==========\n"); #endif } #endif /*!COMMON_HULLU*/ void Winding::Clip(const dplane_t& plane, Winding** front, Winding** back) { vec3_t normal; vec_t dist; VectorCopy(plane.normal, normal); dist = plane.dist; Clip(normal, dist, front, back); } void Winding::Clip(const vec3_t normal, const vec_t dist, Winding** front, Winding** back) { vec_t dists[MAX_POINTS_ON_WINDING + 4]; int sides[MAX_POINTS_ON_WINDING + 4]; int counts[3]; vec_t dot; unsigned int i, j; unsigned int maxpts; counts[0] = counts[1] = counts[2] = 0; // determine sides for each point for (i = 0; i < m_NumPoints; i++) { dot = DotProduct(m_Points[i], normal); dot -= 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]; if (!counts[0]) { *front = NULL; *back = new Winding(*this); return; } if (!counts[1]) { *front = new Winding(*this); *back = NULL; return; } maxpts = m_NumPoints + 4; // can't use counts[0]+2 because // of fp grouping errors Winding* f = new Winding(maxpts); Winding* b = new Winding(maxpts); *front = f; *back = b; f->m_NumPoints = 0; b->m_NumPoints = 0; for (i = 0; i < m_NumPoints; i++) { vec_t* p1 = m_Points[i]; if (sides[i] == SIDE_ON) { VectorCopy(p1, f->m_Points[f->m_NumPoints]); VectorCopy(p1, b->m_Points[b->m_NumPoints]); f->m_NumPoints++; b->m_NumPoints++; continue; } else if (sides[i] == SIDE_FRONT) { VectorCopy(p1, f->m_Points[f->m_NumPoints]); f->m_NumPoints++; } else if (sides[i] == SIDE_BACK) { VectorCopy(p1, b->m_Points[b->m_NumPoints]); b->m_NumPoints++; } if ((sides[i + 1] == SIDE_ON) | (sides[i + 1] == sides[i])) // | instead of || for branch optimization { continue; } // generate a split point vec3_t mid; unsigned int tmp = i + 1; if (tmp >= m_NumPoints) { tmp = 0; } vec_t* p2 = m_Points[tmp]; dot = dists[i] / (dists[i] - dists[i + 1]); #if 0 for (j = 0; j < 3; j++) { // avoid round off error when possible if (normal[j] < 1.0 - NORMAL_EPSILON) { if (normal[j] > -1.0 + NORMAL_EPSILON) { mid[j] = p1[j] + dot * (p2[j] - p1[j]); } else { mid[j] = -dist; } } else { mid[j] = dist; } } #else for (j = 0; j < 3; j++) { // avoid round off error when possible if (normal[j] == 1) mid[j] = dist; else if (normal[j] == -1) mid[j] = -dist; else mid[j] = p1[j] + dot * (p2[j] - p1[j]); } #endif VectorCopy(mid, f->m_Points[f->m_NumPoints]); VectorCopy(mid, b->m_Points[b->m_NumPoints]); f->m_NumPoints++; b->m_NumPoints++; } if ((f->m_NumPoints > maxpts) | (b->m_NumPoints > maxpts)) // | instead of || for branch optimization { Error("Winding::Clip : points exceeded estimate"); } if ((f->m_NumPoints > MAX_POINTS_ON_WINDING) | (b->m_NumPoints > MAX_POINTS_ON_WINDING)) // | instead of || for branch optimization { Error("Winding::Clip : MAX_POINTS_ON_WINDING"); } f->RemoveColinearPoints(); b->RemoveColinearPoints(); } bool Winding::Chop(const vec3_t normal, const vec_t dist) { Winding* f; Winding* b; Clip(normal, dist, &f, &b); if (b) { delete b; } if (f) { delete[] m_Points; m_NumPoints = f->m_NumPoints; m_Points = f->m_Points; f->m_Points = NULL; delete f; return true; } else { m_NumPoints = 0; delete[] m_Points; m_Points = NULL; return false; } } int Winding::WindingOnPlaneSide(const vec3_t normal, const vec_t dist) { bool front, back; unsigned int i; vec_t d; front = false; back = false; for (i = 0; i < m_NumPoints; i++) { d = DotProduct(m_Points[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; } bool Winding::Clip(const dplane_t& split, bool keepon) { vec_t dists[MAX_POINTS_ON_WINDING]; int sides[MAX_POINTS_ON_WINDING]; int counts[3]; vec_t dot; int i, j; counts[0] = counts[1] = counts[2] = 0; // determine sides for each point // do this exactly, with no epsilon so tiny portals still work for (i = 0; i < m_NumPoints; i++) { dot = DotProduct(m_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]; if (keepon && !counts[0] && !counts[1]) { return true; } if (!counts[0]) { delete[] m_Points; m_Points = NULL; m_NumPoints = 0; return false; } if (!counts[1]) { return true; } unsigned maxpts = m_NumPoints + 4; // can't use counts[0]+2 because of fp grouping errors unsigned newNumPoints = 0; vec3_t* newPoints = new vec3_t[maxpts]; memset(newPoints, 0, sizeof(vec3_t) * maxpts); for (i = 0; i < m_NumPoints; i++) { vec_t* p1 = m_Points[i]; if (sides[i] == SIDE_ON) { VectorCopy(p1, newPoints[newNumPoints]); newNumPoints++; continue; } else if (sides[i] == SIDE_FRONT) { VectorCopy(p1, newPoints[newNumPoints]); newNumPoints++; } if (sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i]) { continue; } // generate a split point vec3_t mid; unsigned int tmp = i + 1; if (tmp >= m_NumPoints) { tmp = 0; } vec_t* p2 = m_Points[tmp]; 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.0 - NORMAL_EPSILON) { if (split.normal[j] > -1.0 + NORMAL_EPSILON) { mid[j] = p1[j] + dot * (p2[j] - p1[j]); } else { mid[j] = -split.dist; } } else { mid[j] = split.dist; } } VectorCopy(mid, newPoints[newNumPoints]); newNumPoints++; } if (newNumPoints > maxpts) { Error("Winding::Clip : points exceeded estimate"); } delete[] m_Points; m_Points = newPoints; m_NumPoints = newNumPoints; RemoveColinearPoints(); return true; } /* * ================== * Divide * 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. * ================== */ void Winding::Divide(const dplane_t& split, Winding** front, Winding** back) { vec_t dists[MAX_POINTS_ON_WINDING]; int sides[MAX_POINTS_ON_WINDING]; int counts[3]; vec_t dot; int i, j; int maxpts; counts[0] = counts[1] = counts[2] = 0; // determine sides for each point for (i = 0; i < m_NumPoints; i++) { dot = DotProduct(m_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 = this; // Makes this function non-const return; } if (!counts[1]) { *front = this; // Makes this function non-const return; } maxpts = m_NumPoints + 4; // can't use counts[0]+2 because // of fp grouping errors Winding* f = new Winding(maxpts); Winding* b = new Winding(maxpts); *front = f; *back = b; f->m_NumPoints = 0; b->m_NumPoints = 0; for (i = 0; i < m_NumPoints; i++) { vec_t* p1 = m_Points[i]; if (sides[i] == SIDE_ON) { VectorCopy(p1, f->m_Points[f->m_NumPoints]); VectorCopy(p1, b->m_Points[b->m_NumPoints]); f->m_NumPoints++; b->m_NumPoints++; continue; } else if (sides[i] == SIDE_FRONT) { VectorCopy(p1, f->m_Points[f->m_NumPoints]); f->m_NumPoints++; } else if (sides[i] == SIDE_BACK) { VectorCopy(p1, b->m_Points[b->m_NumPoints]); b->m_NumPoints++; } if (sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i]) { continue; } // generate a split point vec3_t mid; unsigned int tmp = i + 1; if (tmp >= m_NumPoints) { tmp = 0; } vec_t* p2 = m_Points[tmp]; 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.0 - NORMAL_EPSILON) { if (split.normal[j] > -1.0 + NORMAL_EPSILON) { mid[j] = p1[j] + dot * (p2[j] - p1[j]); } else { mid[j] = -split.dist; } } else { mid[j] = split.dist; } } VectorCopy(mid, f->m_Points[f->m_NumPoints]); VectorCopy(mid, b->m_Points[b->m_NumPoints]); f->m_NumPoints++; b->m_NumPoints++; } if (f->m_NumPoints > maxpts || b->m_NumPoints > maxpts) { Error("Winding::Divide : points exceeded estimate"); } f->RemoveColinearPoints(); b->RemoveColinearPoints(); } void Winding::addPoint(const vec3_t newpoint) { if (m_NumPoints >= m_MaxPoints) { resize(m_NumPoints + 1); } VectorCopy(newpoint, m_Points[m_NumPoints]); m_NumPoints++; } void Winding::insertPoint(const vec3_t newpoint, const unsigned int offset) { if (offset >= m_NumPoints) { addPoint(newpoint); } else { if (m_NumPoints >= m_MaxPoints) { resize(m_NumPoints + 1); } unsigned x; for (x = m_NumPoints; x>offset; x--) { VectorCopy(m_Points[x-1], m_Points[x]); } VectorCopy(newpoint, m_Points[x]); m_NumPoints++; } } void Winding::resize(UINT32 newsize) { newsize = (newsize + 3) & ~3; // groups of 4 vec3_t* newpoints = new vec3_t[newsize]; m_NumPoints = min(newsize, m_NumPoints); memcpy(newpoints, m_Points, m_NumPoints); delete[] m_Points; m_Points = newpoints; m_MaxPoints = newsize; } void Winding::CopyPoints(vec3_t *points, int &numpoints) { if(!points) { numpoints = 0; return; } memcpy(points, m_Points, sizeof(vec3_t) * m_NumPoints); numpoints = m_NumPoints; } void Winding::Reset(void) { if(m_Points) { delete [] m_Points; m_Points = NULL; } m_NumPoints = m_MaxPoints = 0; }