Files
tech5/source/engine/gamelib/physics/physics_af.cpp
T
2026-08-09 05:16:51 -07:00

1282 lines
49 KiB
C++

#include "gamelib/physics/physics_af.h"
#include "gamelib/physics/clipmodel.h"
#include "idlib/math/rotation.h"
#include <algorithm>
#include <cmath>
#include <cstring>
bool GameLib_GetMasterPhysicsTransform(idPhysicsCallbacks* callbacks,
idVec3& origin, idMat3& axis);
void GameLib_NotifyPhysicsActivated(idPhysicsCallbacks* callbacks,
int physicsId);
void GameLib_NotifyPhysicsDeactivated(idPhysicsCallbacks* callbacks,
int physicsId);
void GameLib_NotifyPhysicsCollision(idPhysicsCallbacks* callbacks,
int physicsId, const trace_t& collision, const idVec3& velocity);
void GameLib_SerializeAFPhysics(idSerializer* serializer,
idPhysics_AF& physics);
namespace {
constexpr int ENTITYNUM_NONE = 0x1FFF;
constexpr float RAD2DEG = 57.29577951308232f;
const idVec3 kZeroVector(0.0f, 0.0f, 0.0f);
const idMat3 kIdentityAxis(1.0f);
const idBounds kZeroBounds{{idVec3(0.0f, 0.0f, 0.0f),
idVec3(0.0f, 0.0f, 0.0f)}};
idAFBody* SelectBody(idList<idAFBody*, 71>& bodies, const int id) {
if (bodies.Num() == 0) return nullptr;
if (id < 0) return bodies[0];
return id < bodies.Num() ? bodies[id] : nullptr;
}
const idAFBody* SelectBody(const idList<idAFBody*, 71>& bodies,
const int id) {
if (bodies.Num() == 0) return nullptr;
if (id < 0) return bodies[0];
return id < bodies.Num() ? bodies[id] : nullptr;
}
idVec3 LinearVelocity(const idAFBody& body) {
return idVec3(body.current.spatialVelocity[0],
body.current.spatialVelocity[1], body.current.spatialVelocity[2]);
}
idVec3 AngularVelocity(const idAFBody& body) {
return idVec3(body.current.spatialVelocity[3],
body.current.spatialVelocity[4], body.current.spatialVelocity[5]);
}
void SetLinearVelocity(idAFBody& body, const idVec3& velocity) {
body.current.spatialVelocity[0] = velocity.x;
body.current.spatialVelocity[1] = velocity.y;
body.current.spatialVelocity[2] = velocity.z;
}
void SetAngularVelocity(idAFBody& body, const idVec3& velocity) {
body.current.spatialVelocity[3] = velocity.x;
body.current.spatialVelocity[4] = velocity.y;
body.current.spatialVelocity[5] = velocity.z;
}
void ZeroSpatial(idVec6& vector) {
for (int i = 0; i < 6; ++i) vector[i] = 0.0f;
}
float RowVelocity(const idAFConstraint& constraint, const int row) {
float velocity = 0.0f;
if (constraint.body1 != nullptr) {
for (int i = 0; i < 6; ++i)
velocity += constraint.J1(row, i)
* constraint.body1->current.spatialVelocity[i];
}
if (constraint.body2 != nullptr) {
for (int i = 0; i < 6; ++i)
velocity += constraint.J2(row, i)
* constraint.body2->current.spatialVelocity[i];
}
return velocity;
}
float RowEffectiveMass(const idAFConstraint& constraint, const int row) {
float inverseMass = 0.0f;
const idAFBody* bodyList[2] = {constraint.body1, constraint.body2};
const idSpatialMat* jacobians[2] = {&constraint.J1, &constraint.J2};
for (int bodyIndex = 0; bodyIndex < 2; ++bodyIndex) {
const idAFBody* const body = bodyList[bodyIndex];
if (body == nullptr) continue;
const idSpatialMat& jacobian = *jacobians[bodyIndex];
const idVec3 linear(jacobian(row, 0), jacobian(row, 1),
jacobian(row, 2));
const idVec3 angular(jacobian(row, 3), jacobian(row, 4),
jacobian(row, 5));
inverseMass += linear.LengthSqr() * body->invMass;
inverseMass += angular.Dot(body->inverseInertiaTensor * angular);
}
return (std::max)(inverseMass, 1.0e-6f);
}
void ApplyConstraintImpulse(idAFBody* const body,
const idSpatialMat& jacobian, const int row, const float impulse) {
if (body == nullptr) return;
idVec3 linear(jacobian(row, 0), jacobian(row, 1), jacobian(row, 2));
idVec3 angular(jacobian(row, 3), jacobian(row, 4), jacobian(row, 5));
SetLinearVelocity(*body, LinearVelocity(*body)
+ linear * (impulse * body->invMass));
SetAngularVelocity(*body, AngularVelocity(*body)
+ body->inverseInertiaTensor * angular * impulse);
}
void SolveConstraintRows(idList<idAFConstraint*, 71>& list,
const int iterations) {
for (int iteration = 0; iteration < iterations; ++iteration) {
for (int index = 0; index < list.Num(); ++index) {
idAFConstraint* const constraint = list[index];
if (constraint == nullptr) continue;
const int rows = constraint->J1.GetNumRows();
for (int row = 0; row < rows; ++row) {
float lambda = -(RowVelocity(*constraint, row)
+ constraint->c1[row])
/ RowEffectiveMass(*constraint, row);
const float oldLambda = constraint->lm[row];
const float next = (std::max)(constraint->lo[row],
(std::min)(constraint->hi[row], oldLambda + lambda));
lambda = next - oldLambda;
constraint->lm[row] = next;
ApplyConstraintImpulse(constraint->body1, constraint->J1,
row, lambda);
ApplyConstraintImpulse(constraint->body2, constraint->J2,
row, lambda);
}
}
}
}
bool IsDescendantOf(const idAFBody* body, const idAFBody* ancestor) {
for (const idAFBody* current = body; current != nullptr;
current = current->parent) {
if (current == ancestor) return true;
}
return false;
}
void ExpandBounds(idBounds& destination, const idBounds& source) {
for (int axis = 0; axis < 3; ++axis) {
destination[0][axis] = (std::min)(destination[0][axis],
source[0][axis]);
destination[1][axis] = (std::max)(destination[1][axis],
source[1][axis]);
}
}
} // namespace
// Constraint code calls through these ownership-boundary functions so the
// circular AF/constraint dependency remains link-clean.
void GameLib_AddAFFrameConstraint(idPhysics_AF* const physics,
idAFConstraint* const constraint) {
if (physics != nullptr) physics->AddFrameConstraint(constraint);
}
float GameLib_GetAFJointFrictionScale(const idPhysics_AF* const physics) {
return physics != nullptr ? physics->GetJointFrictionScale() : 1.0f;
}
float GameLib_GetAFContactFrictionScale(const idPhysics_AF* const physics) {
return physics != nullptr ? physics->GetContactFrictionScale() : 1.0f;
}
idPhysics_AF::idPhysics_AF()
: idPhysics_DynamicBase()
, trees(4)
, bodies(4)
, constraints(4)
, primaryConstraints(4)
, auxiliaryConstraints(4)
, frameConstraints(4)
, contactConstraints(4)
, contactBodies(4)
, noclipBodies()
, changedAF(true)
, linearFriction(0.005f)
, angularFriction(0.005f)
, contactFriction(0.8f)
, bouncyness(0.5f)
, totalMass(-1.0f)
, suspendVelocity(10.0f, 15.0f)
, suspendAcceleration(20.0f, 20.0f)
, noMoveTime(1.0f)
, noMoveTranslation(10.0f)
, noMoveRotation(10.0f)
, minMoveTime(-1.0f)
, maxMoveTime(-1.0f)
, impulseThreshold(1.0f)
, timeScale(1.0f)
, timeScaleRampStart(0.0f)
, timeScaleRampEnd(0.0f)
, jointFrictionScale(0.0f)
, jointFrictionDent(0.0f)
, jointFrictionDentStart(0.0f)
, jointFrictionDentEnd(0.0f)
, jointFrictionDentScale(0.0f)
, contactFrictionScale(0.0f)
, contactFrictionDent(0.0f)
, contactFrictionDentStart(0.0f)
, contactFrictionDentEnd(0.0f)
, contactFrictionDentScale(0.0f)
, errorReduction(idFader::FADE_LINEAR, 0.5f)
, errorReductionMax(idFader::FADE_LINEAR, 0.5f)
, lcpEpsilon(idFader::FADE_LINEAR, 1.0e-7f)
, limitErrorReduction(idFader::FADE_LINEAR, 0.3f)
, limitErrorReductionMax(idFader::FADE_LINEAR, 0.5f)
, limitLcpEpsilon(idFader::FADE_LINEAR, 1.0e-6f)
, contactErrorReduction(idFader::FADE_LINEAR, 0.7f)
, contactErrorReductionMax(idFader::FADE_LINEAR, 0.9f)
, contactLcpEpsilon(idFader::FADE_LINEAR, 1.0e-6f)
, universalErrorReduction(idFader::FADE_LINEAR, 0.5f)
, universalErrorReductionMax(idFader::FADE_LINEAR, 0.5f)
, universalTorsionLcpEpsilon(idFader::FADE_LINEAR, 1.0e-6f)
, passEntityNum(ENTITYNUM_NONE)
, selfCollision(true)
, comeToRest(true)
, linearTime(true)
, noImpact(false)
, worldConstraintsLocked(false)
, forcePushable(false)
, addContactConstraints(true)
, addGravity(true)
, masterBody(nullptr)
, current{}
, saved{}
, lastTimeStep(1.0f / 60.0f)
, endTime(0)
, timeStep(0.0f)
, lastImpulse(0.0f, 0.0f, 0.0f)
, lcp(nullptr) {
type = PHYSICS_AF;
current.atRest = false;
saved = current;
for (int i = 0; i < 8; ++i) {
noclipBodies.staticList[i].bodyId = -1;
noclipBodies.staticList[i].originalClipMask = 0;
noclipBodies.staticList[i].query.index = 0x100000000ULL;
}
}
idPhysics_AF::~idPhysics_AF() { Shutdown(); }
void idPhysics_AF::Shutdown() {
UnlinkClip();
for (int i = 0; i < contactConstraints.Num(); ++i)
delete contactConstraints[i];
contactConstraints.Clear();
frameConstraints.Clear();
for (int i = 0; i < constraints.Num(); ++i) delete constraints[i];
constraints.Clear();
primaryConstraints.Clear();
auxiliaryConstraints.Clear();
for (int i = 0; i < trees.Num(); ++i) delete trees[i];
trees.Clear();
for (int i = 0; i < bodies.Num(); ++i) delete bodies[i];
bodies.Clear();
contactBodies.Clear();
noclipBodies.Clear();
delete lcp;
lcp = nullptr;
masterBody = nullptr;
}
void idPhysics_AF::SetSuspendTime(const float minimum,
const float maximum) { minMoveTime = minimum; maxMoveTime = maximum; }
void idPhysics_AF::SetSuspendTolerance(const float quietTime,
const float translation, const float rotation) {
noMoveTime = quietTime;
noMoveTranslation = translation;
noMoveRotation = rotation;
}
void idPhysics_AF::SetSuspendSpeed(const idVec2& velocity,
const idVec2& acceleration) {
suspendVelocity = velocity;
suspendAcceleration = acceleration;
}
void idPhysics_AF::SetTimeScaleRamp(const float start, const float end) {
timeScaleRampStart = start;
timeScaleRampEnd = end;
}
void idPhysics_AF::SetJointFrictionDent(const float dent,
const float start, const float end) {
jointFrictionDent = dent;
jointFrictionDentStart = start;
jointFrictionDentEnd = end;
}
void idPhysics_AF::SetContactFrictionDent(const float dent,
const float start, const float end) {
contactFrictionDent = dent;
contactFrictionDentStart = start;
contactFrictionDentEnd = end;
}
void idPhysics_AF::SetDefaultFriction(const float linear,
const float angular, const float contact) {
if (linear < 0.0f || linear > 1.0f || angular < 0.0f
|| angular > 1.0f || contact < 0.0f || contact > 1.0f) return;
linearFriction = linear;
angularFriction = angular;
contactFriction = contact;
}
float idPhysics_AF::GetJointFrictionScale() const {
float scale = jointFrictionScale > 0.0f ? jointFrictionScale : 1.0f;
if (current.activateTime >= jointFrictionDentStart
&& current.activateTime <= jointFrictionDentEnd)
scale *= (std::max)(0.0f, 1.0f - jointFrictionDent);
return scale;
}
float idPhysics_AF::GetContactFrictionScale() const {
float scale = contactFrictionScale > 0.0f ? contactFrictionScale : 1.0f;
if (current.activateTime >= contactFrictionDentStart
&& current.activateTime <= contactFrictionDentEnd)
scale *= (std::max)(0.0f, 1.0f - contactFrictionDent);
return scale;
}
void idPhysics_AF::SetClipModel(idClipModel* const model,
const float density, const int id, const bool freeOld) {
idAFBody* const body = SelectBody(bodies, id);
if (body == nullptr) {
if (model != nullptr && freeOld) model->Delete();
return;
}
if (!freeOld && body->clipModel != nullptr && body->clipModel != model)
body->clipModel->Unlink();
body->SetClipModel(model);
if (density > 0.0f) body->SetDensity(density, kIdentityAxis);
changedAF = true;
Activate();
}
idClipModel* idPhysics_AF::GetClipModel(const int id) {
idAFBody* const body = SelectBody(bodies, id);
return body != nullptr ? body->clipModel : nullptr;
}
int idPhysics_AF::GetNumClipModels() { return bodies.Num(); }
void idPhysics_AF::SetMass(const float newMass, const int id) {
idAFBody* const body = SelectBody(bodies, id);
if (body == nullptr || newMass <= 0.0f) return;
const float scale = newMass / (std::max)(body->mass, 1.0e-6f);
body->mass = newMass;
body->invMass = 1.0f / newMass;
for (int row = 0; row < 3; ++row)
for (int column = 0; column < 3; ++column)
body->inertiaTensor[row][column] *= scale;
body->inverseInertiaTensor = body->inertiaTensor;
body->inverseInertiaTensor.InverseSelf();
totalMass = -1.0f;
}
float idPhysics_AF::GetMass(const int id) {
if (id >= 0) {
const idAFBody* const body = SelectBody(bodies, id);
return body != nullptr ? body->mass : 0.0f;
}
if (totalMass < 0.0f) {
totalMass = 0.0f;
for (int i = 0; i < bodies.Num(); ++i)
if (bodies[i] != nullptr) totalMass += bodies[i]->mass;
}
return totalMass;
}
void idPhysics_AF::SetContents(const int contents, const int id) {
if (id >= 0) {
idClipModel* const model = GetClipModel(id);
if (model != nullptr) model->SetContents(contents);
return;
}
for (int i = 0; i < bodies.Num(); ++i)
if (bodies[i] != nullptr && bodies[i]->clipModel != nullptr)
bodies[i]->clipModel->SetContents(contents);
}
int idPhysics_AF::GetContents(const int id) {
if (id >= 0) {
const idClipModel* const model = GetClipModel(id);
return model != nullptr ? model->GetContents() : 0;
}
int contents = 0;
for (int i = 0; i < bodies.Num(); ++i)
if (bodies[i] != nullptr && bodies[i]->clipModel != nullptr)
contents |= bodies[i]->clipModel->GetContents();
return contents;
}
void idPhysics_AF::SetClipMask(const int mask, const int id) {
if (id >= 0) {
idAFBody* const body = SelectBody(bodies, id);
if (body != nullptr) { body->clipMask = mask; body->fl.clipMaskSet = 1; }
return;
}
clipMask = mask;
for (int i = 0; i < bodies.Num(); ++i)
if (bodies[i] != nullptr && !bodies[i]->fl.clipMaskSet)
bodies[i]->clipMask = mask;
}
int idPhysics_AF::GetClipMask(const int id) {
const idAFBody* const body = SelectBody(bodies, id);
return body != nullptr ? body->clipMask : clipMask;
}
const idBounds* idPhysics_AF::GetBounds(const int id) {
if (id >= 0) {
const idClipModel* const model = GetClipModel(id);
return model != nullptr ? &model->GetBounds() : &kZeroBounds;
}
static thread_local idBounds bounds;
bool initialized = false;
for (int i = 0; i < bodies.Num(); ++i) {
const idClipModel* const model = bodies[i] != nullptr
? bodies[i]->clipModel : nullptr;
if (model == nullptr) continue;
if (!initialized) { bounds = model->GetBounds(); initialized = true; }
else ExpandBounds(bounds, model->GetBounds());
}
if (!initialized) bounds = kZeroBounds;
return &bounds;
}
const idBounds* idPhysics_AF::GetAbsBounds(const int id) {
if (id >= 0) {
const idClipModel* const model = GetClipModel(id);
return model != nullptr ? &model->GetAbsBounds() : &kZeroBounds;
}
static thread_local idBounds bounds;
bool initialized = false;
for (int i = 0; i < bodies.Num(); ++i) {
const idClipModel* const model = bodies[i] != nullptr
? bodies[i]->clipModel : nullptr;
if (model == nullptr) continue;
if (!initialized) { bounds = model->GetAbsBounds(); initialized = true; }
else ExpandBounds(bounds, model->GetAbsBounds());
}
if (!initialized) bounds = kZeroBounds;
return &bounds;
}
void idPhysics_AF::SetOrigin(const idVec3* const origin, const int id) {
if (origin == nullptr) return;
idAFBody* const reference = SelectBody(bodies, id);
if (reference == nullptr) return;
const idVec3 delta = *origin - reference->current.worldOrigin;
Translate(&delta, id);
}
void idPhysics_AF::SetAxis(const idMat3* const axis, const int id) {
if (axis == nullptr) return;
idAFBody* const reference = SelectBody(bodies, id);
if (reference == nullptr) return;
const idMat3 rotationMatrix = *axis * reference->current.worldAxis.Transpose();
const idVec3 pivot = reference->current.worldOrigin;
for (int i = 0; i < bodies.Num(); ++i) {
idAFBody* const body = bodies[i];
if (body == nullptr || (id >= 0 && i != id)) continue;
body->current.worldOrigin = pivot
+ rotationMatrix * (body->current.worldOrigin - pivot);
body->current.worldAxis = rotationMatrix * body->current.worldAxis;
}
UpdateClipModels();
Activate();
}
void idPhysics_AF::Translate(const idVec3* const translation, const int id) {
if (translation == nullptr) return;
for (int i = 0; i < bodies.Num(); ++i) {
idAFBody* const body = bodies[i];
if (body == nullptr || (id >= 0 && i != id)) continue;
body->current.worldOrigin = body->current.worldOrigin + *translation;
}
if (id < 0)
for (int i = 0; i < constraints.Num(); ++i)
if (constraints[i] != nullptr) constraints[i]->Translate(*translation);
UpdateClipModels();
Activate();
}
void idPhysics_AF::Rotate(const idRotation* const rotation, const int id) {
if (rotation == nullptr) return;
const idMat3 matrix = rotation->ToMat3();
for (int i = 0; i < bodies.Num(); ++i) {
idAFBody* const body = bodies[i];
if (body == nullptr || (id >= 0 && i != id)) continue;
body->current.worldOrigin = *rotation * body->current.worldOrigin;
body->current.worldAxis = matrix * body->current.worldAxis;
}
if (id < 0)
for (int i = 0; i < constraints.Num(); ++i)
if (constraints[i] != nullptr) constraints[i]->Rotate(*rotation);
UpdateClipModels();
Activate();
}
const idVec3* idPhysics_AF::GetOrigin(const int id) {
const idAFBody* const body = SelectBody(bodies, id);
return body != nullptr ? &body->current.worldOrigin : &kZeroVector;
}
const idMat3* idPhysics_AF::GetAxis(const int id) {
const idAFBody* const body = SelectBody(bodies, id);
return body != nullptr ? &body->current.worldAxis : &kIdentityAxis;
}
const idVec3* idPhysics_AF::GetLocalOrigin(const int id) {
return GetOrigin(id);
}
const idMat3* idPhysics_AF::GetLocalAxis(const int id) { return GetAxis(id); }
void idPhysics_AF::SetLinearVelocity(const idVec3* const velocity,
const int id) {
if (velocity == nullptr) return;
if (id >= 0) {
idAFBody* const body = SelectBody(bodies, id);
if (body != nullptr) ::SetLinearVelocity(*body, *velocity);
} else {
for (int i = 0; i < bodies.Num(); ++i)
if (bodies[i] != nullptr) ::SetLinearVelocity(*bodies[i], *velocity);
}
Activate();
}
void idPhysics_AF::SetAngularVelocity(const idVec3* const velocity,
const int id) {
if (velocity == nullptr) return;
if (id >= 0) {
idAFBody* const body = SelectBody(bodies, id);
if (body != nullptr) ::SetAngularVelocity(*body, *velocity);
} else {
for (int i = 0; i < bodies.Num(); ++i)
if (bodies[i] != nullptr) ::SetAngularVelocity(*bodies[i], *velocity);
}
Activate();
}
idVec3* idPhysics_AF::GetLinearVelocity(idVec3* const result,
const int id) {
if (result == nullptr) return nullptr;
const idAFBody* const body = SelectBody(bodies, id);
*result = body != nullptr ? LinearVelocity(*body) : kZeroVector;
return result;
}
idVec3* idPhysics_AF::GetAngularVelocity(idVec3* const result,
const int id) {
if (result == nullptr) return nullptr;
const idAFBody* const body = SelectBody(bodies, id);
*result = body != nullptr ? AngularVelocity(*body) : kZeroVector;
return result;
}
void idPhysics_AF::SetWaterEntNum(int) {}
int idPhysics_AF::GetWaterEntNum() { return -1; }
void idPhysics_AF::SetWaterSurfaceWrldHeight(float) {}
float idPhysics_AF::GetWaterSurfaceWrldHeight() { return 0.0f; }
void idPhysics_AF::GetImpactInfo(const int id, const idVec3* const point,
impactInfo_t* const info) {
if (info == nullptr) return;
info->Zero();
const idAFBody* const body = SelectBody(bodies, id);
if (body == nullptr) return;
info->invMass = body->invMass;
info->invInertiaTensor = body->inverseInertiaTensor;
info->position = point != nullptr ? *point : body->current.worldOrigin;
info->velocity = body->GetPointVelocity(info->position);
}
void idPhysics_AF::ApplyImpulse(const int bodyId, const idVec3* const point,
const idVec3* const impulse) {
idAFBody* const body = SelectBody(bodies, bodyId);
if (body == nullptr || impulse == nullptr || noImpact) return;
const idVec3 applicationPoint = point != nullptr
? *point : body->current.worldOrigin;
::SetLinearVelocity(*body, LinearVelocity(*body)
+ *impulse * body->invMass);
const idVec3 torque = (applicationPoint - body->current.worldOrigin)
.Cross(*impulse);
::SetAngularVelocity(*body, AngularVelocity(*body)
+ body->inverseInertiaTensor * torque);
lastImpulse = *impulse;
Activate();
}
void idPhysics_AF::ApplyForce(const int bodyId, const idVec3* const point,
const idVec3* const force) {
idAFBody* const body = SelectBody(bodies, bodyId);
if (body == nullptr || force == nullptr) return;
body->AddForce(point != nullptr ? *point : body->current.worldOrigin,
*force);
Activate();
}
void idPhysics_AF::Activate() {
current.atRest = false;
current.noMoveTime = 0.0f;
current.activateTime = 0.0f;
if (callbacks != nullptr)
GameLib_NotifyPhysicsActivated(callbacks, GetPhysicsId());
}
void idPhysics_AF::PutToRest() {
current.atRest = true;
for (int i = 0; i < bodies.Num(); ++i) {
if (bodies[i] == nullptr) continue;
ZeroSpatial(bodies[i]->current.spatialVelocity);
ZeroSpatial(bodies[i]->current.externalForce);
}
}
bool idPhysics_AF::IsAtRest() { return current.atRest; }
bool idPhysics_AF::IsPushable(int) {
return !noImpact && (masterBody == nullptr || forcePushable);
}
void idPhysics_AF::SaveState() {
saved = current;
for (int i = 0; i < bodies.Num(); ++i)
if (bodies[i] != nullptr) bodies[i]->saved = bodies[i]->current;
}
void idPhysics_AF::RestoreState() {
current = saved;
for (int i = 0; i < bodies.Num(); ++i)
if (bodies[i] != nullptr) bodies[i]->current = bodies[i]->saved;
UpdateClipModels();
}
void idPhysics_AF::UpdateTime(const int newEndTime) { endTime = newEndTime; }
void idPhysics_AF::SetPushed(const int deltaTime) {
ZeroSpatial(current.pushVelocity);
if (deltaTime <= 0 || bodies.Num() == 0) return;
const float scale = 1000.0f / static_cast<float>(deltaTime);
int count = 0;
for (int i = 0; i < bodies.Num(); ++i) {
const idAFBody* const body = bodies[i];
if (body == nullptr) continue;
const idVec3 velocity = (body->current.worldOrigin
- body->saved.worldOrigin) * scale;
current.pushVelocity[0] += velocity.x;
current.pushVelocity[1] += velocity.y;
current.pushVelocity[2] += velocity.z;
++count;
}
if (count > 0)
for (int i = 0; i < 3; ++i) current.pushVelocity[i] /= count;
}
idVec3* idPhysics_AF::GetPushedLinearVelocity(idVec3* const result, int) {
if (result != nullptr) result->Set(current.pushVelocity[0],
current.pushVelocity[1], current.pushVelocity[2]);
return result;
}
idVec3* idPhysics_AF::GetPushedAngularVelocity(idVec3* const result, int) {
if (result != nullptr) result->Set(current.pushVelocity[3],
current.pushVelocity[4], current.pushVelocity[5]);
return result;
}
void idPhysics_AF::SetAuxAngularVelocity(const idVec3& angular) {
current.auxVelocity[3] = angular.x;
current.auxVelocity[4] = angular.y;
current.auxVelocity[5] = angular.z;
}
void idPhysics_AF::SetMaster(const bool enable,
const idVec3* const masterOrigin, const idMat3* const masterAxis,
bindFlags_t) {
if (enable && masterOrigin != nullptr && masterAxis != nullptr
&& bodies.Num() > 0) {
masterBody = bodies[0];
const idVec3 oldOrigin = masterBody->current.worldOrigin;
const idMat3 oldAxis = masterBody->current.worldAxis;
const idMat3 rotation = *masterAxis * oldAxis.Transpose();
for (int i = 0; i < bodies.Num(); ++i) {
idAFBody* const body = bodies[i];
if (body == nullptr) continue;
body->current.worldOrigin = *masterOrigin
+ rotation * (body->current.worldOrigin - oldOrigin);
body->current.worldAxis = rotation * body->current.worldAxis;
}
worldConstraintsLocked = true;
UpdateClipModels();
} else {
masterBody = nullptr;
worldConstraintsLocked = false;
Activate();
}
}
void idPhysics_AF::SetLocalOrigin(const idVec3* const origin,
const int id) { SetOrigin(origin, id); }
void idPhysics_AF::SetLocalAxis(const idMat3* const axis,
const int id) { SetAxis(axis, id); }
int idPhysics_AF::GetBlockingEntityNum() {
return contacts.Num() > 0 ? contacts[0].entityNum : ENTITYNUM_NONE;
}
int idPhysics_AF::GetLinearEndTime() { return 0; }
int idPhysics_AF::GetAngularEndTime() { return 0; }
void idPhysics_AF::Serialize(idSerializer* const serializer) {
if (serializer != nullptr) GameLib_SerializeAFPhysics(serializer, *this);
}
void idPhysics_AF::DisableClip() {
for (int i = 0; i < bodies.Num(); ++i)
if (bodies[i] != nullptr && bodies[i]->clipModel != nullptr)
bodies[i]->clipModel->Disable();
}
void idPhysics_AF::EnableClip() {
for (int i = 0; i < bodies.Num(); ++i)
if (bodies[i] != nullptr && bodies[i]->clipModel != nullptr)
bodies[i]->clipModel->Enable();
}
void idPhysics_AF::UnlinkClip() {
for (int i = 0; i < bodies.Num(); ++i)
if (bodies[i] != nullptr && bodies[i]->clipModel != nullptr)
bodies[i]->clipModel->Unlink();
}
void idPhysics_AF::LinkClip() { UpdateClipModels(); }
void idPhysics_AF::UpdateClipModels() {
for (int i = 0; i < bodies.Num(); ++i) {
idAFBody* const body = bodies[i];
if (body == nullptr || body->clipModel == nullptr) continue;
body->clipModel->Link(GetEntityNumber(), GetEntityNumber(), i,
body->current.worldOrigin, body->current.worldAxis);
}
}
void idPhysics_AF::ClipTranslation(trace_t* const result,
const idVec3* const translation, const idClipModel* const model) {
if (result == nullptr) return;
std::memset(result, 0, sizeof(*result));
result->fraction = 1.0f;
if (translation == nullptr || clip == nullptr) return;
for (int i = 0; i < bodies.Num(); ++i) {
const idAFBody* const body = bodies[i];
if (body == nullptr || body->clipModel == nullptr) continue;
trace_t local{};
local.fraction = 1.0f;
if (model != nullptr) {
clip->TranslationModel(local, body->current.worldOrigin,
body->current.worldOrigin + *translation, body->clipModel,
body->current.worldAxis, body->clipMask,
model->GetOrigin(), model, model->GetAxis());
} else {
clip->Translation(&local, body->current.worldOrigin,
body->current.worldOrigin + *translation, body->clipModel,
body->current.worldAxis, body->clipMask, passEntityNum,
false, "idPhysics_AF::ClipTranslation");
}
if (local.fraction < result->fraction) *result = local;
}
}
void idPhysics_AF::ClipRotation(trace_t* const result,
const idRotation* const rotation, const idClipModel* const model) {
if (result == nullptr) return;
std::memset(result, 0, sizeof(*result));
result->fraction = 1.0f;
if (rotation == nullptr || clip == nullptr) return;
for (int i = 0; i < bodies.Num(); ++i) {
const idAFBody* const body = bodies[i];
if (body == nullptr || body->clipModel == nullptr) continue;
trace_t local{};
local.fraction = 1.0f;
if (model != nullptr) {
clip->RotationModel(local, body->current.worldOrigin,
*rotation, body->clipModel, body->current.worldAxis,
body->clipMask, model->GetOrigin(), model, model->GetAxis());
} else {
clip->Rotation(&local, body->current.worldOrigin, *rotation,
body->clipModel, body->current.worldAxis, body->clipMask,
passEntityNum, false, "idPhysics_AF::ClipRotation");
}
if (local.fraction < result->fraction) *result = local;
}
}
int idPhysics_AF::ClipContents(const idClipModel* const model,
const int contentMask) {
if (clip == nullptr) return 0;
int contents = 0;
for (int i = 0; i < bodies.Num(); ++i) {
const idAFBody* const body = bodies[i];
if (body == nullptr || body->clipModel == nullptr) continue;
trace_t local{};
if (model != nullptr) {
clip->ContentsModel(local, body->current.worldOrigin,
body->clipModel, body->current.worldAxis, contentMask,
model->GetOrigin(), model, model->GetAxis());
} else {
clip->Contents(&local, body->current.worldOrigin,
body->clipModel, body->current.worldAxis, contentMask,
passEntityNum, "idPhysics_AF::ClipContents");
}
contents |= local.c.contentFlags;
}
return contents;
}
int idPhysics_AF::AddBody(idAFBody* const body) {
if (body == nullptr) return -1;
for (int i = 0; i < bodies.Num(); ++i)
if (bodies[i] == body) return i;
if (!body->fl.clipMaskSet) body->clipMask = clipMask;
if (body->linearFriction < 0.0f) body->linearFriction = linearFriction;
if (body->angularFriction < 0.0f) body->angularFriction = angularFriction;
if (body->contactFriction < 0.0f) body->contactFriction = contactFriction;
if (body->bouncyness < 0.0f) body->bouncyness = bouncyness;
bodies.Append(body);
totalMass = -1.0f;
changedAF = true;
return bodies.Num() - 1;
}
void idPhysics_AF::ForceBodyId(idAFBody* const body, const int newId) {
const int oldId = GetBodyId(body);
if (oldId < 0 || newId < 0 || newId >= bodies.Num()
|| oldId == newId) return;
idAFBody* const displaced = bodies[newId];
bodies[newId] = body;
bodies[oldId] = displaced;
changedAF = true;
}
int idPhysics_AF::GetBodyId(const idAFBody* const body) const {
for (int i = 0; i < bodies.Num(); ++i) if (bodies[i] == body) return i;
return -1;
}
idAFBody* idPhysics_AF::GetBody(const char* const bodyName) const {
if (bodyName == nullptr) return nullptr;
for (int i = 0; i < bodies.Num(); ++i)
if (bodies[i] != nullptr
&& std::strcmp(bodies[i]->name.c_str(), bodyName) == 0)
return bodies[i];
return nullptr;
}
idAFBody* idPhysics_AF::GetBody(const int id) const {
return const_cast<idAFBody*>(SelectBody(bodies, id));
}
idAFConstraint* idPhysics_AF::GetConstraint(
const char* const constraintName) const {
if (constraintName == nullptr) return nullptr;
for (int i = 0; i < constraints.Num(); ++i)
if (constraints[i] != nullptr
&& std::strcmp(constraints[i]->name.c_str(),
constraintName) == 0)
return constraints[i];
return nullptr;
}
idAFConstraint* idPhysics_AF::GetConstraint(const int id) const {
return id >= 0 && id < constraints.Num() ? constraints[id] : nullptr;
}
void idPhysics_AF::AddConstraint(idAFConstraint* const constraint) {
if (constraint == nullptr) return;
for (int i = 0; i < constraints.Num(); ++i)
if (constraints[i] == constraint) return;
constraint->physics = this;
constraints.Append(constraint);
changedAF = true;
}
void idPhysics_AF::DeleteConstraint(const int id) {
if (id < 0 || id >= constraints.Num()) return;
idAFConstraint* const constraint = constraints[id];
constraints.RemoveIndex(id);
primaryConstraints.Remove(constraint);
auxiliaryConstraints.Remove(constraint);
frameConstraints.Remove(constraint);
delete constraint;
changedAF = true;
}
void idPhysics_AF::DeleteConstraint(const char* const constraintName) {
idAFConstraint* const constraint = GetConstraint(constraintName);
if (constraint == nullptr) return;
DeleteConstraint(constraints.FindIndex(constraint));
}
void idPhysics_AF::DeleteBody(const int id) {
if (id < 0 || id >= bodies.Num()) return;
idAFBody* const body = bodies[id];
for (int i = constraints.Num() - 1; i >= 0; --i)
if (constraints[i] != nullptr
&& (constraints[i]->body1 == body
|| constraints[i]->body2 == body))
DeleteConstraint(i);
bodies.RemoveIndex(id);
delete body;
totalMass = -1.0f;
changedAF = true;
}
void idPhysics_AF::AddFrameConstraint(idAFConstraint* const constraint) {
if (constraint == nullptr) return;
for (int i = 0; i < frameConstraints.Num(); ++i)
if (frameConstraints[i] == constraint) return;
constraint->physics = this;
constraint->fl.frameConstraint = 1;
frameConstraints.Append(constraint);
}
int idPhysics_AF::AddNoclipBody(const int bodyId) {
idAFBody* const body = GetBody(bodyId);
if (body == nullptr || noclipBodies.Num() >= noclipBodies.Max()) return -1;
for (int i = 0; i < noclipBodies.Num(); ++i)
if (noclipBodies[i].bodyId == bodyId) return i;
noclipBodyInfo_t info{};
info.bodyId = bodyId;
info.originalClipMask = body->clipMask;
info.query.index = 0;
noclipBodies.Append(info);
body->clipMask = 0;
return noclipBodies.Num() - 1;
}
void idPhysics_AF::TestNoclipBodies() {
if (clip == nullptr) return;
for (int i = noclipBodies.Num() - 1; i >= 0; --i) {
noclipBodyInfo_t& info = noclipBodies[i];
idAFBody* const body = GetBody(info.bodyId);
if (body == nullptr || body->clipModel == nullptr) {
noclipBodies.RemoveIndex(i);
continue;
}
trace_t result{};
info.query = clip->Contents(&result, body->current.worldOrigin,
body->clipModel, body->current.worldAxis,
info.originalClipMask, passEntityNum,
"idPhysics_AF::TestNoclipBodies");
if (result.c.contentFlags == 0) {
body->clipMask = info.originalClipMask;
noclipBodies.RemoveIndex(i);
}
}
}
void idPhysics_AF::ApplyFriction(const float step, float) {
const float jointScale = GetJointFrictionScale();
for (int i = 0; i < bodies.Num(); ++i) {
idAFBody* const body = bodies[i];
if (body == nullptr) continue;
const float linear = body->linearFriction >= 0.0f
? body->linearFriction : linearFriction;
const float angular = body->angularFriction >= 0.0f
? body->angularFriction : angularFriction;
::SetLinearVelocity(*body, LinearVelocity(*body)
* (std::max)(0.0f, 1.0f - linear * step));
::SetAngularVelocity(*body, AngularVelocity(*body)
* (std::max)(0.0f, 1.0f - angular * jointScale * step));
}
}
void idPhysics_AF::AddGravity() {
if (!addGravity) return;
for (int i = 0; i < bodies.Num(); ++i) {
idAFBody* const body = bodies[i];
if (body == nullptr) continue;
body->current.externalForce[0] += gravityVector.x * body->mass;
body->current.externalForce[1] += gravityVector.y * body->mass;
body->current.externalForce[2] += gravityVector.z * body->mass;
}
}
void idPhysics_AF::PrimaryFactor() {
for (int i = 0; i < trees.Num(); ++i)
if (trees[i] != nullptr) trees[i]->Factor();
}
void idPhysics_AF::PrimaryForces(const float step) {
for (int i = 0; i < trees.Num(); ++i)
if (trees[i] != nullptr) trees[i]->CalculateForces(step);
}
void idPhysics_AF::AuxiliaryForces(float) {
SolveConstraintRows(auxiliaryConstraints, 8);
SolveConstraintRows(frameConstraints, 8);
}
void idPhysics_AF::AddFrameConstraints() {
frameConstraints.Clear();
const float inverseStep = timeStep > 0.0f ? 1.0f / timeStep : 0.0f;
for (int i = 0; i < constraints.Num(); ++i)
if (constraints[i] != nullptr)
constraints[i]->ApplyFriction(inverseStep);
for (int i = 0; i < contactConstraints.Num(); ++i)
if (contactConstraints[i] != nullptr)
contactConstraints[i]->ApplyFriction(inverseStep);
}
void idPhysics_AF::RemoveFrameConstraints() { frameConstraints.Clear(); }
void idPhysics_AF::EvaluateConstraints(const float step) {
const float inverseStep = step > 0.0f ? 1.0f / step : 0.0f;
for (int i = 0; i < primaryConstraints.Num(); ++i)
if (primaryConstraints[i] != nullptr)
primaryConstraints[i]->Evaluate(this, inverseStep);
for (int i = 0; i < auxiliaryConstraints.Num(); ++i)
if (auxiliaryConstraints[i] != nullptr)
auxiliaryConstraints[i]->Evaluate(this, inverseStep);
for (int i = 0; i < contactConstraints.Num(); ++i)
if (contactConstraints[i] != nullptr)
contactConstraints[i]->Evaluate(this, inverseStep);
AddFrameConstraints();
for (int i = 0; i < frameConstraints.Num(); ++i)
if (frameConstraints[i] != nullptr
&& frameConstraints[i]->J1.GetNumRows() == 0)
frameConstraints[i]->Evaluate(this, inverseStep);
SolveConstraintRows(primaryConstraints, 8);
SolveConstraintRows(auxiliaryConstraints, 8);
// Contact rows are kept in their own authoritative list.
idList<idAFConstraint*, 71> contactRows(4);
for (int i = 0; i < contactConstraints.Num(); ++i)
contactRows.Append(contactConstraints[i]);
SolveConstraintRows(contactRows, 8);
SolveConstraintRows(frameConstraints, 8);
}
void idPhysics_AF::AddContacts(idAFBody* const body,
const contactsResult_t& result) {
if (body == nullptr) return;
const int count = (std::min)(12, result.numContacts);
for (int i = 0; i < count; ++i) {
const contactInfo_t& info = result.contacts[i];
contacts.Append(info);
UpdateCollisionResidency(info);
bool bodyListed = false;
const int bodyId = GetBodyId(body);
for (int j = 0; j < contactBodies.Num(); ++j)
bodyListed |= contactBodies[j] == bodyId;
if (!bodyListed) contactBodies.Append(bodyId);
if (!addContactConstraints) continue;
idAFBody* otherBody = nullptr;
idPhysics* const otherPhysics = idPhysics::GetPhysicsForId(
info.physicsId);
if (otherPhysics == this)
otherBody = GetBody(info.bodyId);
idAFConstraint_Contact* const contact =
new idAFConstraint_Contact();
contact->physics = this;
contact->Setup(body, otherBody, info, info.separation,
timeStep > 0.0f ? 1.0f / timeStep : 0.0f);
contactConstraints.Append(contact);
}
}
void idPhysics_AF::SetupContactConstraints(const float step) {
const float inverseStep = step > 0.0f ? 1.0f / step : 0.0f;
for (int i = 0; i < contactConstraints.Num(); ++i) {
idAFConstraint_Contact* const constraint = contactConstraints[i];
if (constraint != nullptr)
constraint->Setup(constraint->body1, constraint->body2,
constraint->contact, constraint->separation, inverseStep);
}
}
void idPhysics_AF::CollisionImpulse(idAFBody* const body,
const trace_t& collision, float) {
if (body == nullptr || collision.fraction >= 1.0f) return;
idVec3 velocity = body->GetPointVelocity(collision.c.point);
const float into = velocity.Dot(collision.c.normal);
if (into >= 0.0f) return;
const float bounce = body->bouncyness >= 0.0f
? body->bouncyness : bouncyness;
const idVec3 impulse = collision.c.normal
* (-(1.0f + bounce) * into / (std::max)(body->invMass, 1.0e-6f));
ApplyImpulse(GetBodyId(body), &collision.c.point, &impulse);
if (callbacks != nullptr)
GameLib_NotifyPhysicsCollision(callbacks, GetPhysicsId(),
collision, velocity);
}
void idPhysics_AF::IssueCollisionQueries() {
for (int i = 0; i < constraints.Num(); ++i)
if (constraints[i] != nullptr)
constraints[i]->IssueCollisionQueries();
}
void idPhysics_AF::EvaluateBodies(const float step) {
for (int i = 0; i < bodies.Num(); ++i) {
idAFBody* const body = bodies[i];
if (body == nullptr) continue;
idVec3 linear = LinearVelocity(*body);
idVec3 angular = AngularVelocity(*body);
linear = linear + idVec3(body->current.externalForce[0],
body->current.externalForce[1],
body->current.externalForce[2]) * (body->invMass * step);
angular = angular + body->inverseInertiaTensor
* idVec3(body->current.externalForce[3],
body->current.externalForce[4],
body->current.externalForce[5]) * step;
::SetLinearVelocity(*body, linear);
::SetAngularVelocity(*body, angular);
const idVec3 start = body->current.worldOrigin;
const idVec3 end = start + linear * step;
idVec3 angularAxis = angular;
const float angularSpeed = angularAxis.NormalizeFast();
idRotation rotation(start, angularSpeed > 0.0f ? angularAxis
: idVec3(0.0f, 0.0f, 1.0f), angularSpeed * step * RAD2DEG);
trace_t collision{};
collision.fraction = 1.0f;
collision.endpos = end;
collision.endAxis = rotation.ToMat3() * body->current.worldAxis;
contactsResult_t contactResult{};
if (clip != nullptr && body->clipModel != nullptr
&& body->clipMask != 0) {
body->motionQuery = clip->MotionContacts(&collision,
&contactResult, start, end, rotation, 0.25f,
body->clipModel, body->current.worldAxis, body->clipMask,
passEntityNum, false, "idPhysics_AF::EvaluateBodies");
}
body->current.worldOrigin = collision.endpos;
body->current.worldAxis = collision.endAxis;
if (contactResult.numContacts > 0) AddContacts(body, contactResult);
if (collision.fraction < 1.0f)
CollisionImpulse(body, collision, step);
ZeroSpatial(body->current.externalForce);
}
UpdateClipModels();
}
void idPhysics_AF::ResolveCollisions(float) {
AddContactPhysicsForContacts();
}
bool idPhysics_AF::EvaluateContacts() {
ClearContacts();
for (int i = 0; i < contactConstraints.Num(); ++i)
delete contactConstraints[i];
contactConstraints.Clear();
contactBodies.Clear();
if (clip == nullptr) return false;
for (int i = 0; i < bodies.Num(); ++i) {
idAFBody* const body = bodies[i];
if (body == nullptr || body->clipModel == nullptr) continue;
contactsResult_t result{};
body->motionQuery = clip->Contacts(&result,
body->current.worldOrigin, gravityNormal, 0.25f,
body->clipModel, body->current.worldAxis, body->clipMask,
passEntityNum, "idPhysics_AF::EvaluateContacts");
AddContacts(body, result);
}
AddContactPhysicsForContacts();
return contacts.Num() > 0;
}
bool idPhysics_AF::TestIfAtRest(const float step) {
float maximumLinear = 0.0f;
float maximumAngular = 0.0f;
float maximumAcceleration = 0.0f;
for (int i = 0; i < bodies.Num(); ++i) {
const idAFBody* const body = bodies[i];
if (body == nullptr) continue;
maximumLinear = (std::max)(maximumLinear, LinearVelocity(*body).Length());
maximumAngular = (std::max)(maximumAngular, AngularVelocity(*body).Length());
maximumAcceleration = (std::max)(maximumAcceleration,
idVec3(body->acceleration[0], body->acceleration[1],
body->acceleration[2]).Length());
}
const bool quiet = maximumLinear <= suspendVelocity.x
&& maximumAngular <= suspendVelocity.y
&& maximumAcceleration <= (std::max)(suspendAcceleration.x,
suspendAcceleration.y);
current.activateTime += step;
if (quiet) current.noMoveTime += step;
else current.noMoveTime = 0.0f;
const bool minimumElapsed = minMoveTime < 0.0f
|| current.activateTime >= minMoveTime;
const bool maximumElapsed = maxMoveTime >= 0.0f
&& current.activateTime >= maxMoveTime;
if (comeToRest && ((minimumElapsed && current.noMoveTime >= noMoveTime)
|| maximumElapsed)) {
PutToRest();
return true;
}
return false;
}
void idPhysics_AF::Evolve(const float step) {
timeStep = step;
lastTimeStep = step;
ClearContacts();
for (int i = 0; i < contactConstraints.Num(); ++i)
delete contactConstraints[i];
contactConstraints.Clear();
contactBodies.Clear();
RemoveFrameConstraints();
TestNoclipBodies();
ApplyFriction(step, static_cast<float>(endTime));
AddGravity();
IssueCollisionQueries();
EvaluateConstraints(step);
EvaluateBodies(step);
ResolveCollisions(step);
EvaluateContacts();
SetupContactConstraints(step);
TestIfAtRest(step);
}
void idPhysics_AF::BuildTrees() {
for (int i = 0; i < trees.Num(); ++i) delete trees[i];
trees.Clear();
primaryConstraints.Clear();
auxiliaryConstraints.Clear();
for (int i = 0; i < bodies.Num(); ++i) {
if (bodies[i] == nullptr) continue;
bodies[i]->parent = nullptr;
bodies[i]->children.Clear();
bodies[i]->primaryConstraint = nullptr;
bodies[i]->tree = nullptr;
}
for (int i = 0; i < constraints.Num(); ++i) {
idAFConstraint* const constraint = constraints[i];
if (constraint == nullptr || !constraint->fl.allowPrimary
|| constraint->body1 == nullptr
|| constraint->body2 == nullptr
|| constraint->body1->parent != nullptr
|| IsDescendantOf(constraint->body2, constraint->body1)) {
if (constraint != nullptr) auxiliaryConstraints.Append(constraint);
continue;
}
constraint->fl.isPrimary = 1;
constraint->body1->parent = constraint->body2;
constraint->body1->primaryConstraint = constraint;
constraint->body2->children.Append(constraint->body1);
primaryConstraints.Append(constraint);
}
for (int i = 0; i < bodies.Num(); ++i) {
idAFBody* const body = bodies[i];
if (body == nullptr || body->parent != nullptr) continue;
idAFTree* const tree = new idAFTree();
tree->sortedBodies.Append(body);
tree->SortBodies();
tree->SetMaxSubTreeAuxiliaryIndex();
trees.Append(tree);
}
changedAF = false;
}
void idPhysics_AF::DebugDraw() {
for (int i = 0; i < constraints.Num(); ++i)
if (constraints[i] != nullptr) constraints[i]->DebugDraw();
const idVec4 color(0.2f, 0.8f, 1.0f, 1.0f);
for (int i = 0; i < trees.Num(); ++i)
if (trees[i] != nullptr) trees[i]->DebugDraw(color);
}
float idPhysics_AF::GetLcpEpsilon(float) const {
return lcpEpsilon.GetValue(endTime);
}
float idPhysics_AF::GetErrorReduction(float) const {
return errorReduction.GetValue(endTime);
}
float idPhysics_AF::GetErrorReductionMax(float) const {
return errorReductionMax.GetValue(endTime);
}
float idPhysics_AF::GetLimitErrorReduction(float) const {
return limitErrorReduction.GetValue(endTime);
}
float idPhysics_AF::GetLimitErrorReductionMax(float) const {
return limitErrorReductionMax.GetValue(endTime);
}
float idPhysics_AF::GetLimitLcpEpsilon(float) const {
return limitLcpEpsilon.GetValue(endTime);
}
float idPhysics_AF::GetContactErrorReduction(float) const {
return contactErrorReduction.GetValue(endTime);
}
float idPhysics_AF::GetContactErrorReductionMax(float) const {
return contactErrorReductionMax.GetValue(endTime);
}
float idPhysics_AF::GetContactLcpEpsilon(float) const {
return contactLcpEpsilon.GetValue(endTime);
}
float idPhysics_AF::GetUniversalErrorReduction(float) const {
return universalErrorReduction.GetValue(endTime);
}
float idPhysics_AF::GetUniversalTorsionLcpEpsilon(float) const {
return universalTorsionLcpEpsilon.GetValue(endTime);
}
bool idPhysics_AF::Evaluate(const int timeStepMSec,
const int newEndTime) {
endTime = newEndTime;
if (changedAF) BuildTrees();
if (masterBody != nullptr && callbacks != nullptr) {
idVec3 masterOrigin;
idMat3 masterAxis;
if (GameLib_GetMasterPhysicsTransform(callbacks, masterOrigin,
masterAxis)) {
SetMaster(true, &masterOrigin, &masterAxis,
static_cast<bindFlags_t>(0));
}
}
if (current.atRest) return false;
float step = timeStepMSec * 0.001f * timeScale;
if (timeScaleRampEnd > timeScaleRampStart
&& current.activateTime < timeScaleRampEnd) {
const float fraction = (std::max)(0.0f, (std::min)(1.0f,
(current.activateTime - timeScaleRampStart)
/ (timeScaleRampEnd - timeScaleRampStart)));
step *= fraction;
}
if (step <= 0.0f) return false;
Evolve(step);
if (IsOutsideWorld()) {
PutToRest();
if (callbacks != nullptr)
GameLib_NotifyPhysicsDeactivated(callbacks, GetPhysicsId());
}
return true;
}