Files
tech5/source/shared/idlib/bv/bounds.h
T

139 lines
5.2 KiB
C++

#pragma once
#include "../math/plane.h"
#include "../math/vector.h"
#include <cmath>
// Recovered two-vector bounds layout used by the geometry reconstruction.
class idBounds {
public:
idVec3 b[2];
const idVec3& operator[](const int index) const {
return b[index];
}
idVec3& operator[](const int index) {
return b[index];
}
// Recovered from shared/idlib/bv/bounds.cpp. This scalar PC spelling is
// the center/extents form of the original VMX implementation.
void FromTransformedBounds(const idBounds& bounds,
const idVec3& origin, const idMat3& axis) {
const idVec3 center = (bounds[0] + bounds[1]) * 0.5f;
const idVec3 extents = bounds[1] - center;
const idVec3 transformedCenter = origin + idVec3(
axis[0].x * center.x + axis[1].x * center.y + axis[2].x * center.z,
axis[0].y * center.x + axis[1].y * center.y + axis[2].y * center.z,
axis[0].z * center.x + axis[1].z * center.y + axis[2].z * center.z);
const idVec3 transformedExtents(
std::fabs(axis[0].x * extents.x) +
std::fabs(axis[1].x * extents.y) +
std::fabs(axis[2].x * extents.z),
std::fabs(axis[0].y * extents.x) +
std::fabs(axis[1].y * extents.y) +
std::fabs(axis[2].y * extents.z),
std::fabs(axis[0].z * extents.x) +
std::fabs(axis[1].z * extents.y) +
std::fabs(axis[2].z * extents.z));
b[0] = transformedCenter - transformedExtents;
b[1] = transformedCenter + transformedExtents;
}
// Recovered from shared/idlib/bv/bounds.cpp. Return values match the
// original plane-side convention: 0 front, 1 back, 3 crossing.
int PlaneSide(const idPlane& plane, const float epsilon = 0.0f) const {
const idVec3 center = (b[0] + b[1]) * 0.5f;
const idVec3 extents = b[1] - center;
const float radius = std::fabs(extents.x * plane.a)
+ std::fabs(extents.y * plane.b)
+ std::fabs(extents.z * plane.c);
const float distance = plane.Distance(center);
if (distance - radius > epsilon) {
return 0;
}
if (distance + radius < -epsilon) {
return 1;
}
return 3;
}
// Recovered from shared/idlib/bv/bounds.cpp. This is the separating-axis
// segment/AABB test used by GameLib obstacle routing.
bool LineIntersection(const idVec3& start, const idVec3& end) const {
const idVec3 lineDir = (end - start) * 0.5f;
const idVec3 lineCenter = start + lineDir;
const idVec3 boundsCenter = (b[0] + b[1]) * 0.5f;
const idVec3 extents = b[1] - boundsCenter;
const idVec3 dirAbs(
std::fabs(lineDir.x), std::fabs(lineDir.y),
std::fabs(lineDir.z)
);
const idVec3 centerDelta = lineCenter - boundsCenter;
if (std::fabs(centerDelta.x) > extents.x + dirAbs.x
|| std::fabs(centerDelta.y) > extents.y + dirAbs.y
|| std::fabs(centerDelta.z) > extents.z + dirAbs.z) {
return false;
}
if (std::fabs(centerDelta.z * lineDir.y
- centerDelta.y * lineDir.z)
> extents.y * dirAbs.z + extents.z * dirAbs.y) {
return false;
}
if (std::fabs(lineDir.z * centerDelta.x
- centerDelta.z * lineDir.x)
> extents.z * dirAbs.x + extents.x * dirAbs.z) {
return false;
}
return std::fabs(centerDelta.y * lineDir.x
- lineDir.y * centerDelta.x)
<= extents.y * dirAbs.x + extents.x * dirAbs.y;
}
bool RayIntersection(const idVec3& start, const idVec3& direction,
float& scale) const {
float minimum = -1.0e30f;
float maximum = 1.0e30f;
for (int axis = 0; axis < 3; ++axis) {
if (std::fabs(direction[axis]) < 1.0e-20f) {
if (start[axis] < b[0][axis] || start[axis] > b[1][axis]) {
return false;
}
continue;
}
const float inverseDirection = 1.0f / direction[axis];
float first = (b[0][axis] - start[axis]) * inverseDirection;
float second = (b[1][axis] - start[axis]) * inverseDirection;
if (first > second) {
const float swap = first;
first = second;
second = swap;
}
if (first > minimum) minimum = first;
if (second < maximum) maximum = second;
if (minimum > maximum) return false;
}
scale = minimum;
return true;
}
bool SphereIntersection(const idVec3& origin, const float radius) const {
float distanceSquared = 0.0f;
for (int axis = 0; axis < 3; ++axis) {
float delta = 0.0f;
if (origin[axis] < b[0][axis]) {
delta = b[0][axis] - origin[axis];
} else if (origin[axis] > b[1][axis]) {
delta = origin[axis] - b[1][axis];
}
distanceSquared += delta * delta;
}
return distanceSquared < radius * radius;
}
};
static_assert(sizeof(idBounds) == 24, "Recovered idBounds layout changed");