admin管理员组

文章数量:1355540

I am trying to 'freeze objects together' using Bullet (via JavaScript + Three.JS + Ammo.js)

Here is my scene

Here we see a Merry-go-round simulation, in which 8 horses are generated above a turntable/disc.

This GIF illustrates the system working as expected - the 8 horses (instances of bodyA) are frozen-in-place to the turntable/disc (bodyB)

Each bodyA (horse) has a bTFixedConstraint with bodyB (disc). The problem becomes apparent when the disc (bodyB) has different rotation applied: the horses are being rotated by a factor of bodyB's rotation, knocking them off of their target axis.

When the physics starts, it causes the FixedConstraints to activate. Because the FixedConstraints are not configured correctly, it causes the horses to lose alignment with their target axis.

I have tried every combination of absolute and relative rotations that I can think of (to the point of making an interface to trial different combinations), but the horses never adhere correctly to the turntable.

Here's a simplified version of my btFixedConstraint implementation

// bodyA = the rigidBody being attached
// bodyB = what bodyA is being attached to
const applyFixedConstraint = (bodyA, bodyB) => {
    const relRotationAB = getRelativeRotation(bodyA, bodyB);
    const relPosBA = getRelativePosition(bodyB, bodyA);
    // FRAME IN A
    const frameInA = new Ammo.btTransform();
    frameInA.setIdentity();
    frameInA.getOrigin().setValue(0, 0, 0);
    frameInA.setRotation(new Ammo.btQuaternion(0,0,0,1)); // i.e. 'none'
    // FRAME IN B
    const frameInB = new Ammo.btTransform();
    frameInB.setIdentity();
    frameInB.getOrigin().setValue(relPosBA.x, relPosBA.y, relPosBA.z); 
    frameInB.setRotation(new Ammo.btQuaternion(relRotationAB.x, relRotationAB.y, relRotationAB.z, relRotationAB.w));
    // RESULT
    const fixedConstraint = new Ammo.btFixedConstraint(bodyA,bodyB,frameInA,frameInB); // this line doesn't "freeze items in the air" like it should
    physicsWorld.addConstraint(fixedConstraint, true);
    return fixedConstraint;
}

Objects in Bullet/Ammo.js typically have a variety of different constructor patterns but btFixedConstraint only just one: bodyA,bodyB,frameInA,frameInB

Since the bodyA/bodyB are unlikely to be causing an issue, the solution MUST be to fix the frameInA or frameInB parameters.

That said, how can I calculate the correct btTransform's (frameInA and frameInB) for freezing bodies together using btFixedConstraint ?

I am trying to 'freeze objects together' using Bullet (via JavaScript + Three.JS + Ammo.js)

Here is my scene https://codepen.io/PartyPants2/pen/emYLRPX?editors=0010

Here we see a Merry-go-round simulation, in which 8 horses are generated above a turntable/disc.

This GIF illustrates the system working as expected - the 8 horses (instances of bodyA) are frozen-in-place to the turntable/disc (bodyB)

Each bodyA (horse) has a bTFixedConstraint with bodyB (disc). The problem becomes apparent when the disc (bodyB) has different rotation applied: the horses are being rotated by a factor of bodyB's rotation, knocking them off of their target axis.

When the physics starts, it causes the FixedConstraints to activate. Because the FixedConstraints are not configured correctly, it causes the horses to lose alignment with their target axis.

I have tried every combination of absolute and relative rotations that I can think of (to the point of making an interface to trial different combinations), but the horses never adhere correctly to the turntable.

Here's a simplified version of my btFixedConstraint implementation

// bodyA = the rigidBody being attached
// bodyB = what bodyA is being attached to
const applyFixedConstraint = (bodyA, bodyB) => {
    const relRotationAB = getRelativeRotation(bodyA, bodyB);
    const relPosBA = getRelativePosition(bodyB, bodyA);
    // FRAME IN A
    const frameInA = new Ammo.btTransform();
    frameInA.setIdentity();
    frameInA.getOrigin().setValue(0, 0, 0);
    frameInA.setRotation(new Ammo.btQuaternion(0,0,0,1)); // i.e. 'none'
    // FRAME IN B
    const frameInB = new Ammo.btTransform();
    frameInB.setIdentity();
    frameInB.getOrigin().setValue(relPosBA.x, relPosBA.y, relPosBA.z); 
    frameInB.setRotation(new Ammo.btQuaternion(relRotationAB.x, relRotationAB.y, relRotationAB.z, relRotationAB.w));
    // RESULT
    const fixedConstraint = new Ammo.btFixedConstraint(bodyA,bodyB,frameInA,frameInB); // this line doesn't "freeze items in the air" like it should
    physicsWorld.addConstraint(fixedConstraint, true);
    return fixedConstraint;
}

Objects in Bullet/Ammo.js typically have a variety of different constructor patterns but btFixedConstraint only just one: bodyA,bodyB,frameInA,frameInB

Since the bodyA/bodyB are unlikely to be causing an issue, the solution MUST be to fix the frameInA or frameInB parameters.

That said, how can I calculate the correct btTransform's (frameInA and frameInB) for freezing bodies together using btFixedConstraint ?

Share Improve this question edited Mar 31 at 13:38 1owk3y asked Mar 31 at 7:37 1owk3y1owk3y 1,2021 gold badge16 silver badges33 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

Update: further testing indicates moving BodyB after starting the simulation disrupts the axis of BodyA, so I'm likely incorrect after all. I am leaving this answer here until I can revise it as it may help someone else, it certainly would have helped me...

Well this took an unexpected turn. Hopefully it helps someone else out there.

BEHOLD! It is working: https://codepen.io/PartyPants2/pen/KwKrdvq

If you are as misfortunate as me and got stuck on this for literally a whole week, then I'm afraid I have some more bad news: this solution is so confounding that it will only raise further questions about the inner workings of Bullet/Ammo, and I do not have answers for you. And I never will.

All I know is this:

let motionStateB = bodyB.getMotionState();
const transformB = new Ammo.btTransform();
motionStateB.getWorldTransform(transformB);
console.log("Before", rotationA.x(), rotationA.y(), rotationA.z(), 
rotationA.w())
// Before 0 0.9238795638084412 0 -0.3826834559440613
const rotationB = transformB.getRotation();
console.log("After", rotationA.x(), rotationA.y(), rotationA.z(), 
rotationA.w());
// After 0 0 0 1
// Yes, seriously. It deleted rotationA's values for no reason.
// I don't even think this is rotationB's values. It's just... gone?? 

For some HORRIBLE reason, if you transformB.getRotation() it DELETES the values of rotationA.

Now that you know you have incorrect input values for absolutely no reason, we can now simply pre-store those quaternions and use them in the final transforms, making the solution to the question simply:

  • The correct frameInA rotation is bodyA's rotation.
  • The correct frameInB rotation is bodyB's rotation.

Update: nevermind, this is probably wrong. read top

(Just make sure you are getting real values for those quaternions, Ammo likes to obliterate unrelated memory references for some reason I will never understand)

See CodePen for proof

本文标签: