Open floppyhammer opened 1 year ago
By the way, do we have SpringJoint (or something similar) in bevy_xpbd?
In the case of a DistanceJoint (the green line), the blue ball will move towards the red one. A so-called SpringJoint will try to keep the blue ball in its rest position (but will deform like a spring depending on the exerted forces).
I use DistanceJoint to connect hair joints. When I move the model's root entity, the hair will not be affected. But when I change the rotation, it does have effect on the hair.
I'm not 100% sure, but if the strands of hair are children of the body you're moving, I think this is somewhat expected. Changes in the parent's Transform
will propagate to the children, so the positions of the hair strands will change instantly as well. The relative position of the hair and the hair's "roots" (the joints) won't change, so you don't see any effect on the hair.
However, the rotation does have an effect, because the hair will snap to the parent's rotation just like the position. The hair won't be pointing directly down anymore, so it will move due to gravity.
If you instead change the parent's Position
component, does that affect the hair? There might be an inconsistency in how hierarchies are handled differently when changing Transform
vs. Position
. Changing the Position
should currently leave the children unaffected if that's what you want.
In the future I'll try to make this consistent and maybe have some component for controlling how transform changes affect children.
By the way, do we have SpringJoint (or something similar) in bevy_xpbd?
So you want to not allow the blue ball to "swing" around the red ball when the red ball is stationary, and to be kept at some rest distance along one axis (the green line) with a given stiffness?
The PrismaticJoint
is very close to this. It only allows translation along one axis like you described, and you can use joint limits to determine the minimum and maximum rest distance. You can control the compliance (inverse of stiffness), but it currently affects the strength of the joint along all axes, not just the specified axis.
If you want a custom spring joint, it should be pretty easy to add by just copying PrismaticJoint
and changing a few lines. Let me know if this is something you want and I can give more help as needed.
If you instead change the parent's
Position
component, does that affect the hair?
The parent doesn't have Position
component. Only hair joints were added a rigidbody, so I can't really answer this question.
The translation and rotation effect I expected is that if I move the parent, it will affect the child like below (maybe through the component PreviousPosition
and PreviousRotation
):
However, this might be something beyond what a standard physics engine provides. I may need to implement such behaviour with xpbd myself.
If you want a custom spring joint, it should be pretty easy to add by just copying
PrismaticJoint
and changing a few lines. Let me know if this is something you want and I can give more help as needed.
PrismaticJoint
is not really what I wanted. I realize my expression isn't accurate. I wanted something like this
Such a joint won't allow movement in the axis direction but allow rotation along all axes (like hair or clothes).
I managed to get a close effect using custom constraint:
I'll just leave the code here in case someone is interested.
#[derive(Component)]
struct SpringConstraint {
entity1: Entity,
entity2: Entity,
// Relative position from entity2 to entity1.
relative_rest_position: Vector,
lagrange: Scalar,
compliance: Scalar,
}
impl PositionConstraint for SpringConstraint {}
impl XpbdConstraint<2> for SpringConstraint {
fn entities(&self) -> [Entity; 2] {
[self.entity1, self.entity2]
}
fn clear_lagrange_multipliers(&mut self) {
self.lagrange = 0.0;
}
fn solve(&mut self, bodies: [&mut RigidBodyQueryItem; 2], dt: Scalar) {
let [body1, body2] = bodies;
// Local attachment points at the centers of the bodies for simplicity.
let [r1, r2] = [Vector::ZERO, Vector::ZERO];
// Compute the positional difference.
let delta_pos = body1.current_position() - body2.current_position();
// The current separation distance.
let length = delta_pos.length();
// The value of the constraint function. When this is zero, the constraint is satisfied.
let c = delta_pos - self.relative_rest_position;
// Avoid division by zero and unnecessary computation.
if length <= 0.0 || c.length() == 0.0 {
return;
}
let n = c.normalize();
// Compute generalized inverse masses (method from PositionConstraint).
let w1 = self.compute_generalized_inverse_mass(body1, r1, n);
let w2 = self.compute_generalized_inverse_mass(body2, r2, n);
let w = [w1, w2];
// Constraint gradients, i.e. how the bodies should be moved
// relative to each other in order to satisfy the constraint.
let gradients = [n, -n];
// Compute Lagrange multiplier update, essentially the signed magnitude of the correction.
let delta_lagrange =
self.compute_lagrange_update(self.lagrange, c.length(), &gradients, &w, self.compliance, dt);
self.lagrange += delta_lagrange;
// Apply positional correction (method from PositionConstraint).
self.apply_positional_correction(body1, body2, delta_lagrange, n, r1, r2);
}
}
Isn't that just the DistanceJoint
? It allows setting a rest distance (and optional min/max distance limits), which is what your custom constraint does.
From my limited experience of using DistanceJoint
, not exactly. This custom constraint keeps the body at its rest position (relative), while a DistanceJoint
only limits movement along the joint direction.
Initial setup
How a DistanceJoint behaves (with gravity)
How this custom joint behaves (with gravity)
I use
DistanceJoint
to connect hair joints. When I move the model's root entity, the hair will not be affected. But when I change the rotation, it does have effect on the hair.In addition, if I change the rotation too quickly, the game becomes super laggy.