rapier
rapier copied to clipboard
Feature Request: Spring
Hi,
I've been wanting to create an open source simulation of tensegrity structures and I've been wanting to use rapier to do it because its been really fun to use compared to other physics libraries I've tried.
However, one of the things necessary for tensegrity simulation is some form of spring physics. I've noticed that rapier has a SpringModel
but it seems like that's only used for joint motors.
Is supporting springs something that the rapier team is interested in? I'm willing to write the code, but I would still need direction on how you would like it implemented, and how robust you would like it (perhaps like the ForceGenerator
in nphysics to allow for nonlinear springs?). I already have a bare-bones working example trying to quickly hack it in as a joint, but after learning how joints are implemented in rapier it does not seem like it belongs there.
I have already tried implementing a spring externally using body.apply_force
after each physics step, but for some reason it added a bunch of energy to the system over time, and that issue did not happen when implemented the same way but internally as a joint.
The ForceGenerator
in nphysics
wasn’t complete enough to handle non-linear springs properly.
I believe it will be best to model a spring as a joint. My guess is that we could either:
- Create a whole new joint just for that.
- Or modify the
BallJoint
so it can work in "spring mode". This could, for example be achieved by doing the position correction at the velocity level instead of the position level (we already have code for theBallJoint
to handle velocity-level position correction, but whether or not this is enabled depends on a global flag that affects the whole simulation.
I wrote a SpringJoint
implementation and included both the normal and ground constraint versions. I haven't written the wide version of the joint yet, but before I do that I want to ask about a couple implementation details. All the other joints have limits and motors, should the SpringJoint
have those too? If so, what kind of limits and what kind of motors?
Here's my fork with the SpringJoint code in case you want to see: https://github.com/nascheinkman/rapier/tree/spring_joint
I haven't submitted a Pull Request yet because it doesn't yet compile with simd-stable
enabled.
All the other joints have limits and motors, should the SpringJoint have those too? If so, what kind of limits and what kind of motors?
I don’t think that a motor really makes sense for a spring. A limit however sounds useful. That would control the min/max length of the spring.
Here's my fork with the SpringJoint code in case you want to see: https://github.com/nascheinkman/rapier/tree/spring_joint I haven't submitted a Pull Request yet because it doesn't yet compile with simd-stable enabled.
I suggest opening a PR anyways; it doesn’t matter if it doesn’t always compiles yet. This will be easier for me to see the diffs. You can set the PR as "work-in-progress" as long as you don’t think it is ready to be merged.
I've added distance limits to the spring that seem to work pretty well and I fixed a few issues I found with my initial implementation. I still need to add the wide version, but I think the normal version is ready.
The draft pull request is here: https://github.com/dimforge/rapier/pull/255 I included an example for the spring joint that I've been using for testing and comparison.
The SpringJoint should be ready now here: https://github.com/dimforge/rapier/pull/255 . It includes limits and supports SIMD constraints.
Hey 👋 ,will this be merged in?
Hello, wondering the same :)
@chrislicodes @marwie I wouldn't count on this getting merged at all. The code was written before an overhaul of the engine happened and this code would need a lot of rework to get in now.
I just opened a PR for Rope Joints #415. Planning on working other Joint types that exist in Box2d, but have not been defined in Rapier yet. I'll add Spring Joints to my list.
Thank you @Wolftousen!
Rope joints sound great too
I too would love to see spring joints in rapier, @Wolftousen.
springs are the only thing missing for this to be able to replace box2d for me
Hello, we are using spring joints in soft-body dynamics for the medical field, we would LOVE to see this implemented in the Needle engine!
A while ago I successfully implemented springs externally in Rapier.js, I could send the code here if it would help anyone for the time being
How would this Spring Joint be implemented? Does it have some parameters for the simulation of the spring, eg. Stiffness and Damping?
@aMySour I am interested in ur implementation. @sebcrozet What are your thoughts on this after the big rewrite, has something changed? How would someone go about implementing this?
@aMySour I am interested in ur implementation.
Here's my spring in TypeScript (Rapier.js):
applySpringForce(spring: SimuloSpringDesc) {
const pointAWorld = this.getWorldPoint(spring.getBodyAPosition(), spring.getBodyARotation(), spring.localAnchorA);
const pointBWorld = this.getWorldPoint(spring.getBodyBPosition(), spring.getBodyBRotation(), spring.localAnchorB);
const velA = spring.getBodyAVelocity();
const velB = spring.getBodyBVelocity();
const springVector = this.sub(pointBWorld, pointAWorld);
const distance = this.magnitude(springVector);
if (distance == 0) return;
const direction = this.normalize(springVector);
const u = this.sub(velB, velA);
const rj = this.crossZV(spring.getBodyBAngularVelocity(), spring.localAnchorB);
const ri = this.crossZV(spring.getBodyAAngularVelocity(), spring.localAnchorA);
const tmp = this.add(u, rj, ri);
const f = this.multiply(direction, -spring.stiffness * (distance - spring.targetLength) - spring.damping * this.dot(u, direction));
const forceA = this.multiply(f, -1);
const forceB = f;
spring.applyBodyAImpulse(forceA, pointAWorld);
spring.applyBodyBImpulse(forceB, pointBWorld);
}
I was initially trying to use body.addForceAtPoint
, since intuitively springs add a force on an object, and the spring formula F=-kx has F for force, but I found that applyImpulse
achieves the intended result, while addForce
does not
My SimuloSpringDesc
interface is this:
/** Simulo Rapier creates fake spring joint with `applyImpulseAtPoint` on two bodies.
*
* Since you provide your own functions like `getBodyAPosition`, this is general-purpose, and you can do things like attach one end to a mouse cursor. */
interface SimuloSpringDesc {
bodyA: string | null;
bodyB: string | null;
getBodyAPosition: () => Rapier.Vector2;
getBodyBPosition: () => Rapier.Vector2;
getBodyARotation: () => number;
getBodyBRotation: () => number;
getBodyAVelocity: () => Rapier.Vector2;
getBodyBVelocity: () => Rapier.Vector2;
getBodyAAngularVelocity: () => number;
getBodyBAngularVelocity: () => number;
applyBodyAImpulse: (impulse: Rapier.Vector2, worldPoint: Rapier.Vector2) => void;
applyBodyBImpulse: (impulse: Rapier.Vector2, worldPoint: Rapier.Vector2) => void;
localAnchorA: Rapier.Vector2;
localAnchorB: Rapier.Vector2;
/** Multiplier of spring impulse */
stiffness: number;
/** Dampens spring, stopping it from infinitely oscillating */
damping: number;
/** Target length is also known as rest length. We chose to call it target length because it's more descriptive of how it's used. */
targetLength: number;
}
Now you can simply run applySpringForce
every physics step. In my game, I just used an Object of springs (springs: { [id: string]: SimuloSpringDesc }
) and applied forces for all values in those.
If anyone wants to see a live demo, at simulo.org/client
I deployed a basic prototype of this, and when you drag objects around with your mouse you'll see the springs in action