291 lines
9.7 KiB
C++
291 lines
9.7 KiB
C++
#include "gamelib/effectphysics/effectphysicsbroadphase.h"
|
|
|
|
#include "gamelib/physics/physics.h"
|
|
|
|
#include <algorithm>
|
|
|
|
bool GameLib_GetEffectPhysicsMotionResult(
|
|
idCollisionQuery& query, trace_t& result);
|
|
idCollisionQuery GameLib_SubmitEffectPhysicsMotion(idClip* clip,
|
|
const idTraceModel& traceModel, const idSPObject& object,
|
|
const idPositionedCollisionModel* models, int numModels,
|
|
bool worldCollisionModelOnly);
|
|
|
|
idEffectPhysicsBroadPhase::idEffectPhysicsBroadPhase()
|
|
: clip(nullptr)
|
|
, traceModelCache(nullptr)
|
|
, objects(0)
|
|
, markers{idList<idSPMarker, 59>(0), idList<idSPMarker, 59>(0),
|
|
idList<idSPMarker, 59>(0)} {
|
|
}
|
|
|
|
idEffectPhysicsBroadPhase::~idEffectPhysicsBroadPhase() {
|
|
clip = nullptr;
|
|
traceModelCache = nullptr;
|
|
objects.Clear();
|
|
for (idList<idSPMarker, 59>& axisMarkers : markers) {
|
|
axisMarkers.Clear();
|
|
}
|
|
}
|
|
|
|
void idEffectPhysicsBroadPhase::Init(idClip* const clip_,
|
|
idTraceModelCache* const traceModelCache_) {
|
|
clip = clip_;
|
|
traceModelCache = traceModelCache_;
|
|
}
|
|
|
|
const idBounds* idEffectPhysicsBroadPhase::ResolveBounds(
|
|
const idSPObject* const object, const idBounds* const suppliedBounds) const {
|
|
if (suppliedBounds != nullptr) {
|
|
return suppliedBounds;
|
|
}
|
|
if (traceModelCache == nullptr) {
|
|
return nullptr;
|
|
}
|
|
const idTraceModelCache::trmCache_t* const entry =
|
|
traceModelCache->GetEntry(object->traceModelIndex);
|
|
return entry != nullptr && entry->trm != nullptr
|
|
? &entry->trm->bounds
|
|
: nullptr;
|
|
}
|
|
|
|
void idEffectPhysicsBroadPhase::AddObject(idSPObject* const object,
|
|
const idVec3& origin, const idMat3& axis, const idBounds* const bounds) {
|
|
const idBounds* const localBounds = ResolveBounds(object, bounds);
|
|
if (localBounds == nullptr) {
|
|
return;
|
|
}
|
|
|
|
idBounds transformedBounds;
|
|
transformedBounds.FromTransformedBounds(*localBounds, origin, axis);
|
|
object->origin = origin;
|
|
object->axis = axis;
|
|
object->absBounds.SetBounds(transformedBounds);
|
|
|
|
int slot = 0;
|
|
while (slot < objects.Num() && objects[slot] != nullptr) {
|
|
++slot;
|
|
}
|
|
if (slot == objects.Num()) {
|
|
object->id = objects.Append(object);
|
|
} else {
|
|
object->id = slot;
|
|
objects[slot] = object;
|
|
}
|
|
RebuildBroadPhase();
|
|
}
|
|
|
|
void idEffectPhysicsBroadPhase::RemoveObject(idSPObject* const object) {
|
|
if (object == nullptr || object->id < 0 || object->id >= objects.Num() ||
|
|
objects[object->id] != object) {
|
|
return;
|
|
}
|
|
objects[object->id] = nullptr;
|
|
object->id = -1;
|
|
object->overlap.Clear();
|
|
for (int side = 0; side < 2; ++side) {
|
|
for (int axis = 0; axis < 3; ++axis) {
|
|
object->index[side][axis] = -1;
|
|
}
|
|
}
|
|
RebuildBroadPhase();
|
|
}
|
|
|
|
void idEffectPhysicsBroadPhase::RebuildBroadPhase() {
|
|
for (idList<idSPMarker, 59>& axisMarkers : markers) {
|
|
axisMarkers.Clear();
|
|
}
|
|
for (int index = 0; index < objects.Num(); ++index) {
|
|
idSPObject* const object = objects[index];
|
|
if (object == nullptr) {
|
|
continue;
|
|
}
|
|
object->overlap.Clear();
|
|
if (object->motion.worldCollisionOnly) {
|
|
continue;
|
|
}
|
|
for (int axis = 0; axis < 3; ++axis) {
|
|
idSPMarker minimum = {
|
|
object->absBounds.b[0][axis],
|
|
static_cast<unsigned short>(object->id)};
|
|
idSPMarker maximum = {
|
|
object->absBounds.b[1][axis],
|
|
static_cast<unsigned short>(object->id | 0x8000)};
|
|
markers[axis].Append(minimum);
|
|
markers[axis].Append(maximum);
|
|
}
|
|
}
|
|
|
|
for (int axis = 0; axis < 3; ++axis) {
|
|
idList<idSPMarker, 59>& axisMarkers = markers[axis];
|
|
std::sort(axisMarkers.Ptr(), axisMarkers.Ptr() + axisMarkers.Num(),
|
|
[](const idSPMarker& left, const idSPMarker& right) {
|
|
if (left.value != right.value) {
|
|
return left.value < right.value;
|
|
}
|
|
return left.data < right.data;
|
|
});
|
|
for (int markerIndex = 0; markerIndex < axisMarkers.Num();
|
|
++markerIndex) {
|
|
const unsigned short data = axisMarkers[markerIndex].data;
|
|
const int objectIndex = data & 0x7FFF;
|
|
if (objectIndex < objects.Num() && objects[objectIndex] != nullptr) {
|
|
objects[objectIndex]->index[data >> 15][axis] = markerIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int first = 0; first < objects.Num(); ++first) {
|
|
idSPObject* const firstObject = objects[first];
|
|
if (firstObject == nullptr || firstObject->motion.worldCollisionOnly) {
|
|
continue;
|
|
}
|
|
for (int second = first + 1; second < objects.Num(); ++second) {
|
|
idSPObject* const secondObject = objects[second];
|
|
if (secondObject == nullptr ||
|
|
secondObject->motion.worldCollisionOnly ||
|
|
!firstObject->absBounds.IntersectsBounds(
|
|
secondObject->absBounds)) {
|
|
continue;
|
|
}
|
|
firstObject->overlap.Append(static_cast<short>(second));
|
|
secondObject->overlap.Append(static_cast<short>(first));
|
|
}
|
|
}
|
|
}
|
|
|
|
void idEffectPhysicsBroadPhase::MoveObject(idSPObject* const object) {
|
|
(void)object;
|
|
RebuildBroadPhase();
|
|
}
|
|
|
|
void idEffectPhysicsBroadPhase::SetObjectPosition(idSPObject* const object,
|
|
const idVec3& origin, const idMat3& axis, const idBounds* const bounds) {
|
|
const idBounds* const localBounds = ResolveBounds(object, bounds);
|
|
if (localBounds == nullptr) {
|
|
return;
|
|
}
|
|
|
|
idBounds transformedBounds;
|
|
transformedBounds.FromTransformedBounds(*localBounds, origin, axis);
|
|
object->absBounds.SetBounds(transformedBounds);
|
|
object->origin = origin;
|
|
object->axis = axis;
|
|
if (!object->motion.worldCollisionOnly) {
|
|
MoveObject(object);
|
|
}
|
|
object->motion.valid = false;
|
|
object->query.offset = 0;
|
|
}
|
|
|
|
void idEffectPhysicsBroadPhase::ApplyMotion(idSPObject* const object,
|
|
const idVec3& translation, const idRotation& rotation) {
|
|
const idBounds* const localBounds = ResolveBounds(object, nullptr);
|
|
if (localBounds == nullptr) {
|
|
return;
|
|
}
|
|
|
|
idBounds startBounds;
|
|
startBounds.FromTransformedBounds(*localBounds, object->origin,
|
|
object->axis);
|
|
idMat3 endAxis = object->axis;
|
|
rotation.RotateAxis(endAxis);
|
|
idBounds endBounds;
|
|
endBounds.FromTransformedBounds(*localBounds,
|
|
object->origin + translation, endAxis);
|
|
|
|
idBounds motionBounds;
|
|
for (int axis = 0; axis < 3; ++axis) {
|
|
motionBounds[0][axis] = (std::min)(
|
|
startBounds[0][axis], endBounds[0][axis]) - 1.0f;
|
|
motionBounds[1][axis] = (std::max)(
|
|
startBounds[1][axis], endBounds[1][axis]) + 1.0f;
|
|
}
|
|
object->absBounds.SetBounds(motionBounds);
|
|
if (!object->motion.worldCollisionOnly) {
|
|
MoveObject(object);
|
|
}
|
|
|
|
object->motion.valid = true;
|
|
object->motion.translation = translation;
|
|
object->motion.rotationVec = rotation.vec;
|
|
object->motion.rotationAngle = rotation.angle;
|
|
}
|
|
|
|
int idEffectPhysicsBroadPhase::GetPositionedModels(
|
|
const idSPObject* const object, idPositionedCollisionModel* const models,
|
|
const int maxModels) const {
|
|
if (traceModelCache == nullptr) {
|
|
return 0;
|
|
}
|
|
|
|
int count = 0;
|
|
for (int index = 0; index < object->overlap.Num() && count < maxModels;
|
|
++index) {
|
|
const int objectIndex = object->overlap[index];
|
|
if (objectIndex < 0 || objectIndex >= objects.Num()) {
|
|
continue;
|
|
}
|
|
const idSPObject* const other = objects[objectIndex];
|
|
if (other == nullptr || other->traceModelIndex == -1 ||
|
|
other->motion.ignore) {
|
|
continue;
|
|
}
|
|
const idTraceModelCache::trmCache_t* const entry =
|
|
traceModelCache->GetEntry(other->traceModelIndex);
|
|
if (entry == nullptr || entry->collisionModel == nullptr) {
|
|
continue;
|
|
}
|
|
|
|
idPositionedCollisionModel& model = models[count++];
|
|
model.model = entry->collisionModel;
|
|
model.modelJoints = nullptr;
|
|
model.modelOrigin = other->origin;
|
|
model.modelAxis = other->axis;
|
|
model.modelEntityNum = 0x1FFF;
|
|
model.modelPhysicsId = other->id;
|
|
model.modelBodyId = 0;
|
|
model.modelContentsOverride = other->clipMask;
|
|
model.modelQuery = other->query;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
void idEffectPhysicsBroadPhase::SubmitQueries(
|
|
const bool worldCollisionModelOnly) {
|
|
if (traceModelCache == nullptr || clip == nullptr) {
|
|
return;
|
|
}
|
|
|
|
idPositionedCollisionModel models[128]{};
|
|
for (int index = 0; index < objects.Num(); ++index) {
|
|
idSPObject* const object = objects[index];
|
|
if (object == nullptr || !object->motion.valid) {
|
|
continue;
|
|
}
|
|
const idTraceModelCache::trmCache_t* const entry =
|
|
traceModelCache->GetEntry(object->traceModelIndex);
|
|
if (entry == nullptr || entry->trm == nullptr) {
|
|
object->motion.valid = false;
|
|
continue;
|
|
}
|
|
const int numModels = object->motion.worldCollisionOnly
|
|
? 0
|
|
: GetPositionedModels(object, models, 128);
|
|
object->query = GameLib_SubmitEffectPhysicsMotion(
|
|
clip, *entry->trm, *object, models, numModels,
|
|
worldCollisionModelOnly);
|
|
object->motion.valid = false;
|
|
}
|
|
}
|
|
|
|
bool idEffectPhysicsBroadPhase::GetMotionResult(trace_t* const result,
|
|
idSPObject* const object) {
|
|
const bool available = GameLib_GetEffectPhysicsMotionResult(
|
|
object->query, *result);
|
|
object->origin = result->endpos;
|
|
object->axis = result->endAxis;
|
|
object->axis.OrthoNormalizeSelf();
|
|
return available;
|
|
}
|