@enable3d/ammo-physics
Version:
Physics Plugin for three.js
171 lines • 10.6 kB
JavaScript
/**
* @author Yannick Deubel (https://github.com/yandeu)
* @copyright Copyright (c) 2020 Yannick Deubel; Project Url: https://github.com/enable3d/enable3d
* @license {@link https://github.com/enable3d/enable3d/blob/master/LICENSE|LGPL-3.0}
*/
export default class Constraints {
worldTransform;
physicsWorld;
tmpBtVector3 = new Ammo.btVector3();
constructor(worldTransform, physicsWorld) {
this.worldTransform = worldTransform;
this.physicsWorld = physicsWorld;
}
toAmmoV3(v, d = 0) {
return new Ammo.btVector3(typeof v?.x !== 'undefined' ? v.x : d, typeof v?.y !== 'undefined' ? v.y : d, typeof v?.z !== 'undefined' ? v.z : d);
}
get addConstraints() {
return {
lock: (bodyA, bodyB, disableCollisionsBetweenLinkedBodies) => this.lock(bodyA, bodyB, disableCollisionsBetweenLinkedBodies),
fixed: (bodyA, bodyB, disableCollisionsBetweenLinkedBodies) => this.fixed(bodyA, bodyB, disableCollisionsBetweenLinkedBodies),
pointToPoint: (bodyA, bodyB, config, disableCollisionsBetweenLinkedBodies) => this.pointToPoint(bodyA, bodyB, config, disableCollisionsBetweenLinkedBodies),
hinge: (bodyA, bodyB, config, disableCollisionsBetweenLinkedBodies) => this.hinge(bodyA, bodyB, config, disableCollisionsBetweenLinkedBodies),
slider: (bodyA, bodyB, config = {}, disableCollisionsBetweenLinkedBodies) => this.slider(bodyA, bodyB, config, disableCollisionsBetweenLinkedBodies),
spring: (bodyA, bodyB, config = {}, disableCollisionsBetweenLinkedBodies) => this.spring(bodyA, bodyB, config, disableCollisionsBetweenLinkedBodies),
coneTwist: (bodyA, bodyB, config = { frameA: {}, frameB: {} }, disableCollisionsBetweenLinkedBodies) => this.coneTwist(bodyA, bodyB, config, disableCollisionsBetweenLinkedBodies),
dof: (bodyA, bodyB, config, disableCollisionsBetweenLinkedBodies) => this.dof(bodyA, bodyB, config, disableCollisionsBetweenLinkedBodies)
};
}
getTransform(bodyA, bodyB, offset = { x: 0, y: 0, z: 0 }, center = false) {
// @ts-expect-error: Should be refactored to avoid the ts error.
offset = { x: 0, y: 0, z: 0, ...offset };
const centerVector = (v1, v2) => {
var dx = (v1.x() - v2.x()) / 2 + offset.x;
var dy = (v1.y() - v2.y()) / 2 + offset.y;
var dz = (v1.z() - v2.z()) / 2 + offset.z;
return new Ammo.btVector3(dx, dy, dz);
};
const transformB = new Ammo.btTransform();
transformB.setIdentity();
if (!center) {
// offset
transformB.setOrigin(new Ammo.btVector3(offset.x, offset.y, offset.z));
const transformA = bodyA.getCenterOfMassTransform().inverse().op_mul(bodyB.getWorldTransform()).op_mul(transformB);
return { transformA: transformA, transformB: transformB };
}
else {
const center = centerVector(bodyA.getWorldTransform().getOrigin(), bodyB.getWorldTransform().getOrigin());
const transformB = new Ammo.btTransform();
transformB.setIdentity();
transformB.setOrigin(center);
const transformA = bodyA.getCenterOfMassTransform().inverse().op_mul(bodyB.getWorldTransform());
transformA.op_mul(transformB);
return { transformA: transformA, transformB: transformB };
}
}
lock(bodyA, bodyB, disableCollisionsBetweenLinkedBodies = true) {
const zero = { x: 0, y: 0, z: 0 };
return this.dof(bodyA, bodyB, { angularLowerLimit: zero, angularUpperLimit: zero }, disableCollisionsBetweenLinkedBodies);
}
fixed(bodyA, bodyB, disableCollisionsBetweenLinkedBodies = true) {
const transform = this.getTransform(bodyA.ammo, bodyB.ammo);
transform.transformA.setRotation(bodyA.ammo.getWorldTransform().getRotation());
transform.transformB.setRotation(bodyB.ammo.getWorldTransform().getRotation());
const constraint = new Ammo.btFixedConstraint(bodyA.ammo, bodyB.ammo, transform.transformA, transform.transformB);
this.physicsWorld.addConstraint(constraint, disableCollisionsBetweenLinkedBodies);
return constraint;
}
pointToPoint(bodyA, bodyB, config = {}, disableCollisionsBetweenLinkedBodies = true) {
const { pivotA, pivotB } = config;
const pivotV3 = new Ammo.btVector3(pivotA?.x || 0, pivotA?.y || 0, pivotA?.z || 0);
const targetPivotV3 = new Ammo.btVector3(pivotB?.x || 0, pivotB?.y || 0, pivotB?.z || 0);
const constraint = new Ammo.btPoint2PointConstraint(bodyA.ammo, bodyB.ammo, pivotV3, targetPivotV3);
this.physicsWorld.addConstraint(constraint, disableCollisionsBetweenLinkedBodies);
return constraint;
}
hinge(bodyA, bodyB, config = {}, disableCollisionsBetweenLinkedBodies = true) {
const { pivotA, pivotB, axisA, axisB } = config;
const pivotV3 = new Ammo.btVector3(pivotA?.x || 0, pivotA?.y || 0, pivotA?.z || 0);
const targetPivotV3 = new Ammo.btVector3(pivotB?.x || 0, pivotB?.y || 0, pivotB?.z || 0);
const axisV3 = new Ammo.btVector3(axisA?.x || 0, axisA?.y || 0, axisA?.z || 0);
const targetAxisV3 = new Ammo.btVector3(axisB?.x || 0, axisB?.y || 0, axisB?.z || 0);
const constraint = new Ammo.btHingeConstraint(bodyA.ammo, bodyB.ammo, pivotV3, targetPivotV3, axisV3, targetAxisV3, true);
this.physicsWorld.addConstraint(constraint, disableCollisionsBetweenLinkedBodies);
return constraint;
}
// https://pybullet.org/Bullet/phpBB3/viewtopic.php?f=9&t=12690&p=42152&hilit=btSliderConstraint#p42152
slider(bodyA, bodyB, config = {}, disableCollisionsBetweenLinkedBodies = true) {
const transform = this.getTransform(bodyA.ammo, bodyB.ammo);
const { frameA = {}, frameB = {}, linearLowerLimit: lll = 0, linearUpperLimit: lul = 0, angularLowerLimit: all = 0, angularUpperLimit: aul = 0 } = config;
const rotationA = transform.transformA.getRotation();
rotationA.setEulerZYX(frameA.x || 0, frameA.y || 0, frameA.z || 0);
transform.transformA.setRotation(rotationA);
const rotationB = transform.transformB.getRotation();
rotationB.setEulerZYX(frameB.x || 0, frameB.y || 0, frameB.z || 0);
transform.transformB.setRotation(rotationB);
const constraint = new Ammo.btSliderConstraint(bodyA.ammo, bodyB.ammo, transform.transformA, transform.transformB, true);
constraint.setLowerLinLimit(lll);
constraint.setUpperLinLimit(lul);
constraint.setLowerAngLimit(all);
constraint.setUpperAngLimit(aul);
this.physicsWorld.addConstraint(constraint, disableCollisionsBetweenLinkedBodies);
return constraint;
}
spring(bodyA, bodyB, config = {}, disableCollisionsBetweenLinkedBodies = true) {
const { stiffness = 50, damping = 0.01, angularLock = false, linearLowerLimit: lll = {}, linearUpperLimit: lul = {}, angularLowerLimit: all = {}, angularUpperLimit: aul = {}, offset = {}, center = false, enableSpring = true } = config;
const off = { x: 0, y: 0, z: 0, ...offset };
const transform = this.getTransform(bodyA.ammo, bodyB.ammo, off, center);
const constraint = new Ammo.btGeneric6DofSpringConstraint(bodyA.ammo, bodyB.ammo, transform.transformA, transform.transformB, true);
this.tmpBtVector3.setValue(lll.x || 0, lll.y || 0, lll.z || 0);
constraint.setLinearLowerLimit(this.tmpBtVector3);
this.tmpBtVector3.setValue(lul.x || 0, lul.y || 0, lul.z || 0);
constraint.setLinearUpperLimit(this.tmpBtVector3);
if (angularLock) {
this.tmpBtVector3.setValue(0, 0, 0);
constraint.setAngularLowerLimit(this.tmpBtVector3);
constraint.setAngularUpperLimit(this.tmpBtVector3);
}
else {
console.log(all, aul);
constraint.setAngularLowerLimit(this.toAmmoV3(all, -Math.PI));
constraint.setAngularUpperLimit(this.toAmmoV3(aul, Math.PI));
}
for (let i = 0; i < 3; i++) {
constraint.enableSpring(i, enableSpring);
constraint.setStiffness(i, stiffness);
constraint.setDamping(i, damping);
}
// I have no idea what setEquilibriumPoint does :/
// constraint.setEquilibriumPoint()
this.physicsWorld.addConstraint(constraint, disableCollisionsBetweenLinkedBodies);
return constraint;
}
coneTwist(bodyA, bodyB, config, disableCollisionsBetweenLinkedBodies = true) {
const { frameA, frameB } = config;
const rbAFrame = new Ammo.btTransform();
rbAFrame.setIdentity();
rbAFrame.getOrigin().setValue(frameA?.x || 0, frameA?.y || 0, frameA?.z || 0);
const rbBFrame = new Ammo.btTransform();
rbBFrame.setIdentity();
rbBFrame.getOrigin().setValue(frameB?.x || 0, frameB?.y || 0, frameB?.z || 0);
const t = this.getTransform(bodyA.ammo, bodyB.ammo);
const constraint = new Ammo.btConeTwistConstraint(bodyB.ammo, bodyA.ammo, rbAFrame, rbBFrame);
// does not work at all :/
// constraint.setLimit(-Math.PI / 50, Math.PI / 50)
constraint.setAngularOnly(true);
this.physicsWorld.addConstraint(constraint, disableCollisionsBetweenLinkedBodies);
return constraint;
}
dof(bodyA, bodyB, config = {}, disableCollisionsBetweenLinkedBodies = true) {
const { offset, center = false } = config;
const off = { x: 0, y: 0, z: 0, ...offset };
const transform = this.getTransform(bodyA.ammo, bodyB.ammo, off, center);
const constraint = new Ammo.btGeneric6DofConstraint(bodyA.ammo, bodyB.ammo, transform.transformA, transform.transformB, true);
const { linearLowerLimit, linearUpperLimit, angularLowerLimit, angularUpperLimit } = config;
const lll = this.toAmmoV3(linearLowerLimit);
const lul = this.toAmmoV3(linearUpperLimit);
const all = this.toAmmoV3(angularLowerLimit, -Math.PI);
const aul = this.toAmmoV3(angularUpperLimit, Math.PI);
constraint.setLinearLowerLimit(lll);
constraint.setLinearUpperLimit(lul);
constraint.setAngularLowerLimit(all);
constraint.setAngularUpperLimit(aul);
Ammo.destroy(lll);
Ammo.destroy(lul);
Ammo.destroy(all);
Ammo.destroy(aul);
this.physicsWorld.addConstraint(constraint, disableCollisionsBetweenLinkedBodies);
return constraint;
}
}
//# sourceMappingURL=constraints.js.map