Files
tech5/source/engine/cm/collisionmodelmanager_debug.cpp
T
2026-08-09 01:29:43 -07:00

236 lines
8.7 KiB
C++

#include "cm/collisionmodelmanager.h"
#include "cm/collisionmodel.h"
#include "cm/jobs/polygonmodel/polygonmodel.h"
#include "cm/jobs/polygonmodel/polygonmodeldata.h"
#include "cm/jobs/spheremodel/spheremodel.h"
#include "idlib/geometry/jointtransform.h"
#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
namespace {
idCollisionDebugDrawSink* debugDrawSink = nullptr;
struct contentsName_t {
const char* name;
int flag;
};
const contentsName_t contentsNames[] = {
{ "solid", 0x00000001 }, { "opaque", 0x00000002 },
{ "water", 0x00000004 }, { "playerclip", 0x00000008 },
{ "monsterclip", 0x00000010 }, { "vehicleclip", 0x00000020 },
{ "moveableclip", 0x00000040 }, { "shotclip", 0x00000080 },
{ "ikclip", 0x00000100 }, { "aiaware", 0x00000200 },
{ "ai", 0x00000400 }, { "projectile", 0x00000800 },
{ "corpse", 0x00001000 }, { "breakable", 0x00002000 },
{ "trigger", 0x00004000 }, { "player", 0x00008000 },
{ "vehicle", 0x00010000 }, { "obstacle", 0x00020000 },
{ "contextualcover_clip", 0x00040000 },
{ "playercoverclip", 0x00080000 },
{ "monstercoverclip", 0x00100000 },
{ "playerfocus", 0x00200000 }, { "pushable", 0x00400000 },
{ "shield", 0x00800000 }, { "tickclip", 0x01000000 },
{ "aas_fly", 0x02000000 }, { "aas_solid", 0x04000000 },
{ "aas_obstacle", 0x08000000 },
{ "aas_cluster_portal", 0x10000000 },
{ "aas_walkable_wall", 0x20000000 },
{ "nocover", 0x40000000 },
{ "do_not_use", static_cast<int>(0x80000000u) }
};
idVec3 TransformPoint(const idVec3& point, const idVec3& origin,
const idMat3& axis) {
return origin + idVec3(
axis[0].x * point.x + axis[1].x * point.y + axis[2].x * point.z,
axis[0].y * point.x + axis[1].y * point.y + axis[2].y * point.z,
axis[0].z * point.x + axis[1].z * point.y + axis[2].z * point.z);
}
bool InRadius(const idVec3& point, const idVec3& viewOrigin,
const float radius) {
return radius <= 0.0f
|| (point - viewOrigin).LengthSqr() <= radius * radius;
}
void DrawEdge(const cm_subModelPtrs_t* const pointers,
const std::uint16_t edgeReference, const idVec3& origin,
const idMat3& axis, const idVec3& viewOrigin, const float radius,
const int lifeTime) {
if (debugDrawSink == nullptr) {
return;
}
const cm_edge_t& edge = pointers->edges[CM_EdgeIndex(edgeReference)];
const idVec3 start = TransformPoint(pointers->vertices[
CM_EdgeStartVertex(edge, edgeReference)].p, origin, axis);
const idVec3 end = TransformPoint(pointers->vertices[
CM_EdgeEndVertex(edge, edgeReference)].p, origin, axis);
if (InRadius(start, viewOrigin, radius)
|| InRadius(end, viewOrigin, radius)) {
debugDrawSink->DrawLine(start, end,
(edgeReference & 0x4000) != 0, lifeTime);
}
}
void DrawPolygon(const cm_subModelPtrs_t* const pointers,
const cm_polygon_t& polygon, const idVec3& origin,
const idMat3& axis, const idVec3& viewOrigin, const float radius,
const int lifeTime) {
if (debugDrawSink == nullptr || polygon.numEdges == 0) {
return;
}
std::vector<idVec3> points;
points.reserve(polygon.numEdges);
bool visible = radius <= 0.0f;
for (int index = 0; index < polygon.numEdges; ++index) {
const std::uint16_t edgeReference = pointers->polygonEdges[
polygon.firstEdge + index];
const cm_edge_t& edge = pointers->edges[CM_EdgeIndex(edgeReference)];
points.push_back(TransformPoint(pointers->vertices[
CM_EdgeStartVertex(edge, edgeReference)].p, origin, axis));
visible = visible || InRadius(points.back(), viewOrigin, radius);
}
if (!visible) {
return;
}
const cm_material_t& material = pointers->materials[polygon.material];
debugDrawSink->DrawPolygon(points.data(),
static_cast<int>(points.size()), material.contentFlags,
material.surfaceFlags, material.surfaceType, lifeTime);
for (int index = 0; index < polygon.numEdges; ++index) {
DrawEdge(pointers, pointers->polygonEdges[polygon.firstEdge + index],
origin, axis, viewOrigin, radius, lifeTime);
}
}
void DrawNodePolygons(const cm_subModelPtrs_t* const pointers,
const cm_subModelData_t& data, const idVec3& origin,
const idMat3& axis, const idVec3& viewOrigin, const float radius,
const int lifeTime) {
for (int polygon = 0; polygon < data.numPolygons; ++polygon) {
DrawPolygon(pointers, pointers->polygons[polygon], origin, axis,
viewOrigin, radius, lifeTime);
}
}
void SpeedTest(const idVec3*) {
// The recovered benchmark is command/CVar driven. Query timing belongs
// to the console integration layer; collision drawing itself is live.
}
void DebugTranslationFailure(const idVec3*, const idMat3*) {}
void DebugRotationFailure(const idVec3*, const idMat3*) {}
void DebugFailedQuery(const idVec3*, const idMat3*) {}
} // namespace
int ContentsFromString(const char* const string) {
if (string == nullptr) {
return 0;
}
int flags = 0;
const char* cursor = string;
while (*cursor != '\0') {
while (*cursor != '\0'
&& (std::isspace(static_cast<unsigned char>(*cursor))
|| *cursor == ',')) {
++cursor;
}
const char* const begin = cursor;
while (*cursor != '\0'
&& !std::isspace(static_cast<unsigned char>(*cursor))
&& *cursor != ',') {
++cursor;
}
if (cursor == begin) {
continue;
}
std::string token(begin, cursor);
if (token.compare(0, 9, "CONTENTS_") == 0
|| token.compare(0, 9, "contents_") == 0) {
token.erase(0, 9);
}
for (const contentsName_t& entry : contentsNames) {
if (_stricmp(token.c_str(), entry.name) == 0) {
flags |= entry.flag;
break;
}
}
}
return flags;
}
void idCollisionModelManager::SetDebugDrawSink(
idCollisionDebugDrawSink* const sink) {
debugDrawSink = sink;
}
void idCollisionModelManager::DrawCollisionModel(
idCollisionModel* const model, const idJointMat* const modelJoints,
const idVec3& modelOrigin, const idMat3& modelAxis,
const idVec3& viewOrigin, const idMat3&, const float radius,
const int lifeTime) {
if (debugDrawSink == nullptr || model == nullptr) {
return;
}
idCollisionModelLocal* const local =
dynamic_cast<idCollisionModelLocal*>(model);
if (local == nullptr) {
return;
}
if (local->modelType == CM_SPHEREMODEL && local->sphereModel != nullptr) {
cm_sphereModelPtrs_t spheres{};
const int count = idSphereModelCollisionDetection::
SetupCollisionSpherePtrs(local->sphereModel, spheres);
for (int index = 0; index < count; ++index) {
idVec3 center(spheres.offsetX[index], spheres.offsetY[index],
spheres.offsetZ[index]);
if (modelJoints != nullptr) {
const idJointMat& joint = modelJoints[spheres.joint[index]];
center.Set(
joint.mat[0] * center.x + joint.mat[1] * center.y
+ joint.mat[2] * center.z + joint.mat[3],
joint.mat[4] * center.x + joint.mat[5] * center.y
+ joint.mat[6] * center.z + joint.mat[7],
joint.mat[8] * center.x + joint.mat[9] * center.y
+ joint.mat[10] * center.z + joint.mat[11]);
}
center = TransformPoint(center, modelOrigin, modelAxis);
if (InRadius(center, viewOrigin, radius)) {
debugDrawSink->DrawSphere(center, spheres.radius[index],
spheres.surfType[index], lifeTime);
}
}
return;
}
if (local->modelType != CM_POLYGONMODEL) {
return;
}
for (int index = 0; index < local->polygonModel.numSubModels; ++index) {
const cm_subModel_t& subModel = local->polygonModel.subModels[index];
const cm_subModelData_t* const data = AcquireSubModelData(subModel);
if (data != nullptr && data->header.loadedSize != 32) {
cm_subModelPtrs_t pointers{};
idPolygonModelCollisionDetection::SetupSubModelPtrsFromData(
pointers, data);
DrawNodePolygons(&pointers, *data, modelOrigin, modelAxis,
viewOrigin, radius, lifeTime);
}
ReleaseSubModelData(subModel, data);
}
}
void idCollisionModelManager::DebugOutput(const idVec3& viewOrigin,
const idMat3& viewAxis) {
SpeedTest(&viewOrigin);
DebugTranslationFailure(&viewOrigin, &viewAxis);
DebugRotationFailure(&viewOrigin, &viewAxis);
DebugFailedQuery(&viewOrigin, &viewAxis);
}