engine/game/rigid.cc
2024-01-07 04:36:33 +00:00

263 lines
6.3 KiB
C++

//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#include "game/rigid.h"
#include "console/console.h"
//----------------------------------------------------------------------------
Rigid::Rigid()
{
state.force = state.torque =
state.linVelocity =
state.linPosition =
state.linMomentum =
state.angVelocity =
state.angMomentum = Point3F(0,0,0);
state.angPosition.identity();
state.invWorldInertia.identity();
mass = oneOverMass = 1.0;
invObjectInertia.identity();
restitution = 0.3;
friction = 0.5;
angDamping = 0;
linDamping = 0;
atRest = false;
microCount = 0;
}
void Rigid::clearForces()
{
state.force.set(0,0,0);
state.torque.set(0,0,0);
}
void Rigid::integrate(State& t,F32 delta)
{
lastDelta = delta;
t.linPosition += t.linVelocity * delta;
t.linMomentum += t.force * delta;
t.linVelocity = t.linMomentum * oneOverMass;
// // Linear momentum and velocity
// t.linPosition = state.linPosition + (state.linVelocity * delta);
// t.linMomentum = state.linMomentum + (state.force * delta);
// t.linVelocity = t.linMomentum * oneOverMass;
// Angular position is average velocity * delta
F32 sinHalfAngle;
F32 angle = state.angVelocity.len();
if (angle != 0.0f) {
QuatF dq;
mSinCos(angle * delta * -0.5,
sinHalfAngle,
dq.w);
sinHalfAngle *= 1 / angle;
dq.x = t.angVelocity.x * sinHalfAngle;
dq.y = t.angVelocity.y * sinHalfAngle;
dq.z = t.angVelocity.z * sinHalfAngle;
QuatF temp = t.angPosition;
t.angPosition.mul(temp, dq);
t.angPosition.normalize();
}
else
{
//t.angPosition = state.angPosition;
}
t.angMomentum += t.torque * delta;
// Move angular momentum into world space
MatrixF iv,qmat;
t.angPosition.setMatrix(&qmat);
iv.mul(qmat,invObjectInertia);
qmat.transpose();
t.invWorldInertia.mul(iv,qmat);
t.invWorldInertia.mulV(t.angMomentum, &t.angVelocity);
}
void Rigid::updateVelocity(State& t)
{
t.linVelocity.x = t.linMomentum.x * oneOverMass;
t.linVelocity.y = t.linMomentum.y * oneOverMass;
t.linVelocity.z = t.linMomentum.z * oneOverMass;
t.invWorldInertia.mulV(t.angMomentum,&t.angVelocity);
}
void Rigid::applyImpulse(State& t, const Point3F &r, const Point3F &impulse)
{
atRest = false;
// Linear momentum and velocity
t.linMomentum += impulse;
t.linVelocity.x = t.linMomentum.x * oneOverMass;
t.linVelocity.y = t.linMomentum.y * oneOverMass;
t.linVelocity.z = t.linMomentum.z * oneOverMass;
// Rotational momentum and velocity
Point3F tv;
mCross(r,impulse,&tv);
t.angMomentum += tv;
t.invWorldInertia.mulV(t.angMomentum, &t.angVelocity);
}
// bool Rigid::resolveCollision(State& s, const Point3F& p, Point3F normal, Rigid* rigid)
// {
// atRest = false;
// Point3F v1,v2,r1,r2;
// r1 = p - s.linPosition;
// s.getVelocity(r1,&v1);
// r2 = p - rigid->state.linPosition;
// rigid->getVelocity(r2,&v2);
// // Make sure they are converging
// F32 nv = mDot(v1,normal);
// nv -= mDot(v2,normal);
// if (nv > 0)
// return false;
// // Compute impulse
// F32 d, n = -nv * (1 + restitution) * rigid->restitution;
// Point3F a1,b1,c1;
// mCross(r1,normal,&a1);
// s.invWorldInertia.mulV(a1,&b1);
// mCross(b1,r1,&c1);
// Point3F a2,b2,c2;
// mCross(r2,normal,&a2);
// rigid->state.invWorldInertia.mulV(a2,&b2);
// mCross(b2,r2,&c2);
// Point3F c3 = c1 + c2;
// d = oneOverMass + rigid->oneOverMass + mDot(c3,normal);
// Point3F impulse = normal * (n / d);
// applyImpulse(s, r1,impulse);
// impulse.neg();
// rigid->applyImpulse(r2,impulse);
// return true;
// }
bool Rigid::resolveCollision(State& s, const Point3F& p, Point3F normal)
{
atRest = false;
Point3F v1,r1 = p - s.linPosition;
s.getVelocity(r1,&v1);
F32 n = -mDot(v1,normal);
if (n >= 0) {
// Collision impulse
F32 d = getZeroImpulse(s,r1,normal);
F32 j = n * (1 + restitution) * d;
Point3F impulse = normal * j;
// Friction impulse
Point3F uv = v1 + (normal * n);
F32 ul = uv.len();
if (ul) {
uv /= -ul;
F32 u = n * d * friction * getZeroImpulse(s,r1,uv);
impulse += uv * u;
}
//
applyImpulse(s, r1,impulse);
}
return true;
}
F32 Rigid::getZeroImpulse(State& s,const Point3F& r,const Point3F& normal)
{
Point3F a,b,c;
mCross(r,normal,&a);
s.invWorldInertia.mulV(a,&b);
mCross(b,r,&c);
return 1 / (oneOverMass + mDot(c,normal));
}
void Rigid::getVelocity(const Point3F &r, Point3F *v)
{
state.getVelocity(r, v);
}
F32 Rigid::getKineticEnergy(const MatrixF& wto)
{
Point3F w;
wto.mulV(state.angVelocity,&w);
const F32* f = invObjectInertia;
return 0.5 * ((mass * mDot(state.linVelocity,state.linVelocity)) +
w.x * w.x / f[0] +
w.y * w.y / f[5] +
w.z * w.z / f[10]);
}
void Rigid::State::getVelocity(const Point3F& r, Point3F* v)
{
mCross(angVelocity, r, v);
*v += linVelocity;
}
void Rigid::State::getTransform(MatrixF* mat)
{
angPosition.setMatrix(mat);
mat->setColumn(3,linPosition);
}
void Rigid::State::setTransform(const MatrixF& mat)
{
angPosition.set(mat);
mat.getColumn(3,&linPosition);
}
void Rigid::setObjectInertia(const Point3F& r)
{
// Rotational moment of inertia of a box
F32 ot = mass/12;
F32 a = r.x * r.x;
F32 b = r.y * r.y;
F32 c = r.z * r.z;
F32* f = invObjectInertia;
invObjectInertia.identity();
// f[0] = 1 / (ot * (b + c));
// f[5] = 1 / (ot * (c + a));
// f[10] = 1 / (ot * (a + b));
MatrixF iv,qmat;
state.angPosition.setMatrix(&qmat);
iv.mul(qmat,invObjectInertia);
qmat.transpose();
state.invWorldInertia.mul(iv,qmat);
}
//----------------------------------------------------------------------------
bool Rigid::checkRestCondition()
{
if (state.linMomentum.len() < 0.1f &&
state.angMomentum.len() < 0.05f) {
setAtRest();
}
return atRest;
}
void Rigid::setAtRest()
{
atRest = true;
state.linVelocity =
state.linMomentum =
state.angVelocity =
state.angMomentum =
Point3F(0,0,0);
}