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

205 lines
5.8 KiB
C++

#include "gamelib/physics/afbody.h"
#include "gamelib/physics/clipmodel.h"
#include "idlib/lib_print.h"
#include <algorithm>
#include <cstring>
void GameLib_SerializeAFBody(idSerializer* serializer, idAFBody& body);
namespace {
void ZeroState(AFBodyPState_t& state) {
state.worldOrigin.Zero();
state.worldAxis = idMat3(1.0f);
state.atRestOrigin.Zero();
state.atRestAxis = idMat3(1.0f);
std::memset(state.spatialVelocity.p, 0, sizeof(state.spatialVelocity.p));
std::memset(state.externalForce.p, 0, sizeof(state.externalForce.p));
}
} // namespace
idAFBody::idAFBody()
: name()
, clipModel(nullptr)
, children(4)
, constraints(4)
, motionQuery{0}
, inverseWorldSpatialInertia()
, I()
, invI()
, J()
, s()
, totalForce()
, auxForce()
, acceleration() {
Init();
}
idAFBody::idAFBody(const char* const bodyName,
idClipModel* const model, const float density)
: idAFBody() {
name.Set(bodyName != nullptr ? bodyName : "noname");
SetClipModel(model);
SetDensity(density, idMat3(1.0f));
}
idAFBody::~idAFBody() {
if (clipModel != nullptr) {
clipModel->Delete();
clipModel = nullptr;
}
children.Clear();
constraints.Clear();
response = nullptr;
responseIndex = nullptr;
}
void idAFBody::Init() {
name.Set("noname");
clipModel = nullptr;
clipMask = 0;
linearFrictionWater = -1.0f;
angularFrictionWater = -1.0f;
linearFriction = -1.0f;
angularFriction = -1.0f;
contactFriction = -1.0f;
bouncyness = -1.0f;
frictionDir.Zero();
contactMotorDir.Zero();
contactMotorVelocity = 0.0f;
contactMotorForce = 0.0f;
mass = 1.0f;
invMass = 1.0f;
centerOfMass.Zero();
inertiaTensor = idMat3(1.0f);
inverseInertiaTensor = idMat3(1.0f);
parent = nullptr;
primaryConstraint = nullptr;
tree = nullptr;
std::memset(&fl, 0, sizeof(fl));
fl.isZero = 1;
fl.selfCollision = 1;
ZeroState(current);
saved = current;
inverseWorldSpatialInertia.Zero(6, 6);
I.Zero(6, 6);
invI.Zero(6, 6);
J.Zero(6, 6);
s.Zero();
totalForce.Zero();
auxForce.Zero();
acceleration.Zero();
response = nullptr;
responseIndex = nullptr;
numResponses = 0;
maxAuxiliaryIndex = 0;
maxSubTreeAuxiliaryIndex = 0;
}
void idAFBody::SetClipModel(idClipModel* const model) {
if (clipModel != nullptr && clipModel != model) {
clipModel->Delete();
}
clipModel = model;
}
void idAFBody::SetBouncyness(const float bounce) {
if (bounce < 0.0f || bounce > 1.0f) {
idLibPrint::Warning("idAFBody::SetBouncyness: %.1f out of range",
bounce);
return;
}
bouncyness = bounce;
}
void idAFBody::SetFriction(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) {
idLibPrint::Warning("idAFBody::SetFriction: invalid coefficients");
return;
}
linearFriction = linear;
angularFriction = angular;
contactFriction = contact;
linearFrictionWater = (std::min)(1.0f,
(std::max)(0.0f, linear * 1.2f));
angularFrictionWater = (std::min)(1.0f,
(std::max)(0.0f, angular * 1.2f));
}
void idAFBody::SetDensity(const float density,
const idMat3& inertiaScale) {
if (clipModel == nullptr) return;
clipModel->GetMassProperties(density, mass, centerOfMass,
inertiaTensor);
if (mass <= 0.0f) {
idLibPrint::Warning("idAFBody::SetDensity: invalid mass for '%s'",
name.c_str());
mass = 1.0f;
centerOfMass.Zero();
inertiaTensor = idMat3(1.0f);
}
invMass = 1.0f / mass;
inertiaTensor *= inertiaScale;
inverseInertiaTensor = inertiaTensor;
if (!inverseInertiaTensor.InverseSelf()) {
inverseInertiaTensor = idMat3(1.0f);
}
}
void idAFBody::SetFrictionDirection(const idVec3& direction) {
frictionDir = clipModel != nullptr
? clipModel->GetAxis().Transpose() * direction : direction;
fl.useFrictionDir = 1;
}
bool idAFBody::GetFrictionDirection(idVec3& direction) const {
if (!fl.useFrictionDir) return false;
direction = clipModel != nullptr
? clipModel->GetAxis() * frictionDir : frictionDir;
return true;
}
void idAFBody::SetContactMotorDirection(const idVec3& direction) {
contactMotorDir = clipModel != nullptr
? clipModel->GetAxis().Transpose() * direction : direction;
fl.useContactMotorDir = 1;
}
bool idAFBody::GetContactMotorDirection(idVec3& direction) const {
if (!fl.useContactMotorDir) return false;
direction = clipModel != nullptr
? clipModel->GetAxis() * contactMotorDir : contactMotorDir;
return true;
}
idVec3 idAFBody::GetPointVelocity(const idVec3& point) const {
const idVec3 linear(current.spatialVelocity[0],
current.spatialVelocity[1], current.spatialVelocity[2]);
const idVec3 angular(current.spatialVelocity[3],
current.spatialVelocity[4], current.spatialVelocity[5]);
const idVec3 origin = clipModel != nullptr
? clipModel->GetOrigin() : current.worldOrigin;
return linear + angular.Cross(point - origin);
}
void idAFBody::AddForce(const idVec3& point, const idVec3& force) {
current.externalForce[0] += force.x;
current.externalForce[1] += force.y;
current.externalForce[2] += force.z;
const idVec3 origin = clipModel != nullptr
? clipModel->GetOrigin() : current.worldOrigin;
const idVec3 torque = (point - origin).Cross(force);
current.externalForce[3] += torque.x;
current.externalForce[4] += torque.y;
current.externalForce[5] += torque.z;
}
void idAFBody::Serialize(idSerializer* const serializer) {
if (serializer != nullptr) GameLib_SerializeAFBody(serializer, *this);
}