Files
2026-08-09 01:29:43 -07:00

774 lines
31 KiB
C++

#include "cm/jobs/polygonmodel/polygonmodel.h"
#include "cm/jobs/polygonmodel/polygonmodeldata.h"
#include "idlib/geometry/tracemodel.h"
#include <algorithm>
#include <cmath>
#include <cstring>
#include <limits>
namespace {
constexpr float DEG2RAD = 0.01745329251994329577f;
constexpr float RAD2DEG = 57.295779513082320876f;
constexpr float ROTATION_EPSILON = 0.01f;
idVec3 Vec3(const idVec4& value) {
return idVec3(value.x, value.y, value.z);
}
void SetVec4(idVec4& target, const idVec3& value, const float w = 0.0f) {
target.Set(value.x, value.y, value.z, w);
}
idVec3 ModelToWorldVector(const idMat3& axis, const idVec3& value) {
return idVec3(
axis[0].x * value.x + axis[1].x * value.y + axis[2].x * value.z,
axis[0].y * value.x + axis[1].y * value.y + axis[2].y * value.z,
axis[0].z * value.x + axis[1].z * value.y + axis[2].z * value.z);
}
idVec3 WorldToModelVector(const idMat3& axis, const idVec3& value) {
return idVec3(axis[0].Dot(value), axis[1].Dot(value),
axis[2].Dot(value));
}
idVec3 RotateAroundAxis(const idVec3& point, const idVec3& origin,
idVec3 axis, const float angleDegrees) {
if (axis.NormalizeFast() == 0.0f || angleDegrees == 0.0f) {
return point;
}
const float angle = angleDegrees * DEG2RAD;
const float sine = std::sin(angle);
const float cosine = std::cos(angle);
const idVec3 relative = point - origin;
return origin + relative * cosine + axis.Cross(relative) * sine
+ axis * (axis.Dot(relative) * (1.0f - cosine));
}
idVec3 RotateVector(const idVec3& value, idVec3 axis,
const float angleDegrees) {
return RotateAroundAxis(value, idVec3(0.0f, 0.0f, 0.0f),
axis, angleDegrees);
}
bool TestAndSet(std::uint8_t* bits, const int index) {
if (bits == nullptr) {
return false;
}
const std::uint8_t mask = static_cast<std::uint8_t>(1u << (index & 7));
std::uint8_t& value = bits[index >> 3];
const bool old = (value & mask) != 0;
value = static_cast<std::uint8_t>(value | mask);
return old;
}
void SetMaterial(contactInfo_t& contact, const cm_material_t& material) {
contact.contentFlags = material.contentFlags;
contact.surfaceFlags = material.surfaceFlags;
contact.surfaceType = material.surfaceType;
contact.surfaceColor[0] = material.surfaceColor[0];
contact.surfaceColor[1] = material.surfaceColor[1];
contact.surfaceColor[2] = material.surfaceColor[2];
}
bool PointInsidePolygon(const cm_subModelPtrs_t& model,
const cm_polygon_t& polygon, const idPlane& plane, const idVec3& point) {
bool positive = false;
bool negative = false;
for (int index = 0; index < polygon.numEdges; ++index) {
const std::uint16_t reference = model.polygonEdges[
polygon.firstEdge + index];
const cm_edge_t& edge = model.edges[CM_EdgeIndex(reference)];
const idVec3& start = model.vertices[
CM_EdgeStartVertex(edge, reference)].p;
const idVec3& end = model.vertices[
CM_EdgeEndVertex(edge, reference)].p;
const float side = (end - start).Cross(point - start).Dot(
plane.Normal());
positive |= side > ROTATION_EPSILON;
negative |= side < -ROTATION_EPSILON;
if (positive && negative) {
return false;
}
}
return true;
}
void ClosestSegmentPoints(const idVec3& p1, const idVec3& q1,
const idVec3& p2, const idVec3& q2, idVec3& first, idVec3& second) {
const idVec3 d1 = q1 - p1;
const idVec3 d2 = q2 - p2;
const idVec3 r = p1 - p2;
const float a = d1.Dot(d1);
const float e = d2.Dot(d2);
const float f = d2.Dot(r);
float s = 0.0f;
float t = 0.0f;
if (a <= 1.0e-12f && e <= 1.0e-12f) {
first = p1;
second = p2;
return;
}
if (a <= 1.0e-12f) {
t = (std::max)(0.0f, (std::min)(1.0f, f / e));
} else {
const float c = d1.Dot(r);
if (e <= 1.0e-12f) {
s = (std::max)(0.0f, (std::min)(1.0f, -c / a));
} else {
const float b = d1.Dot(d2);
const float denominator = a * e - b * b;
if (std::fabs(denominator) > 1.0e-12f) {
s = (std::max)(0.0f, (std::min)(1.0f,
(b * f - c * e) / denominator));
}
t = (b * s + f) / e;
if (t < 0.0f) {
t = 0.0f;
s = (std::max)(0.0f, (std::min)(1.0f, -c / a));
} else if (t > 1.0f) {
t = 1.0f;
s = (std::max)(0.0f,
(std::min)(1.0f, (b - c) / a));
}
}
}
first = p1 + d1 * s;
second = p2 + d2 * t;
}
void StoreRotationCollision(idTraceWork& tw, const float fraction,
const contactType_t type, const idVec3& point, idVec3 normal,
const float distance, const cm_material_t& material,
const int modelFeature, const int trmFeature) {
if (tw.traceResult == nullptr || fraction >= tw.fraction) {
return;
}
if (normal.NormalizeFast() == 0.0f) {
return;
}
tw.fraction = (std::max)(0.0f, fraction);
trace_t& trace = *tw.traceResult;
trace.fraction = tw.fraction;
trace.c.type = type;
trace.c.point = point;
trace.c.normal = normal;
trace.c.dist = distance;
trace.c.separation = 0.0f;
SetMaterial(trace.c, material);
trace.c.modelFeature = modelFeature;
trace.c.trmFeature = trmFeature;
trace.c.flags = 0;
}
bool PointInsideInitialTrace(const idTraceWork& tw, const idVec3& point,
int& nearestPlane) {
float nearestDistance = -std::numeric_limits<float>::max();
nearestPlane = 0;
for (unsigned int index = 0; index < tw.numPolys; ++index) {
const float distance = tw.polys[index].plane.Distance(point);
if (distance > 0.0f) {
return false;
}
if (distance > nearestDistance) {
nearestDistance = distance;
nearestPlane = static_cast<int>(index);
}
}
return tw.numPolys != 0;
}
} // namespace
float CM_TanZeroHalfPI(const float angle) {
const float clamped = (std::max)(0.0f,
(std::min)(1.57079632679f, angle));
return std::tan(clamped);
}
float CM_ArcTanPositive(const float value) {
return std::atan((std::max)(0.0f, value));
}
void CM_PointRotationBounds(const idVec3& origin, const idVec3& axis,
const idVec3& start, const idVec3& end, idVec4& boundsMin,
idVec4& boundsMax) {
idVec3 minimum((std::min)(start.x, end.x),
(std::min)(start.y, end.y), (std::min)(start.z, end.z));
idVec3 maximum((std::max)(start.x, end.x),
(std::max)(start.y, end.y), (std::max)(start.z, end.z));
// Include quarter turns around the recovered axis; this captures extrema
// missed by an endpoint-only arc bound.
for (int step = 1; step < 4; ++step) {
const idVec3 point = RotateAroundAxis(start, origin, axis,
static_cast<float>(step * 90));
minimum.x = (std::min)(minimum.x, point.x);
minimum.y = (std::min)(minimum.y, point.y);
minimum.z = (std::min)(minimum.z, point.z);
maximum.x = (std::max)(maximum.x, point.x);
maximum.y = (std::max)(maximum.y, point.y);
maximum.z = (std::max)(maximum.z, point.z);
}
boundsMin.Set(minimum.x, minimum.y, minimum.z, 0.0f);
boundsMax.Set(maximum.x, maximum.y, maximum.z, 0.0f);
}
bool idPolygonModelCollisionDetection::EdgeIntersectsBoundsShort(
const idBoundsShort& bounds, const idVec3& start, const idVec3& end) {
return bounds.ToBounds().LineIntersection(start, end);
}
void idPolygonModelCollisionDetection::RotationSetup(idTraceWork* const tw,
const idVec3& rotationOrigin, const idVec3& rotationAxis,
const float angle, const idVec3& start, const idVec3& offset,
const idMat3& trmAxis, const idVec3& modelOrigin,
const idMat3& modelAxis) {
const idVec3 centerWorld = start + ModelToWorldVector(trmAxis, offset);
const idVec3 localCenter = WorldToModelVector(modelAxis,
centerWorld - modelOrigin);
const idVec3 localOrigin = WorldToModelVector(modelAxis,
rotationOrigin - modelOrigin);
idVec3 localAxis = WorldToModelVector(modelAxis, rotationAxis);
localAxis.NormalizeFast();
SetVec4(tw->start, localCenter);
SetVec4(tw->origin, localOrigin);
SetVec4(tw->axis, localAxis);
tw->angle = angle;
tw->negAngle = -angle;
tw->initialTan = 0.0f;
tw->maxTan = std::tan(std::fabs(angle) * DEG2RAD * 0.5f);
const idVec3 endCenter = RotateAroundAxis(localCenter,
localOrigin, localAxis, angle);
SetVec4(tw->end, endCenter);
SetVec4(tw->dir, endCenter - localCenter);
SetVec4(tw->negDir, localCenter - endCenter);
}
void idPolygonModelCollisionDetection::TransformFromOriginAxisAngle(
idMat3x4& transform, const idVec3& origin, const idVec3& axis,
const float angle) {
const idVec3 x = RotateVector(idVec3(1.0f, 0.0f, 0.0f), axis, angle);
const idVec3 y = RotateVector(idVec3(0.0f, 1.0f, 0.0f), axis, angle);
const idVec3 z = RotateVector(idVec3(0.0f, 0.0f, 1.0f), axis, angle);
transform.mat[0] = x.x; transform.mat[1] = y.x; transform.mat[2] = z.x;
transform.mat[4] = x.y; transform.mat[5] = y.y; transform.mat[6] = z.y;
transform.mat[8] = x.z; transform.mat[9] = y.z; transform.mat[10] = z.z;
const idVec3 translated = origin - RotateVector(origin, axis, angle);
transform.mat[3] = translated.x;
transform.mat[7] = translated.y;
transform.mat[11] = translated.z;
}
void idPolygonModelCollisionDetection::TransformAxisToZAxis(
idMat3x4& transform, const idVec3& origin, const idVec3& axis) {
idVec3 z = axis;
if (z.NormalizeFast() == 0.0f) {
transform.Identity();
return;
}
idVec3 reference = std::fabs(z.z) < 0.9f
? idVec3(0.0f, 0.0f, 1.0f) : idVec3(0.0f, 1.0f, 0.0f);
idVec3 x = reference.Cross(z);
x.NormalizeFast();
idVec3 y = z.Cross(x);
y.NormalizeFast();
transform.mat[0] = x.x; transform.mat[1] = x.y; transform.mat[2] = x.z;
transform.mat[4] = y.x; transform.mat[5] = y.y; transform.mat[6] = y.z;
transform.mat[8] = z.x; transform.mat[9] = z.y; transform.mat[10] = z.z;
transform.mat[3] = -x.Dot(origin);
transform.mat[7] = -y.Dot(origin);
transform.mat[11] = -z.Dot(origin);
}
void idPolygonModelCollisionDetection::RotationVerts(idTraceWork* const tw,
const idTraceModel&, idVec4* const zverts) {
for (unsigned int index = 0; index < tw->numVerts; ++index) {
const idVec3 start = Vec3(tw->vertexPosition[index]);
const idVec3 end = RotateAroundAxis(start, Vec3(tw->origin),
Vec3(tw->axis), tw->angle);
SetVec4(tw->vertexEndPosition[index], end);
if (zverts != nullptr) {
idVec3 transformed;
tw->ZAxisTransform.Transform(transformed, start);
SetVec4(zverts[index], transformed);
}
}
}
void idPolygonModelCollisionDetection::RotationEdges(idTraceWork* const tw,
const idTraceModel&, const idVec4*) {
for (unsigned int index = 0; index < tw->numEdges; ++index) {
const idVec3 start = Vec3(tw->vertexPosition[
tw->edges[index].vertexNum[0]]);
const idVec3 end = Vec3(tw->vertexPosition[
tw->edges[index].vertexNum[1]]);
tw->edgePluecker[index].FromLine(start, end);
tw->edgeZAxisPluecker[index] = tw->edgePluecker[index];
}
}
void idPolygonModelCollisionDetection::RotationPolys(idTraceWork* const tw,
const idTraceModel&) {
for (unsigned int index = 0; index < tw->numPolys; ++index) {
tw->polyIsUsed[index] = tw->polys[index].numEdges != 0;
}
}
void idPolygonModelCollisionDetection::RotationBounds(idTraceWork* const tw) {
idVec3 minimum(std::numeric_limits<float>::max(),
std::numeric_limits<float>::max(),
std::numeric_limits<float>::max());
idVec3 maximum(-std::numeric_limits<float>::max(),
-std::numeric_limits<float>::max(),
-std::numeric_limits<float>::max());
const int steps = (std::max)(1,
static_cast<int>(std::ceil(std::fabs(tw->angle) / 10.0f)));
for (unsigned int vertex = 0; vertex < tw->numVerts; ++vertex) {
const idVec3 original = Vec3(tw->vertexPosition[vertex]);
for (int step = 0; step <= steps; ++step) {
const idVec3 point = RotateAroundAxis(original, Vec3(tw->origin),
Vec3(tw->axis), tw->angle * step / steps);
minimum.x = (std::min)(minimum.x, point.x);
minimum.y = (std::min)(minimum.y, point.y);
minimum.z = (std::min)(minimum.z, point.z);
maximum.x = (std::max)(maximum.x, point.x);
maximum.y = (std::max)(maximum.y, point.y);
maximum.z = (std::max)(maximum.z, point.z);
}
}
tw->traceBoundsMin.Set(minimum.x - 1.0f, minimum.y - 1.0f,
minimum.z - 1.0f, 0.0f);
tw->traceBoundsMax.Set(maximum.x + 1.0f, maximum.y + 1.0f,
maximum.z + 1.0f, 0.0f);
idBounds bounds;
bounds[0].Set(std::floor(minimum.x) - 1.0f,
std::floor(minimum.y) - 1.0f, std::floor(minimum.z) - 1.0f);
bounds[1].Set(std::ceil(maximum.x) + 1.0f,
std::ceil(maximum.y) + 1.0f, std::ceil(maximum.z) + 1.0f);
tw->traceBoundsShort.SetBounds(bounds);
}
void idPolygonModelCollisionDetection::RotationEdgePlueckerCache(
idTraceWork* const tw, const cm_polygon_t& polygon) {
TranslationPlueckerCache(tw, polygon);
}
void idPolygonModelCollisionDetection::RotationCullPolygonEdges(
idTraceWork* const tw, const cm_polygon_t& polygon) {
for (int index = 0; index < polygon.numEdges && index < 20; ++index) {
tw->polygonEdgeSideCache[index].side = 0;
const idPluecker& modelEdge = tw->polygonEdgePlueckerCache[index];
for (unsigned int trmEdge = 0; trmEdge < tw->numEdges; ++trmEdge) {
if (tw->edgePluecker[trmEdge] * modelEdge < 0.0f) {
tw->polygonEdgeSideCache[index].side |= 1u << trmEdge;
}
}
}
}
int idPolygonModelCollisionDetection::CollisionBetweenEdgeBounds(
const idTraceWork&, const idVec3& firstStart, const idVec3& firstEnd,
const idVec3& secondStart, const idVec3& secondEnd, const float,
idVec3& collisionPoint, idVec3& collisionNormal) {
idVec3 firstPoint;
idVec3 secondPoint;
ClosestSegmentPoints(firstStart, firstEnd, secondStart, secondEnd,
firstPoint, secondPoint);
collisionNormal = firstPoint - secondPoint;
if (collisionNormal.LengthSqr() > ROTATION_EPSILON * ROTATION_EPSILON) {
return 0;
}
collisionPoint = (firstPoint + secondPoint) * 0.5f;
if (collisionNormal.NormalizeFast() == 0.0f) {
collisionNormal = (firstEnd - firstStart).Cross(
secondEnd - secondStart);
collisionNormal.NormalizeFast();
}
return 1;
}
int idPolygonModelCollisionDetection::RotateEdgeThroughEdge(
const idPluecker& first, const idPluecker& second, const float angle,
const float minTan, const float maxTan, float& tanHalfAngle) {
idVec3 firstStart;
idVec3 firstEnd;
idVec3 secondStart;
idVec3 secondEnd;
if (!first.ToLine(firstStart, firstEnd)
|| !second.ToLine(secondStart, secondEnd)) {
return 0;
}
const int steps = (std::max)(8,
static_cast<int>(std::ceil(std::fabs(angle) / 5.0f)));
for (int step = 0; step <= steps; ++step) {
const float fraction = static_cast<float>(step) / steps;
const float tangent = std::tan(std::fabs(angle) * DEG2RAD
* fraction * 0.5f);
if (tangent < minTan || tangent > maxTan) {
continue;
}
const idVec3 rotatedStart = RotateAroundAxis(firstStart,
idVec3(0.0f, 0.0f, 0.0f), idVec3(0.0f, 0.0f, 1.0f),
angle * fraction);
const idVec3 rotatedEnd = RotateAroundAxis(firstEnd,
idVec3(0.0f, 0.0f, 0.0f), idVec3(0.0f, 0.0f, 1.0f),
angle * fraction);
idVec3 firstPoint;
idVec3 secondPoint;
ClosestSegmentPoints(rotatedStart, rotatedEnd,
secondStart, secondEnd, firstPoint, secondPoint);
if ((firstPoint - secondPoint).LengthSqr()
<= ROTATION_EPSILON * ROTATION_EPSILON) {
tanHalfAngle = tangent;
return 1;
}
}
return 0;
}
int idPolygonModelCollisionDetection::EdgeFurthestFromEdge(
const idPluecker& first, const idPluecker& second, const float angle,
float& tanHalfAngle, float& direction) {
float bestDistance = -1.0f;
idVec3 firstStart;
idVec3 firstEnd;
idVec3 secondStart;
idVec3 secondEnd;
if (!first.ToLine(firstStart, firstEnd)
|| !second.ToLine(secondStart, secondEnd)) {
return 0;
}
for (int step = 0; step <= 32; ++step) {
const float fraction = step / 32.0f;
const idVec3 start = RotateAroundAxis(firstStart, idVec3(0, 0, 0),
idVec3(0, 0, 1), angle * fraction);
const idVec3 end = RotateAroundAxis(firstEnd, idVec3(0, 0, 0),
idVec3(0, 0, 1), angle * fraction);
idVec3 a;
idVec3 b;
ClosestSegmentPoints(start, end, secondStart, secondEnd, a, b);
const float distance = (a - b).LengthSqr();
if (distance > bestDistance) {
bestDistance = distance;
tanHalfAngle = std::tan(std::fabs(angle) * DEG2RAD
* fraction * 0.5f);
}
}
direction = angle < 0.0f ? -1.0f : 1.0f;
return bestDistance >= 0.0f;
}
int idPolygonModelCollisionDetection::RotateTrmEdgesThroughPolygon(
idTraceWork* const tw, const cm_polygon_t& polygon) {
const cm_material_t& material = tw->subModelPtrs.materials[polygon.material];
const int steps = (std::max)(8,
static_cast<int>(std::ceil(std::fabs(tw->angle) / 5.0f)));
for (unsigned int trmEdge = 0; trmEdge < tw->numEdges; ++trmEdge) {
const idVec3 originalStart = Vec3(tw->vertexPosition[
tw->edges[trmEdge].vertexNum[0]]);
const idVec3 originalEnd = Vec3(tw->vertexPosition[
tw->edges[trmEdge].vertexNum[1]]);
for (int modelEdgeNumber = 0;
modelEdgeNumber < polygon.numEdges; ++modelEdgeNumber) {
const std::uint16_t reference = tw->subModelPtrs.polygonEdges[
polygon.firstEdge + modelEdgeNumber];
const int modelEdge = CM_EdgeIndex(reference);
const cm_edge_t& edge = tw->subModelPtrs.edges[modelEdge];
const idVec3& modelStart = tw->subModelPtrs.vertices[
CM_EdgeStartVertex(edge, reference)].p;
const idVec3& modelEnd = tw->subModelPtrs.vertices[
CM_EdgeEndVertex(edge, reference)].p;
for (int step = 0; step <= steps; ++step) {
const float fraction = static_cast<float>(step) / steps;
if (fraction >= tw->fraction) {
break;
}
const idVec3 start = RotateAroundAxis(originalStart,
Vec3(tw->origin), Vec3(tw->axis), tw->angle * fraction);
const idVec3 end = RotateAroundAxis(originalEnd,
Vec3(tw->origin), Vec3(tw->axis), tw->angle * fraction);
idVec3 trmPoint;
idVec3 modelPoint;
ClosestSegmentPoints(start, end, modelStart, modelEnd,
trmPoint, modelPoint);
if ((trmPoint - modelPoint).LengthSqr()
> ROTATION_EPSILON * ROTATION_EPSILON) {
continue;
}
idVec3 normal = (end - start).Cross(modelEnd - modelStart);
StoreRotationCollision(*tw, fraction, CONTACT_EDGE,
(trmPoint + modelPoint) * 0.5f, normal,
normal.Dot(modelPoint), material,
((tw->subModelNum << 16) & 0x1FFF0000)
| 0x40000000 | modelEdge,
0x40000000 | static_cast<int>(trmEdge));
break;
}
}
}
return tw->fraction <= 0.0f;
}
int idPolygonModelCollisionDetection::RotatePointThroughPlane(
const idVec3& point, const idPlane& plane, const float angle,
const float minTan, const float maxTan, float& tanHalfAngle) {
const int steps = (std::max)(8,
static_cast<int>(std::ceil(std::fabs(angle) / 5.0f)));
float previousDistance = plane.Distance(point);
for (int step = 1; step <= steps; ++step) {
const float fraction = static_cast<float>(step) / steps;
const float tangent = std::tan(std::fabs(angle) * DEG2RAD
* fraction * 0.5f);
const idVec3 rotated = RotateAroundAxis(point, idVec3(0, 0, 0),
idVec3(0, 0, 1), angle * fraction);
const float distance = plane.Distance(rotated);
if (tangent >= minTan && tangent <= maxTan
&& previousDistance > 0.0f && distance <= 0.0f) {
tanHalfAngle = tangent;
return 1;
}
previousDistance = distance;
}
return 0;
}
int idPolygonModelCollisionDetection::PointFurthestFromPlane(
const idVec3& point, const idPlane& plane, const float angle,
float& tanHalfAngle, float& direction) {
float bestDistance = plane.Distance(point);
int bestStep = 0;
for (int step = 1; step <= 64; ++step) {
const idVec3 rotated = RotateAroundAxis(point, idVec3(0, 0, 0),
idVec3(0, 0, 1), angle * step / 64.0f);
const float distance = plane.Distance(rotated);
if (distance > bestDistance) {
bestDistance = distance;
bestStep = step;
}
}
tanHalfAngle = std::tan(std::fabs(angle) * DEG2RAD
* bestStep / 128.0f);
direction = angle < 0.0f ? -1.0f : 1.0f;
return bestStep != 0;
}
int idPolygonModelCollisionDetection::RotatePointThroughEpsilonPlane(
const idTraceWork& tw, const idVec3& point, const idVec3&,
const idPlane& plane, const float angle, const idVec3& rotationOrigin,
float& tanHalfAngle, idVec3& collisionPoint, idVec3& endDirection) {
const int steps = (std::max)(8,
static_cast<int>(std::ceil(std::fabs(angle) / 5.0f)));
float previousDistance = plane.Distance(point) - ROTATION_EPSILON;
for (int step = 1; step <= steps; ++step) {
const float fraction = static_cast<float>(step) / steps;
const idVec3 rotated = RotateAroundAxis(point, rotationOrigin,
Vec3(tw.axis), angle * fraction);
const float distance = plane.Distance(rotated) - ROTATION_EPSILON;
if (previousDistance > 0.0f && distance <= 0.0f) {
tanHalfAngle = std::tan(std::fabs(angle) * DEG2RAD
* fraction * 0.5f);
collisionPoint = rotated;
endDirection = Vec3(tw.axis).Cross(rotated - rotationOrigin);
return 1;
}
previousDistance = distance;
}
return 0;
}
int idPolygonModelCollisionDetection::RotateTrmVertsThroughPolygon(
idTraceWork* const tw, const cm_polygon_t& polygon,
const idPlane& polygonPlane) {
const cm_material_t& material = tw->subModelPtrs.materials[polygon.material];
const int polygonNum = static_cast<int>(&polygon - tw->subModelPtrs.polygons);
const int steps = (std::max)(8,
static_cast<int>(std::ceil(std::fabs(tw->angle) / 5.0f)));
for (unsigned int vertex = 0; vertex < tw->numVerts; ++vertex) {
const idVec3 original = Vec3(tw->vertexPosition[vertex]);
idVec3 previous = original;
float previousDistance = polygonPlane.Distance(previous);
for (int step = 1; step <= steps; ++step) {
const float fraction = static_cast<float>(step) / steps;
if (fraction >= tw->fraction) {
break;
}
const idVec3 current = RotateAroundAxis(original,
Vec3(tw->origin), Vec3(tw->axis), tw->angle * fraction);
const float distance = polygonPlane.Distance(current);
if (previousDistance > 0.0f && distance <= 0.0f) {
float low = static_cast<float>(step - 1) / steps;
float high = fraction;
idVec3 collision = current;
for (int iteration = 0; iteration < 12; ++iteration) {
const float middle = (low + high) * 0.5f;
collision = RotateAroundAxis(original, Vec3(tw->origin),
Vec3(tw->axis), tw->angle * middle);
if (polygonPlane.Distance(collision) > 0.0f) {
low = middle;
} else {
high = middle;
}
}
if (PointInsidePolygon(tw->subModelPtrs, polygon,
polygonPlane, collision)) {
StoreRotationCollision(*tw, high, CONTACT_TRMVERTEX,
collision, polygonPlane.Normal(), polygonPlane.Dist(),
material, ((tw->subModelNum << 16) & 0x1FFF0000)
| 0x60000000 | polygonNum,
static_cast<int>(vertex));
}
}
previous = current;
previousDistance = distance;
}
}
return tw->fraction <= 0.0f;
}
int idPolygonModelCollisionDetection::RotatePolygonVertsThroughTrm(
idTraceWork* const tw, const cm_polygon_t& polygon) {
if (!tw->isConvex || tw->numPolys == 0) {
return 0;
}
const cm_material_t& material = tw->subModelPtrs.materials[polygon.material];
const int steps = (std::max)(8,
static_cast<int>(std::ceil(std::fabs(tw->angle) / 5.0f)));
for (int edgeNumber = 0; edgeNumber < polygon.numEdges; ++edgeNumber) {
const std::uint16_t reference = tw->subModelPtrs.polygonEdges[
polygon.firstEdge + edgeNumber];
const cm_edge_t& edge = tw->subModelPtrs.edges[CM_EdgeIndex(reference)];
const int vertexNumber = CM_EdgeStartVertex(edge, reference);
if (TestAndSet(tw->modelCheckCounts.vertexCheckCounts, vertexNumber)) {
continue;
}
const idVec3& modelPoint = tw->subModelPtrs.vertices[vertexNumber].p;
for (int step = 1; step <= steps; ++step) {
const float fraction = static_cast<float>(step) / steps;
if (fraction >= tw->fraction) {
break;
}
// Inverse-rotate the stationary model point into the trace model's
// initial frame, then test its recovered convex planes.
const idVec3 localPoint = RotateAroundAxis(modelPoint,
Vec3(tw->origin), Vec3(tw->axis), -tw->angle * fraction);
int nearestPlane = 0;
if (!PointInsideInitialTrace(*tw, localPoint, nearestPlane)) {
continue;
}
idVec3 normal = -RotateVector(tw->polys[nearestPlane].plane.Normal(),
Vec3(tw->axis), tw->angle * fraction);
StoreRotationCollision(*tw, fraction, CONTACT_MODELVERTEX,
modelPoint, normal, normal.Dot(modelPoint), material,
((tw->subModelNum << 16) & 0x1FFF0000)
| 0x20000000 | vertexNumber,
0x60000000 | nearestPlane);
break;
}
}
return tw->fraction <= 0.0f;
}
bool idPolygonModelCollisionDetection::RotateTrmThroughPolygon(
idTraceWork* const tw, const int polygonNum) {
if (TestAndSet(tw->modelCheckCounts.polygonCheckCounts, polygonNum)) {
return false;
}
const cm_polygon_t& polygon = tw->subModelPtrs.polygons[polygonNum];
const cm_material_t& material = tw->subModelPtrs.materials[polygon.material];
if ((material.contentFlags & tw->contents) == 0
|| !tw->traceBoundsShort.IntersectsBounds(polygon.bounds)) {
return false;
}
idPlane plane;
CM_GetPolygonPlane(tw->subModelPtrs, polygon, plane);
RotationEdgePlueckerCache(tw, polygon);
RotationCullPolygonEdges(tw, polygon);
if (RotateTrmVertsThroughPolygon(tw, polygon, plane)
|| RotateTrmEdgesThroughPolygon(tw, polygon)
|| RotatePolygonVertsThroughTrm(tw, polygon)) {
return true;
}
return false;
}
int idPolygonModelCollisionDetection::StartRotation(idTraceWork* const tw,
trace_t* const result, const idVec3& rotationOrigin,
const idVec3& rotationAxis, const float angle, const idVec3& start,
const idTraceModel* const trm, const idMat3& trmAxis,
const int contentMask, const idVec3& modelOrigin,
const idMat3& modelAxis) {
if (trm == nullptr) {
return StartRotationPoint(tw, result, rotationOrigin, rotationAxis,
angle, start, contentMask, modelOrigin, modelAxis);
}
StartContents(tw, result, start, trm, trmAxis, contentMask,
modelOrigin, modelAxis);
tw->traceType = TRACE_ROTATION;
tw->fraction = 1.0f;
result->fraction = 1.0f;
RotationSetup(tw, rotationOrigin, rotationAxis, angle, start,
trm->offset, trmAxis, modelOrigin, modelAxis);
TransformAxisToZAxis(tw->ZAxisTransform, Vec3(tw->origin),
Vec3(tw->axis));
TransformFromOriginAxisAngle(tw->endTransform, Vec3(tw->origin),
Vec3(tw->axis), angle);
idVec4 zverts[32];
RotationVerts(tw, *trm, zverts);
RotationEdges(tw, *trm, zverts);
RotationPolys(tw, *trm);
RotationBounds(tw);
return angle != 0.0f;
}
int idPolygonModelCollisionDetection::StartRotationPoint(
idTraceWork* const tw, trace_t* const result,
const idVec3& rotationOrigin, const idVec3& rotationAxis,
const float angle, const idVec3& start, const int contentMask,
const idVec3& modelOrigin, const idMat3& modelAxis) {
StartContentsPoint(tw, result, start, contentMask, modelOrigin, modelAxis);
tw->traceType = TRACE_ROTATION_POINT;
RotationSetup(tw, rotationOrigin, rotationAxis, angle, start,
idVec3(0.0f, 0.0f, 0.0f), idMat3(1.0f),
modelOrigin, modelAxis);
tw->vertexPosition[0] = tw->start;
tw->vertexEndPosition[0] = tw->end;
RotationBounds(tw);
return angle != 0.0f;
}
void idPolygonModelCollisionDetection::FinishRotation(idTraceWork* const tw,
const idVec3& rotationOrigin, const idVec3& rotationAxis,
const float angle, const idVec3& start, const idMat3& trmAxis,
const idVec3& modelOrigin, const idMat3& modelAxis,
const int modelEntityNum, const int modelPhysicsId,
const int modelBodyId, const int selfId,
const int modelContentsOverride) {
if (tw->traceResult == nullptr) {
return;
}
trace_t& trace = *tw->traceResult;
trace.fraction = tw->fraction;
trace.endpos = RotateAroundAxis(start, rotationOrigin, rotationAxis,
angle * tw->fraction);
for (int column = 0; column < 3; ++column) {
trace.endAxis[column] = RotateVector(trmAxis[column], rotationAxis,
angle * tw->fraction);
}
if (trace.fraction >= 1.0f) {
return;
}
trace.c.normal = ModelToWorldVector(modelAxis, trace.c.normal);
trace.c.point = ModelToWorldVector(modelAxis, trace.c.point)
+ modelOrigin;
trace.c.dist += modelOrigin.Dot(trace.c.normal);
trace.c.entityNum = modelEntityNum;
trace.c.physicsId = modelPhysicsId;
trace.c.bodyId = modelBodyId;
trace.c.selfId = selfId;
if (modelContentsOverride != 0 && trace.c.contentFlags != 0) {
trace.c.contentFlags = modelContentsOverride;
}
}