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'.
Detail
I have a simple method that creates a cable, provided the following parameters:
- Length: The overall length of the cable
- Segment Count: How many segments make up the overall length of the cable
- Diameter: Diameter of each cable segment
My physics world is setup as follows:
- Y Gravity: -30
- quatNormalizeSkip: 0 (using a value greater than 0 makes the chain bounce and explode even worse)
- 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:
- Modifying the mass of each segment
- Changing the gravity of the world
- Changing angularDamping and linearDamping
- Adding padding between the physics bodies
- Changing solver iterations
I appreciate any assistance!