How do I fix exploding end-to-end constraints when using Cannon.js?

166 Views Asked by At

Overview

I'm using three.js and cannon.js to create jiggly, dangling, cables inside of a 3D world. To create each cable, I have strung together a series of cylinder segments using end-to-end constraints - similar to how you would create a necklace out of dried pasta.

Problem

As I add more segments to my cables to increase their realism, I seem to hit a threshold where the cable segments start to pull away from each other and shake violently. I believe gravity is pulling apart my end-to-end constraints, forcing the objects that make up my cable to accelerate and spin/bounce/explode out of control. I am able to mitigate this behavior by reducing my world's gravity, but I end up with cables that move and float unnaturally. I'd would like to know how to make a cable out of N segments that doesn't 'pull itself apart'.

Image on the left shows the cable 'holding together', while the image on the right shows the cable 'exploding' with the addition of more segments

Detail

I have a simple method that creates a cable, provided the following parameters:

  1. Length: The overall length of the cable
  2. Segment Count: How many segments make up the overall length of the cable
  3. Diameter: Diameter of each cable segment

My physics world is setup as follows:

  1. Y Gravity: -30
  2. quatNormalizeSkip: 0 (using a value greater than 0 makes the chain bounce and explode even worse)
  3. quatNormalizeFast: false (using true causes explosion)

I developed the following method to create my physics bodies:

function addCablesToPhysicsWorld(){
    const cableSegmentMass = 0.001;
    const linearDamping = 0.9;
    const angularDamping = 0.9;

    cables.forEach(cable => {
        const numSegments = cable.segmentPositions.length;
        let previousBody;
        cable.segmentPositions.forEach((segment, index) => {
            const segmentLength = cable.length / numSegments;
            const segmentOverlap = 0.01;
            const physicsBoxLength = (segmentLength / 2) - (segmentOverlap * 2);
            const bodyMass = index === 0 || index === numSegments - 1 ? 0 : cableSegmentMass; // First and last segments of a cable have no mass so they stay in place, all other segments 'sag' between these 'fixed' points
            const currentBody = new CANNON.Body({ mass: bodyMass });
            const boxShape = new CANNON.Box(new CANNON.Vec3(cable.diameter, physicsBoxLength , cable.diameter));
            currentBody.addShape(boxShape);
            currentBody.position.set(segment[0], segment[1], segment[2]);
            currentBody.linearDamping = linearDamping;
            currentBody.angularDamping = angularDamping;
            currentBody.fixedRotation = true;

            if(index === numSegments - 1){
                // If this is the last segment, flip upside down so previous body connects to bottom naturally
                const axis = new CANNON.Vec3(1,0,0);
                const angle = 1.5708 * 2;
                currentBody.quaternion.setFromAxisAngle(axis, angle);
            };
            if(index !== 0){
                // If this is not the first segment, create a single end-to-end constraint with previous body
                const c1 = new CANNON.PointToPointConstraint(
                    currentBody,
                    new CANNON.Vec3(0, physicsBoxLength + segmentOverlap, 0),
                    previousBody,
                    new CANNON.Vec3(0, -physicsBoxLength - segmentOverlap , 0),
                );
                physicsWorld.addConstraint(c1);
            };
            previousBody = currentBody;
            physicsWorld.add(currentBody);
            bodies.push(currentBody);
        });
    });
};

Finally, I have experimented with the following settings without much luck:

  1. Modifying the mass of each segment
  2. Changing the gravity of the world
  3. Changing angularDamping and linearDamping
  4. Adding padding between the physics bodies
  5. Changing solver iterations

I appreciate any assistance!

0

There are 0 best solutions below