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

362 lines
15 KiB
C++

#include "cm/jobs/collisionquery.h"
#include "cm/jobs/polygonmodel/polygonmodel.h"
#include "cm/jobs/spheremodel/spheremodel.h"
#include "idlib/geometry/tracemodel.h"
#include <algorithm>
#include <cstring>
namespace {
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);
}
void FinishQuery(idTraceWork& tw, queryResults_t& results,
const queryParms_t& parms) {
if (results.query.status == QUERY_STATUS_PENDING) {
results.query.status = QUERY_STATUS_SUCCESS;
}
for (int index = 0; index < 5; ++index) {
results.query.profile[index] = static_cast<std::uint32_t>(
tw.profile[index]);
}
results.query.type = parms.type;
results.query.done = 1;
results.query.merged = parms.autoMerge ? 1 : 0;
}
template <typename TraceFunction>
void ExecutePolygonOperation(idTraceWork& tw, queryResults_t& results,
const queryParms_t& parms, const idTraceModel* trm,
TraceFunction&& traceFunction) {
trace_t startContents;
if (parms.testQuery && trm != nullptr
&& (parms.type == TRACE_TRANSLATION
|| parms.type == TRACE_ROTATION)) {
idPolygonModelCollisionDetection::StartContents(&tw, &startContents,
parms.start, trm, parms.trmAxis, parms.contentMask,
parms.modelOrigin, parms.modelAxis);
traceFunction(tw);
idPolygonModelCollisionDetection::FinishContents(&tw,
parms.modelOrigin, parms.modelAxis, parms.modelEntityNum,
parms.modelPhysicsId, parms.modelBodyId, parms.selfId,
parms.modelContentsOverride);
if (startContents.c.contentFlags != 0) {
results.query.status = static_cast<queryStatus_t>(
results.query.status | QUERY_STATUS_BAD_START);
}
}
switch (parms.type) {
case TRACE_TRANSLATION:
if (idPolygonModelCollisionDetection::StartTranslation(&tw,
reinterpret_cast<trace_t*>(results.data), nullptr,
parms.start, parms.end, trm, parms.trmAxis,
parms.contentMask, parms.modelOrigin, parms.modelAxis)) {
traceFunction(tw);
idPolygonModelCollisionDetection::FinishTranslation(&tw,
parms.start, parms.end, parms.modelOrigin, parms.modelAxis,
parms.modelEntityNum, parms.modelPhysicsId,
parms.modelBodyId, parms.selfId,
parms.modelContentsOverride);
}
break;
case TRACE_TRANSLATION_POINT:
if (idPolygonModelCollisionDetection::StartTranslationPoint(&tw,
reinterpret_cast<trace_t*>(results.data), parms.start,
parms.end, parms.contentMask, parms.modelOrigin,
parms.modelAxis)) {
traceFunction(tw);
idPolygonModelCollisionDetection::FinishTranslation(&tw,
parms.start, parms.end, parms.modelOrigin, parms.modelAxis,
parms.modelEntityNum, parms.modelPhysicsId,
parms.modelBodyId, parms.selfId,
parms.modelContentsOverride);
}
break;
case TRACE_ROTATION:
if (idPolygonModelCollisionDetection::StartRotation(&tw,
reinterpret_cast<trace_t*>(results.data),
parms.rotationOrigin, parms.rotationAxis,
parms.rotationAngle, parms.start, trm, parms.trmAxis,
parms.contentMask, parms.modelOrigin, parms.modelAxis)) {
traceFunction(tw);
idPolygonModelCollisionDetection::FinishRotation(&tw,
parms.rotationOrigin, parms.rotationAxis,
parms.rotationAngle, parms.start, parms.trmAxis,
parms.modelOrigin, parms.modelAxis, parms.modelEntityNum,
parms.modelPhysicsId, parms.modelBodyId, parms.selfId,
parms.modelContentsOverride);
}
break;
case TRACE_ROTATION_POINT:
if (idPolygonModelCollisionDetection::StartRotationPoint(&tw,
reinterpret_cast<trace_t*>(results.data),
parms.rotationOrigin, parms.rotationAxis,
parms.rotationAngle, parms.start, parms.contentMask,
parms.modelOrigin, parms.modelAxis)) {
traceFunction(tw);
idPolygonModelCollisionDetection::FinishRotation(&tw,
parms.rotationOrigin, parms.rotationAxis,
parms.rotationAngle, parms.start, parms.trmAxis,
parms.modelOrigin, parms.modelAxis, parms.modelEntityNum,
parms.modelPhysicsId, parms.modelBodyId, parms.selfId,
parms.modelContentsOverride);
}
break;
case TRACE_CONTENTS:
if (trm != nullptr) {
idPolygonModelCollisionDetection::StartContents(&tw,
reinterpret_cast<trace_t*>(results.data), parms.start,
trm, parms.trmAxis, parms.contentMask, parms.modelOrigin,
parms.modelAxis);
traceFunction(tw);
idPolygonModelCollisionDetection::FinishContents(&tw,
parms.modelOrigin, parms.modelAxis, parms.modelEntityNum,
parms.modelPhysicsId, parms.modelBodyId, parms.selfId,
parms.modelContentsOverride);
}
break;
case TRACE_CONTENTS_POINT:
idPolygonModelCollisionDetection::StartContentsPoint(&tw,
reinterpret_cast<trace_t*>(results.data), parms.start,
parms.contentMask, parms.modelOrigin, parms.modelAxis);
traceFunction(tw);
idPolygonModelCollisionDetection::FinishContents(&tw,
parms.modelOrigin, parms.modelAxis, parms.modelEntityNum,
parms.modelPhysicsId, parms.modelBodyId, parms.selfId,
parms.modelContentsOverride);
break;
case TRACE_CONTACTS_UNI_DIR:
case TRACE_CONTACTS_OMNI_DIR:
if (trm != nullptr) {
contactsResult_t& contacts =
*reinterpret_cast<contactsResult_t*>(results.data);
contacts.numContacts = 0;
idPolygonModelCollisionDetection::StartContacts(&tw, &contacts,
parms.start, parms.dir, parms.depth, *trm,
parms.trmAxis, parms.contentMask, parms.modelOrigin,
parms.modelAxis);
traceFunction(tw);
idPolygonModelCollisionDetection::FinishContacts(&tw, 0,
parms.modelOrigin, parms.modelAxis, parms.modelEntityNum,
parms.modelPhysicsId, parms.modelBodyId, parms.selfId,
parms.modelContentsOverride);
}
break;
case TRACE_CLIP:
if (trm != nullptr) {
clipResult_t& clip = *reinterpret_cast<clipResult_t*>(results.data);
clip.numVerts = 0;
clip.numIndices = 0;
idPolygonModelCollisionDetection::StartClip(&tw, &clip,
parms.start, *trm, parms.trmAxis, parms.contentMask,
parms.modelOrigin, parms.modelAxis);
traceFunction(tw);
idPolygonModelCollisionDetection::FinishClip(&tw, 0,
parms.modelOrigin, parms.modelAxis);
}
break;
default:
results.query.status = QUERY_STATUS_FAILED;
break;
}
if (parms.testQuery && trm != nullptr
&& (parms.type == TRACE_TRANSLATION
|| parms.type == TRACE_ROTATION)) {
const trace_t& motion = *reinterpret_cast<const trace_t*>(results.data);
trace_t endContents;
idPolygonModelCollisionDetection::StartContents(&tw, &endContents,
motion.endpos, trm, motion.endAxis, parms.contentMask,
parms.modelOrigin, parms.modelAxis);
traceFunction(tw);
idPolygonModelCollisionDetection::FinishContents(&tw,
parms.modelOrigin, parms.modelAxis, parms.modelEntityNum,
parms.modelPhysicsId, parms.modelBodyId, parms.selfId,
parms.modelContentsOverride);
if (endContents.c.contentFlags != 0) {
results.query.status = static_cast<queryStatus_t>(
results.query.status | QUERY_STATUS_FAILED);
}
}
FinishQuery(tw, results, parms);
}
} // namespace
void idCollisionQueryExecute::ExecuteSubModelQuery(idTraceWork* const tw,
queryResults_t& results, const queryParms_t* const parms,
const idTraceModel* const trm,
const cm_subModelData_t* const subModelData, const int subModelNum) {
if (tw == nullptr || parms == nullptr || subModelData == nullptr) {
results.query.status = QUERY_STATUS_FAILED;
results.query.done = 1;
return;
}
std::memset(tw->profile, 0, sizeof(tw->profile));
ExecutePolygonOperation(*tw, results, *parms, trm,
[subModelData, subModelNum](idTraceWork& work) {
idPolygonModelCollisionDetection::TraceThroughSubModel(&work,
subModelData, subModelNum);
});
}
void idCollisionQueryExecute::ExecutePolygonModelQuery(idTraceWork* const tw,
queryResults_t& results, const queryParms_t* const parms,
const idTraceModel* const trm,
const cm_polygonModel_t* const polygonModel) {
if (tw == nullptr || parms == nullptr || polygonModel == nullptr) {
results.query.status = QUERY_STATUS_FAILED;
results.query.done = 1;
return;
}
std::memset(tw->profile, 0, sizeof(tw->profile));
ExecutePolygonOperation(*tw, results, *parms, trm,
[polygonModel](idTraceWork& work) {
idPolygonModelCollisionDetection::TraceThroughModel(&work,
*polygonModel);
});
}
void idCollisionQueryExecute::ExecuteSphereModelQuery(
queryResults_t& results, const queryParms_t* const parms,
const idJointMat* const modelJoints,
const cm_sphereModel_t* const sphereModel) {
if (parms == nullptr || sphereModel == nullptr) {
results.query.status = QUERY_STATUS_FAILED;
results.query.done = 1;
return;
}
switch (parms->type) {
case TRACE_TRANSLATION:
case TRACE_TRANSLATION_POINT:
idSphereModelCollisionDetection::TraceThroughModel(
*reinterpret_cast<trace_t*>(results.data), *sphereModel,
parms->start, parms->end, parms->depth, parms->trmAxis,
modelJoints, parms->modelOrigin, parms->modelAxis,
parms->modelEntityNum, parms->modelPhysicsId,
parms->modelBodyId, parms->selfId,
parms->modelContentsOverride);
break;
case TRACE_CONTACTS_UNI_DIR:
case TRACE_CONTACTS_OMNI_DIR:
reinterpret_cast<contactsResult_t*>(results.data)->numContacts = 0;
break;
case TRACE_CLIP:
reinterpret_cast<clipResult_t*>(results.data)->numVerts = 0;
reinterpret_cast<clipResult_t*>(results.data)->numIndices = 0;
break;
default: {
trace_t& trace = *reinterpret_cast<trace_t*>(results.data);
std::memset(&trace, 0, sizeof(trace));
trace.fraction = 1.0f;
trace.endpos = parms->end;
trace.endAxis = parms->trmAxis;
break;
}
}
if (results.query.status == QUERY_STATUS_PENDING) {
results.query.status = QUERY_STATUS_SUCCESS;
}
std::memset(results.query.profile, 0, sizeof(results.query.profile));
results.query.type = parms->type;
results.query.done = 1;
results.query.merged = parms->autoMerge ? 1 : 0;
}
void idCollisionQueryExecute::SetupDependentParms(
queryParms_t* const resultParms, const queryParms_t* const sourceParms,
const queryResults_t* const modelPosition,
const dependencyType_t dependencyType,
const queryResults_t* const dependency1,
const queryResults_t* const dependency2) {
if (resultParms == nullptr || sourceParms == nullptr) {
return;
}
*resultParms = *sourceParms;
if (modelPosition != nullptr) {
const trace_t& position =
*reinterpret_cast<const trace_t*>(modelPosition->data);
resultParms->modelOrigin = position.endpos;
resultParms->modelAxis = position.endAxis;
}
const trace_t* const first = dependency1 != nullptr
? reinterpret_cast<const trace_t*>(dependency1->data) : nullptr;
const trace_t* const second = dependency2 != nullptr
? reinterpret_cast<const trace_t*>(dependency2->data) : nullptr;
if (first == nullptr && dependencyType != DEPENDENCY_NONE) {
return;
}
switch (dependencyType) {
case DEPENDENCY_NONE:
break;
case DEPENDENCY_MOTION_ROTATION:
resultParms->start = first->endpos;
resultParms->rotationOrigin = first->endpos;
break;
case DEPENDENCY_MOTION_CONTACTS:
resultParms->start = first->endpos;
resultParms->trmAxis = first->endAxis;
break;
case DEPENDENCY_STEPMOVE_STEP_UP:
case DEPENDENCY_STEPMOVE_STEP_DOWN:
case DEPENDENCY_SLIDEMOVE_STEP_UP_1:
case DEPENDENCY_SLIDEMOVE_STEP_UP_2:
case DEPENDENCY_SLIDEMOVE_STEP_UP_3:
case DEPENDENCY_SLIDEMOVE_STEP_UP_4:
case DEPENDENCY_SLIDEMOVE_STEP_DOWN_1:
case DEPENDENCY_SLIDEMOVE_STEP_DOWN_2:
case DEPENDENCY_SLIDEMOVE_STEP_DOWN_3:
case DEPENDENCY_SLIDEMOVE_STEP_DOWN_4:
resultParms->start = first->endpos;
resultParms->end = (first->c.flags & CONTACT_FLAG_SLIDEMOVE_FINISHED)
? first->endpos
: first->endpos + sourceParms->dir * sourceParms->depth;
break;
case DEPENDENCY_STEPMOVE_2ND_MOVE:
case DEPENDENCY_SLIDEMOVE_2ND_MOVE_1:
case DEPENDENCY_SLIDEMOVE_2ND_MOVE_2:
case DEPENDENCY_SLIDEMOVE_2ND_MOVE_3:
case DEPENDENCY_SLIDEMOVE_2ND_MOVE_4:
resultParms->start = first->endpos;
if (first->c.flags & CONTACT_FLAG_SLIDEMOVE_FINISHED) {
resultParms->end = first->endpos;
} else {
idVec3 base = sourceParms->end;
if (second != nullptr) {
base = second->endpos
+ second->c.normal * second->c.dist;
}
resultParms->end = base + sourceParms->dir
* (sourceParms->depth * first->fraction);
}
break;
case DEPENDENCY_STEPMOVE_CONTACTS:
case DEPENDENCY_SLIDEMOVE_CONTACTS:
resultParms->start = first->endpos;
resultParms->end = first->endpos;
break;
case DEPENDENCY_SLIDEMOVE_SLIDE:
resultParms->start = first->endpos;
resultParms->end = (first->c.flags & CONTACT_FLAG_SLIDEMOVE_FINISHED)
? first->endpos
: first->endpos + first->c.normal * first->c.dist;
break;
case DEPENDENCY_LOCAL_SPACE:
resultParms->start = first->endpos
+ ModelToWorldVector(first->endAxis, sourceParms->start);
resultParms->end = first->endpos
+ ModelToWorldVector(first->endAxis, sourceParms->end);
resultParms->trmAxis = first->endAxis * sourceParms->trmAxis;
break;
default:
break;
}
}