介绍
计算碰撞冲量,改变速度,无摩擦力。非线性移动穿透。
方法
单位冲量引起的速度改变量
分为线速度改变量和角速度改变量
线速度改变量
角速度改变量
转动惯性,单位冲量矩,碰撞点相对质心位置
角速度改变量引起的速度改变量
碰撞引起的速度改变量
碰撞前的相对速度
碰撞后的相对速度
为恢复系数,0~1之间,0.4像有弹性
改变量
碰撞冲量
应用冲量
实现
void collisionResolution(PiratePhysics::CollisionShape &a, PiratePhysics::CollisionShape&b, Eigen::Vector3f &penatration)
{
// contact point
Vector3f contactPoint = a.localGetSupportingVertex(penatration);
// contact world
Vector3f contactNormal = -penatration.normalized(), y, z;
if(abs(penatration[0]) > abs(penatration[1]))
{
y = {0.f, 1.0f, 0.0f};
}
else
{
y = {1.0f, 0.0f, 1.0f};
}
makeOrthonormalBasis(contactNormal, y, z);
Matrix3f contactWorld;
contactWorld.block<3, 1>(0, 0) = contactNormal;
contactWorld.block<3, 1>(0, 1) = y;
contactWorld.block<3, 1>(0, 2) = z;
// velocity change per impluse
Vector3f linearVelPer = Vector3f{1.0f, 1.0f, 1.0f}*a.getMassInv();
Vector3f positionRel = contactPoint - a.getOrigin();
Vector3f unitImpluseTorque = positionRel.cross(-penatration.normalized());
Vector3f angularVelPer= a.getInertiaInv() * unitImpluseTorque;
Vector3f velocityDeltaPer = linearVelPer + angularVelPer.cross(positionRel);
// desire velocity change
Vector3f velocityRelBefore = a.getVelocity() + b.getVelocity();
float resitution = 0.4f;
Vector3f velocityRelAfter = -resitution * velocityRelBefore;
Vector3f velocityDeltaDesire = velocityRelAfter - velocityRelBefore;
// impluse
Vector3f impluse = velocityDeltaDesire.cwiseQuotient(velocityDeltaPer);
Vector3f impluseTorque = positionRel.cross(impluse);
// apply impluse
Vector3f velocity = a.getVelocity() + a.getMassInv() * impluse;
a.setVelocity(velocity);
Vector3f omega = a.getOmega() + a.getInertiaInv() * impluseTorque;
a.setOmega(omega);
// resolving interpenetration
Vector3f angularInertiaWorld = positionRel.cross(contactNormal);
angularInertiaWorld = a.getInertiaInv() * angularInertiaWorld;
angularInertiaWorld = angularInertiaWorld.cross(positionRel);
float angularInertia = angularInertiaWorld.dot(contactNormal);
float linearInertia = a.getMassInv();
float totalInertia = linearInertia + angularInertia;
float linearMove = penatration.norm() * linearInertia / totalInertia;
float angularMove = penatration.norm() * angularInertia / totalInertia;
// Check that the angular move is within limits.
float angularLimitConstant = 0.2f;
float limit = angularLimitConstant * positionRel.norm();
if (abs(angularMove) > limit)
{
float totalMove = linearMove + angularMove;
// Set the new angular move, with the same sign as before.
if (angularMove >= 0)
{
angularMove = limit;
}
else
{
angularMove = -limit;
}
// Make the linear move take the extra slack.
linearMove = totalMove - angularMove;
}
// applying movement
Vector3f origin = a.getOrigin() + 10*contactNormal * linearMove;
a.setOrigin(origin);
Matrix3f inverseInertiaTensor = a.getInertiaInv();
Vector3f impulsiveTorque = positionRel.cross(contactNormal);
Vector3f impulsePerMove = inverseInertiaTensor * impluseTorque;
Vector3f rotationPerMove = impulsePerMove / angularInertia;
Vector3f rotation = rotationPerMove * angularMove;
Matrix3f rotationMatrix;
rotationMatrix = AngleAxisf(rotation[0], Vector3f::UnitX())
* AngleAxisf(rotation[1], Vector3f::UnitY())
* AngleAxisf(rotation[2], Vector3f::UnitZ());
Matrix3f rotationChange = a.getRotation() * rotationMatrix;
a.setRotation(rotationChange);
}
测试
参考
- Ian Millington, 14 - Collision Resolution, Editor(s): Ian Millington, Game Physics Engine Development (Second Edition), Morgan Kaufmann, 2010, Pages 335-385, ISBN 9780123819765, doi.org/10.1016/B97…. (www.sciencedirect.com/science/art…)