213 lines
5.7 KiB
C++
213 lines
5.7 KiB
C++
#include "idlib/geometry/winding.h"
|
|
|
|
#include "idlib/lib_print.h"
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <cstring>
|
|
#include <malloc.h>
|
|
|
|
idWinding::idWinding()
|
|
: numPoints(0), p(nullptr), allocedSize(0) {
|
|
}
|
|
|
|
idWinding::idWinding(const idWinding& other)
|
|
: idWinding() {
|
|
*this = other;
|
|
}
|
|
|
|
idWinding::~idWinding() {
|
|
_aligned_free(p);
|
|
p = nullptr;
|
|
}
|
|
|
|
idWinding& idWinding::operator=(const idWinding& other) {
|
|
if (this != &other && ReAllocate(other.numPoints, false)) {
|
|
numPoints = other.numPoints;
|
|
if (numPoints > 0) {
|
|
std::memcpy(p, other.p,
|
|
static_cast<std::size_t>(numPoints) * sizeof(idVec5));
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
void idWinding::Clear() {
|
|
numPoints = 0;
|
|
allocedSize = 0;
|
|
_aligned_free(p);
|
|
p = nullptr;
|
|
}
|
|
|
|
bool idWinding::ReAllocate(const int count, const bool keep) {
|
|
const int newSize = (count + 3) & ~3;
|
|
idVec5* const replacement = static_cast<idVec5*>(_aligned_malloc(
|
|
static_cast<std::size_t>(newSize) * sizeof(idVec5), 16));
|
|
if (replacement == nullptr && newSize > 0) {
|
|
return false;
|
|
}
|
|
if (keep && p != nullptr && numPoints > 0) {
|
|
std::memcpy(replacement, p,
|
|
static_cast<std::size_t>((std::min)(numPoints, newSize)) *
|
|
sizeof(idVec5));
|
|
}
|
|
_aligned_free(p);
|
|
p = replacement;
|
|
allocedSize = newSize;
|
|
if (numPoints > allocedSize) {
|
|
numPoints = allocedSize;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void idWinding::AddPoint(const idVec3& point) {
|
|
if (numPoints + 1 > allocedSize &&
|
|
!ReAllocate(numPoints + 1, true)) {
|
|
return;
|
|
}
|
|
p[numPoints++] = idVec5(point.x, point.y, point.z, 0.0f, 0.0f);
|
|
}
|
|
|
|
void idWinding::AddPoint(const idVec5& point) {
|
|
if (numPoints + 1 > allocedSize &&
|
|
!ReAllocate(numPoints + 1, true)) {
|
|
return;
|
|
}
|
|
p[numPoints++] = point;
|
|
}
|
|
|
|
idFixedWinding::idFixedWinding() {
|
|
p = data;
|
|
numPoints = 0;
|
|
allocedSize = 64;
|
|
}
|
|
|
|
idFixedWinding::idFixedWinding(const idFixedWinding& other)
|
|
: idFixedWinding() {
|
|
*this = other;
|
|
}
|
|
|
|
idFixedWinding& idFixedWinding::operator=(const idFixedWinding& other) {
|
|
idWinding::operator=(other);
|
|
return *this;
|
|
}
|
|
|
|
void idWinding::GetBounds(idBounds& bounds) const {
|
|
if (numPoints <= 0) {
|
|
bounds[0].Set(1.0e30f, 1.0e30f, 1.0e30f);
|
|
bounds[1].Set(-1.0e30f, -1.0e30f, -1.0e30f);
|
|
return;
|
|
}
|
|
bounds[0].Set(p[0].x, p[0].y, p[0].z);
|
|
bounds[1] = bounds[0];
|
|
for (int point = 1; point < numPoints; ++point) {
|
|
bounds[0].x = (std::min)(bounds[0].x, p[point].x);
|
|
bounds[0].y = (std::min)(bounds[0].y, p[point].y);
|
|
bounds[0].z = (std::min)(bounds[0].z, p[point].z);
|
|
bounds[1].x = (std::max)(bounds[1].x, p[point].x);
|
|
bounds[1].y = (std::max)(bounds[1].y, p[point].y);
|
|
bounds[1].z = (std::max)(bounds[1].z, p[point].z);
|
|
}
|
|
}
|
|
|
|
bool idWinding::IsHuge(const float radius) const {
|
|
for (int point = 0; point < numPoints; ++point) {
|
|
for (int axis = 0; axis < 3; ++axis) {
|
|
if (p[point][axis] <= -radius || p[point][axis] >= radius) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
idFixedWinding::~idFixedWinding() {
|
|
p = nullptr;
|
|
}
|
|
|
|
void idFixedWinding::Clear() {
|
|
numPoints = 0;
|
|
p = data;
|
|
allocedSize = 64;
|
|
}
|
|
|
|
bool idFixedWinding::ReAllocate(const int count, const bool) {
|
|
if (count <= 64) {
|
|
return true;
|
|
}
|
|
idLibPrint::Warning(
|
|
"idFixedWinding -> MAX_POINTS_ON_WINDING overflowed");
|
|
return false;
|
|
}
|
|
|
|
int idFixedWinding::SplitInPlace(const idPlane& plane,
|
|
const float epsilon, idFixedWinding* const back) {
|
|
if (back == nullptr) {
|
|
return 0;
|
|
}
|
|
|
|
float distances[65]{};
|
|
int sides[65]{};
|
|
int counts[3]{};
|
|
for (int index = 0; index < numPoints; ++index) {
|
|
distances[index] = plane.a * p[index].x + plane.b * p[index].y +
|
|
plane.c * p[index].z + plane.d;
|
|
if (distances[index] > epsilon) {
|
|
sides[index] = 0;
|
|
} else if (distances[index] < -epsilon) {
|
|
sides[index] = 1;
|
|
} else {
|
|
sides[index] = 2;
|
|
}
|
|
++counts[sides[index]];
|
|
}
|
|
if (counts[1] == 0) {
|
|
return counts[0] != 0 ? 0 : 2;
|
|
}
|
|
if (counts[0] == 0) {
|
|
return 1;
|
|
}
|
|
|
|
distances[numPoints] = distances[0];
|
|
sides[numPoints] = sides[0];
|
|
idFixedWinding front;
|
|
back->Clear();
|
|
for (int index = 0; index < numPoints; ++index) {
|
|
const idVec5& current = p[index];
|
|
if (sides[index] == 2) {
|
|
front.AddPoint(current);
|
|
back->AddPoint(current);
|
|
} else if (sides[index] == 0) {
|
|
front.AddPoint(current);
|
|
} else {
|
|
back->AddPoint(current);
|
|
}
|
|
|
|
if (sides[index + 1] == 2 ||
|
|
sides[index + 1] == sides[index]) {
|
|
continue;
|
|
}
|
|
const idVec5& next = p[(index + 1) % numPoints];
|
|
const float fraction = distances[index] /
|
|
(distances[index] - distances[index + 1]);
|
|
idVec5 split;
|
|
for (int component = 0; component < 5; ++component) {
|
|
split[component] = current[component] +
|
|
fraction * (next[component] - current[component]);
|
|
}
|
|
if (plane.a == 1.0f) split.x = -plane.d;
|
|
else if (plane.a == -1.0f) split.x = plane.d;
|
|
if (plane.b == 1.0f) split.y = -plane.d;
|
|
else if (plane.b == -1.0f) split.y = plane.d;
|
|
if (plane.c == 1.0f) split.z = -plane.d;
|
|
else if (plane.c == -1.0f) split.z = plane.d;
|
|
front.AddPoint(split);
|
|
back->AddPoint(split);
|
|
}
|
|
Clear();
|
|
for (int index = 0; index < front.numPoints; ++index) {
|
|
AddPoint(front[index]);
|
|
}
|
|
return 3;
|
|
}
|