Files
2026-08-09 04:29:27 -07:00

264 lines
9.6 KiB
C++

#include "gamelib/physics/push.h"
#include "gamelib/physics/clipmodel.h"
#include <algorithm>
#include <cstring>
int GameLib_GetPushPhysicsObjects(idClip* clip, const idBounds& bounds,
idPhysics** objects, int maxObjects);
bool GameLib_GetPushSweepBounds(idPhysics* pusher,
const idVec3& oldOrigin, const idVec3& newOrigin,
const idMat3& oldAxis, const idMat3& newAxis, idBounds& bounds);
idRotation GameLib_GetPushRotation(const idVec3& origin,
const idMat3& oldAxis, const idMat3& newAxis);
bool GameLib_RotatePushObjectToAxial(idPhysics* physics,
const idVec3& rotationPoint, const idClipModel* clipModel);
namespace {
void ClearTrace(trace_t& trace, const idVec3& end,
const idMat3& endAxis) {
std::memset(&trace, 0, sizeof(trace));
trace.fraction = 1.0f;
trace.endpos = end;
trace.endAxis = endAxis;
trace.c.entityNum = 0x1FFF;
trace.c.physicsId = -1;
}
} // namespace
idPush::idPush(idClip* const clip_)
: clip(clip_)
, pushed(4) {
}
void idPush::Init(idClip* const clip_) { clip = clip_; }
void idPush::InitSavingPushedPhysicsObjectState() { pushed.Clear(); }
void idPush::SavePhysicsObjectState(idPhysics* const physics) {
if (physics == nullptr) return;
for (int index = 0; index < pushed.Num(); ++index)
if (pushed[index] == physics) return;
physics->SaveState();
pushed.Append(physics);
}
void idPush::RestorePushedPhysicsObjectState() {
for (int index = 0; index < pushed.Num(); ++index)
if (pushed[index] != nullptr) pushed[index]->RestoreState();
}
void idPush::SetPushedOnPushedPhysicsObjects(const int deltaTime) {
for (int index = 0; index < pushed.Num(); ++index)
if (pushed[index] != nullptr) pushed[index]->SetPushed(deltaTime);
}
void idPush::ClipPhysicsObjectRotation(trace_t& trace,
idPhysics* const physics, const idClipModel* const clipModel,
idClipModel* const skip, const idRotation& rotation) {
if (skip != nullptr) skip->Disable();
physics->ClipRotation(&trace, &rotation, clipModel);
if (skip != nullptr) skip->Enable();
}
void idPush::ClipPhysicsObjectTranslation(trace_t& trace,
idPhysics* const physics, const idClipModel* const clipModel,
idClipModel* const skip, const idVec3& translation) {
if (skip != nullptr) skip->Disable();
physics->ClipTranslation(&trace, &translation, clipModel);
if (skip != nullptr) skip->Enable();
}
bool idPush::CanPushPhysicsObject(idPhysics* const physics,
const int flags, idPhysics* const pusher) {
if (physics == nullptr || pusher == nullptr || physics == pusher) {
return false;
}
const int pusherContents = pusher->GetContents(-1);
if (!physics->IsPushable(pusherContents) ||
(physics->GetClipMask(-1) & pusherContents) == 0) {
return false;
}
if ((flags & 4) != 0 && physics->GetType() != PHYSICS_RIGIDBODY) {
return false;
}
if ((flags & 8) != 0 &&
pusher->IsGroundEntity(physics->GetEntityNumber())) {
return false;
}
return true;
}
int idPush::GetPhysicsObjectsTouchingBounds(idPhysics** const objects,
const idBounds& bounds, const int flags, idPhysics* const pusher) {
if (clip == nullptr || objects == nullptr) return 0;
idPhysics* candidates[256]{};
const int count = (std::max)(0, (std::min)(256,
GameLib_GetPushPhysicsObjects(clip, bounds, candidates, 256)));
int accepted = 0;
for (int index = 0; index < count; ++index) {
if (!CanPushPhysicsObject(candidates[index], flags, pusher)) continue;
bool duplicate = false;
for (int check = 0; check < accepted; ++check)
if (objects[check] == candidates[index]) duplicate = true;
if (!duplicate) objects[accepted++] = candidates[index];
}
return accepted;
}
bool idPush::RotatePhysicsObjectToAxial(idPhysics* const physics,
const idVec3& rotationPoint, const idClipModel* const clipModel) {
return physics != nullptr && GameLib_RotatePushObjectToAxial(
physics, rotationPoint, clipModel);
}
idPush::pushResult_t idPush::RecursiveTryTranslatePushPhysicsObject(
trace_t& results, trace_t& trace, idPhysics* const pusher,
const int flags, const idVec3& translation, float& mass) {
idPhysics* const blocking = idPhysics::GetPhysicsForId(trace.c.physicsId);
if (!CanPushPhysicsObject(blocking, flags & ~8, pusher)) {
results = trace;
return PUSH_BLOCKED;
}
const pushResult_t result = TryTranslatePushPhysicsObject(results,
blocking, pusher->GetClipModel(0), flags, *blocking->GetOrigin(0),
translation, mass);
if (result == PUSH_OK) {
pusher->ClipTranslation(&trace, &translation,
pusher->GetClipModel(0));
}
return result;
}
idPush::pushResult_t idPush::TryTranslatePushPhysicsObject(
trace_t& results, idPhysics* const check,
idClipModel* const pusherModel, const int flags,
const idVec3& oldOrigin, const idVec3& translation, float& mass) {
if (check == nullptr) return PUSH_BLOCKED;
trace_t trace{};
ClipPhysicsObjectTranslation(trace, check, pusherModel,
pusherModel, translation);
if (trace.fraction < 1.0f) {
const pushResult_t recursive = RecursiveTryTranslatePushPhysicsObject(
results, trace, check, flags, translation, mass);
if (recursive != PUSH_OK) return recursive;
}
SavePhysicsObjectState(check);
check->Translate(&translation, -1);
mass += check->GetMass(-1);
ClearTrace(results, oldOrigin + translation, *check->GetAxis(0));
return PUSH_OK;
}
idPush::pushResult_t idPush::RecursiveTryRotatePushPhysicsObject(
trace_t& results, trace_t& trace, idPhysics* const pusher,
const int flags, const idRotation& rotation, float& mass) {
idPhysics* const blocking = idPhysics::GetPhysicsForId(trace.c.physicsId);
if (!CanPushPhysicsObject(blocking, flags & ~8, pusher)) {
results = trace;
return PUSH_BLOCKED;
}
return TryRotatePushPhysicsObject(results, blocking,
pusher->GetClipModel(0), flags, rotation.GetOrigin(),
*blocking->GetAxis(0), rotation, mass);
}
idPush::pushResult_t idPush::TryRotatePushPhysicsObject(trace_t& results,
idPhysics* const check, idClipModel* const pusherModel,
const int flags, const idVec3& rotationPoint, const idMat3& oldAxis,
const idRotation& rotation, float& mass) {
if (check == nullptr) return PUSH_BLOCKED;
trace_t trace{};
ClipPhysicsObjectRotation(trace, check, pusherModel, pusherModel,
rotation);
if (trace.fraction < 1.0f) {
const pushResult_t recursive = RecursiveTryRotatePushPhysicsObject(
results, trace, check, flags, rotation, mass);
if (recursive != PUSH_OK) return recursive;
}
SavePhysicsObjectState(check);
check->Rotate(&rotation, -1);
if ((flags & 2) != 0 && !RotatePhysicsObjectToAxial(check,
rotationPoint, pusherModel)) {
check->RestoreState();
return PUSH_BLOCKED;
}
mass += check->GetMass(-1);
ClearTrace(results, *check->GetOrigin(0), oldAxis * rotation.ToMat3());
return PUSH_OK;
}
float idPush::ClipTranslationalPush(trace_t& results,
idPhysics* const pusher, const int flags, const idVec3& newOrigin,
const idVec3& translation) {
idBounds sweep;
if (!GameLib_GetPushSweepBounds(pusher,
newOrigin - translation, newOrigin, *pusher->GetAxis(0),
*pusher->GetAxis(0), sweep)) return 0.0f;
idPhysics* objects[256]{};
const int count = GetPhysicsObjectsTouchingBounds(objects, sweep,
flags, pusher);
float mass = 0.0f;
for (int index = 0; index < count; ++index) {
if (TryTranslatePushPhysicsObject(results, objects[index],
pusher->GetClipModel(0), flags, *objects[index]->GetOrigin(0),
translation, mass) != PUSH_OK) return mass;
}
return mass;
}
float idPush::ClipRotationalPush(trace_t& results,
idPhysics* const pusher, const int flags, const idMat3& newAxis,
const idRotation& rotation) {
idBounds sweep;
const idVec3 origin = *pusher->GetOrigin(0);
if (!GameLib_GetPushSweepBounds(pusher, origin, origin,
*pusher->GetAxis(0), newAxis, sweep)) return 0.0f;
idPhysics* objects[256]{};
const int count = GetPhysicsObjectsTouchingBounds(objects, sweep,
flags, pusher);
float mass = 0.0f;
for (int index = 0; index < count; ++index) {
if (TryRotatePushPhysicsObject(results, objects[index],
pusher->GetClipModel(0), flags, origin,
*objects[index]->GetAxis(0), rotation, mass) != PUSH_OK)
return mass;
}
return mass;
}
float idPush::ClipPush(trace_t& results, idPhysics* const pusher,
const int flags, const idVec3& oldOrigin, const idMat3& oldAxis,
idVec3& newOrigin, idMat3& newAxis) {
ClearTrace(results, newOrigin, newAxis);
if (pusher == nullptr) return 0.0f;
InitSavingPushedPhysicsObjectState();
float totalMass = 0.0f;
const idVec3 translation = newOrigin - oldOrigin;
if (translation.LengthSqr() > 0.0f) {
totalMass += ClipTranslationalPush(results, pusher, flags,
newOrigin, translation);
if (results.fraction < 1.0f) {
RestorePushedPhysicsObjectState();
newOrigin = oldOrigin;
newAxis = oldAxis;
return totalMass;
}
}
const idRotation rotation = GameLib_GetPushRotation(newOrigin,
oldAxis, newAxis);
if (rotation.GetAngle() != 0.0f) {
totalMass += ClipRotationalPush(results, pusher, flags,
newAxis, rotation);
if (results.fraction < 1.0f) {
RestorePushedPhysicsObjectState();
newOrigin = oldOrigin;
newAxis = oldAxis;
}
}
return totalMass;
}