pocket-physics
Version:
Verlet physics extracted from pocket-ces demos
172 lines (171 loc) • 7.83 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.collisionResponseAABB = void 0;
const v2_1 = require("./v2");
// Registers / Preallocations
const basis = (0, v2_1.v2)();
const basisNeg = (0, v2_1.v2)();
const vel1 = (0, v2_1.v2)();
const vel1x = (0, v2_1.v2)();
const vel1y = (0, v2_1.v2)();
const vel2 = (0, v2_1.v2)();
const vel2x = (0, v2_1.v2)();
const vel2y = (0, v2_1.v2)();
const newVel1 = (0, v2_1.v2)();
const newVel2 = (0, v2_1.v2)();
const t1 = (0, v2_1.v2)();
const t2 = (0, v2_1.v2)();
const u1 = (0, v2_1.v2)();
const u2 = (0, v2_1.v2)();
// TODO: Put this somewhere...
const EPSILON = 0.0001;
// TODO: change this API to accept numbers (x, y) instead of vectors
// friction calc: sqrt(friction1*friction2)
// restitution: box2d: https://github.com/erincatto/Box2D/blob/6a69ddbbd59b21c0d6699c43143b4114f7f92e21/Box2D/Box2D/Dynamics/Contacts/b2Contact.h#L42-L47
// Math.max(restitution1, restitution2);
/**
* Really should be called collisionResponseImpulse, as it has nothing to do
* with the shape of the bodies colliding. It's just two points with mass and
* friction.
* @param cpos1
* @param ppos1
* @param mass1
* @param restitution1 1 == perfectly elastic collision, 0 == all energy is
* killed.
* @param staticFriction1 How much friction must be overcome before the object
* will start moving. 0 == no friction, 1 == max friction. Set this higher, like
* 0.9.
* @param dynamicFriction1 How much constant friction occurs when the object is
* already in motion. 0 == no friction, 1 == max friction. Better to set this
* low, like 0.1.
* @param cpos2
* @param ppos2
* @param mass2
* @param restitution2 1 == perfectly elastic collision, 0 == all energy is
* killed.
* @param staticFriction2 How much friction must be overcome before the object
* will start moving. 0 == no friction, 1 == max friction. Set this higher, like
* 0.9.
* @param dynamicFriction2 How much constant friction occurs when the object is
* already in motion. 0 == no friction, 1 == max friction. Better to set this
* low, like 0.1.
* @param collisionNormal The vector defining the relative axis of collision.
* Leaving this as 0,0 will compute it as the midpoint between positions of the
* two colliding objects (modeling a circular collision). If colliding with a
* known edge or line segment, it's best to provide the edge normal as this
* value.
* @param vel1out The new velocity resulting from reacting to this collison.
* cpos1 - this value == new ppos1.
* @param vel2out The new velocity resulting from reacting to this collison.
* cpos2 - this value == new ppos2.
*/
const collisionResponseAABB = (cpos1, ppos1, mass1, restitution1, staticFriction1, dynamicFriction1, cpos2, ppos2, mass2, restitution2, staticFriction2, dynamicFriction2, collisionNormal, vel1out, vel2out) => {
// blank out all preallocated vectors.
(0, v2_1.set)(basis, 0, 0);
(0, v2_1.set)(basisNeg, 0, 0);
(0, v2_1.set)(vel1, 0, 0);
(0, v2_1.set)(vel1x, 0, 0);
(0, v2_1.set)(vel1y, 0, 0);
(0, v2_1.set)(vel2, 0, 0);
(0, v2_1.set)(vel2x, 0, 0);
(0, v2_1.set)(vel2y, 0, 0);
(0, v2_1.set)(newVel1, 0, 0);
(0, v2_1.set)(newVel2, 0, 0);
(0, v2_1.set)(t1, 0, 0);
(0, v2_1.set)(t2, 0, 0);
(0, v2_1.set)(u1, 0, 0);
(0, v2_1.set)(u2, 0, 0);
// If collisionNormal is provided, use it. Otherwise, use midpoint between
// current positions as axis of collision. Midpoint will model a circular
// collision if used.
if (collisionNormal && (collisionNormal.x !== 0 || collisionNormal.y !== 0)) {
(0, v2_1.set)(basis, collisionNormal.x, collisionNormal.y);
}
else {
(0, v2_1.sub)(basis, cpos1, cpos2);
(0, v2_1.normalize)(basis, basis);
}
(0, v2_1.scale)(basisNeg, basis, -1);
//const friction;
// Take max of restitutions, like box2d does.
// https://github.com/erincatto/Box2D/blob/6a69ddbbd59b21c0d6699c43143b4114f7f92e21/Box2D/Box2D/Dynamics/Contacts/b2Contact.h#L42-L47
// "for example, a superball bounces on everything"
const restitution = restitution1 > restitution2 ? restitution1 : restitution2;
const massTotal = mass1 + mass2;
const e = 1 + restitution;
// I = (1+e)*N*(Vr • N) / (1/Ma + 1/Mb)
// Va -= I * 1/Ma
// Vb += I * 1/Mb
//sub(vel1, cpos1, ppos1);
//sub(vel2, cpos2, ppos2);
//const relativeVelocity = sub(vel1, vel2);
//const I = v2();
//scale(I, normal, (1 + restitution) * dot(relativeVelocity, normal));
//scale(I, I, 1 / (1/mass1 + 1/mass2));
// "x" and "y" in the following sections are shorthand for:
// x: component of the box velocity parallel to the collision normal
// y: the rest of the collision velocity
// calculate x-direction velocity vector and perpendicular y-vector for box 1
(0, v2_1.sub)(vel1, cpos1, ppos1);
const x1 = (0, v2_1.dot)(basis, vel1);
(0, v2_1.scale)(vel1x, basis, x1);
(0, v2_1.sub)(vel1y, vel1, vel1x);
// calculate x-direction velocity vector and perpendicular y-vector for box 2
(0, v2_1.sub)(vel2, cpos2, ppos2);
const x2 = (0, v2_1.dot)(basisNeg, vel2);
(0, v2_1.scale)(vel2x, basisNeg, x2);
(0, v2_1.sub)(vel2y, vel2, vel2x);
// equations of motion for box1
(0, v2_1.scale)(t1, vel1x, (mass1 - mass2) / massTotal);
(0, v2_1.scale)(t2, vel2x, (e * mass2) / massTotal);
(0, v2_1.add)(newVel1, t1, t2);
(0, v2_1.add)(newVel1, newVel1, vel1y);
// equations of motion for box2
(0, v2_1.scale)(u1, vel1x, (e * mass1) / massTotal);
(0, v2_1.scale)(u2, vel2x, (mass2 - mass1) / massTotal);
(0, v2_1.add)(newVel2, u1, u2);
(0, v2_1.add)(newVel2, newVel2, vel2y);
// new relative velocity
const rv = (0, v2_1.add)((0, v2_1.v2)(), newVel1, newVel2);
// tangent to relative velocity vector
const reg1 = (0, v2_1.v2)();
(0, v2_1.scale)(reg1, basis, (0, v2_1.dot)(rv, basis));
const tangent = (0, v2_1.sub)((0, v2_1.v2)(), rv, reg1);
(0, v2_1.normalize)(tangent, tangent);
// magnitude of relative velocity in tangent direction
let jt = -(0, v2_1.dot)(rv, tangent);
jt /= 1 / (mass1 + mass2); // not sure about this...
// https://github.com/RandyGaul/ImpulseEngine/blob/d12af9c95555244a37dce1c7a73e60d5177df652/Manifold.cpp#L103
const jtMag = Math.abs(jt);
// only apply significant friction
if (jtMag > EPSILON) {
// magnitudes of velocity along the collision tangent, hopefully.
const vel1ymag = (0, v2_1.magnitude)(vel1y);
const vel2ymag = (0, v2_1.magnitude)(vel2y);
// compute Coulumb's law (choosing dynamic vs static friction)
const frictionImpulse1 = (0, v2_1.v2)();
const frictionImpulse2 = (0, v2_1.v2)();
// TODO: may need to use Math.max(Math.abs(vel1ymag, vel2ymag)) when
// choosing to incorporate velocity magnitude into the friction calc.
// A stationary box getting hit currently receives perfect energy
// transfer, since its vel2ymag is 0.
if (jtMag < vel1ymag * staticFriction1) {
(0, v2_1.scale)(frictionImpulse1, tangent, staticFriction1);
}
else {
(0, v2_1.scale)(frictionImpulse1, tangent, -vel1ymag * dynamicFriction1);
}
if (jtMag < vel2ymag * staticFriction2) {
(0, v2_1.scale)(frictionImpulse2, tangent, staticFriction2);
}
else {
(0, v2_1.scale)(frictionImpulse2, tangent, -vel2ymag * dynamicFriction2);
}
(0, v2_1.add)(newVel1, newVel1, frictionImpulse1);
(0, v2_1.add)(newVel2, newVel2, frictionImpulse2);
}
// output new velocity of box1 and box2
(0, v2_1.copy)(vel1out, newVel1);
(0, v2_1.copy)(vel2out, newVel2);
};
exports.collisionResponseAABB = collisionResponseAABB;