Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a physics joint component #2095

Merged
merged 15 commits into from
Mar 23, 2021
Merged

Add a physics joint component #2095

merged 15 commits into from
Mar 23, 2021

Conversation

willeastcott
Copy link
Contributor

@willeastcott willeastcott commented May 24, 2020

Joint Component

This PR add support for physics joints (AKA constraints):

joint

You can even enable spring 'motors' to power constraints:

wriggle

Internally, it uses the ammo.js btGeneric6DofSpringConstraint class to implement a generic 6 degrees of freedom joint.

API Overview

Here is the new API (it will be private initially until it has had further testing):

API Type Description
pc.JointComponent#angularDampingX number Spring damping of angular X
pc.JointComponent#angularDampingY number Spring damping of angular Y
pc.JointComponent#angularDampingZ number Spring damping of angular Z
pc.JointComponent#angularEquilibriumX number Spring equilibrium of angular X. Can be -180 to 180 degrees.
pc.JointComponent#angularEquilibriumY number Spring equilibrium of angular Y. Can be -180 to 180 degrees.
pc.JointComponent#angularEquilibriumZ number Spring equilibrium of angular Z. Can be -180 to 180 degrees.
pc.JointComponent#angularLimitsX pc.Vec2 The lower and upper angular limit for the X axis
pc.JointComponent#angularLimitsY pc.Vec2 The lower and upper angular limit for the Y axis
pc.JointComponent#angularLimitsZ pc.Vec2 The lower and upper angular limit for the Z axis
pc.JointComponent#angularMotionX string The allowed movement for angular X. Can be "locked", "limited" or "free". Defaults to "locked".
pc.JointComponent#angularMotionY string The allowed movement for angular Y. Can be "locked", "limited" or "free". Defaults to "locked".
pc.JointComponent#angularMotionZ string The allowed movement for angular Z. Can be "locked", "limited" or "free". Defaults to "locked".
pc.JointComponent#angularSpringX boolean Enables a spring motor for angular X
pc.JointComponent#angularSpringY boolean Enables a spring motor for angular Y
pc.JointComponent#angularSpringZ boolean Enables a spring motor for angular Z
pc.JointComponent#angularStiffnessX number Spring stiffness of angular X
pc.JointComponent#angularStiffnessY number Spring stiffness of angular Y
pc.JointComponent#angularStiffnessZ number Spring stiffness of angular Z
pc.JointComponent#breakForce number The impulse required to break the constraint
pc.JointComponent#enableCollision boolean Are collisions between linked bodies taken into account
pc.JointComponent#entityA pc.Entity The first entity linked by the constraint (must have a rigidbody component
pc.JointComponent#entityB pc.Entity The second entity linked by the constraint (must have a rigidbody component
pc.JointComponent#linearDampingX number Spring damping of linear X
pc.JointComponent#linearDampingY number Spring damping of linear Y
pc.JointComponent#linearDampingZ number Spring damping of linear Z
pc.JointComponent#linearEquilibriumX number Spring equilibrium of linear X
pc.JointComponent#linearEquilibriumY number Spring equilibrium of linear Y
pc.JointComponent#linearEquilibriumZ number Spring equilibrium of linear Z
pc.JointComponent#linearLimitsX pc.Vec2 The lower and upper linear limit for the X axis
pc.JointComponent#linearLimitsY pc.Vec2 The lower and upper linear limit for the Y axis
pc.JointComponent#linearLimitsZ pc.Vec2 The lower and upper linear limit for the Z axis
pc.JointComponent#linearMotionX string The allowed movement for linear X. Can be "locked", "limited" or "free". Defaults to "locked".
pc.JointComponent#linearMotionY string The allowed movement for linear Y. Can be "locked", "limited" or "free". Defaults to "locked".
pc.JointComponent#linearMotionZ string The allowed movement for linear Z. Can be "locked", "limited" or "free". Defaults to "locked".
pc.JointComponent#linearSpringX boolean Enables a spring motor for linear X
pc.JointComponent#linearSpringY boolean Enables a spring motor for linear Y
pc.JointComponent#linearSpringZ boolean Enables a spring motor for linear Z
pc.JointComponent#linearStiffnessX number Spring stiffness of linear X
pc.JointComponent#linearStiffnessY number Spring stiffness of linear Y
pc.JointComponent#linearStiffnessZ number Spring stiffness of linear Z

Code Example:

    entity.addComponent('joint', {
        angularLimitsX: new pc.Vec2(0, 0),
        angularLimitsY: new pc.Vec2(0, 0),
        angularLimitsZ: new pc.Vec2(0, 0),
        breakForce: 100,
        enableCollision: true,
        entityA: entityA,
        entityB: entityB,
        linearLimitsX: new pc.Vec2(0, 0),
        linearLimitsY: new pc.Vec2(0, 0),
        linearLimitsZ: new pc.Vec2(0, 0)
    });

Proposed Editor UI

The following is the proposed UI for the joint component in the Editor. It's prototyped below as a script component.

Note that the spring checkboxes should probably show/hide stiffness and equilibrium attributes of the corresponding DOF.

Open questions

  • At the moment, a joint component would live on its own entity, separate from entityA and entityB. But should a joint component be added to the same entity as entityA? This would mean the entityA property would be implicit. One benefit of that is that you'd require one fewer entity, so your hierarchy would be simpler. But then a system for offsetting/editing the position/orientation of the joint would be needed.

  • Should the joint expose presets for making it easier to configure simple, commonly needed joint types like a hinge?

  • Should this component leverage the other ammo.js constraint types for these presets like:

    • btPoint2PointConstraint
    • btHingeConstraint
    • btConeTwistConstraint

    Or should everything be built using the btGeneric6DofConstraint API? Are the more specialized constraint types more stable and/or more performant? It would probably make the code bulkier to utilize the other ammo.js constraint classes.

  • Should limits be in pairs (so 3 pc.Vec2s for angular limits and 3 for linear)? Or should there be 2 `pc.Vec3's for lower and upper for angular and the same again for linear? Or should they be individual numbers? For now, I've made them pairs (one for each axis) because I think that's easier to visualize when configuring a joint.

Fixes #478

I confirm I have signed the Contributor License Agreement.

@willeastcott willeastcott added enhancement area: physics Physics related issue labels May 24, 2020
@willeastcott willeastcott self-assigned this May 24, 2020
@maxbbb
Copy link
Contributor

maxbbb commented May 25, 2020

Not sure if you were looking for feedback here or on the forum post but a few points.

  • I think that having to explicitly define entity A and entity B is a more clear API, but maybe you can have both by having entity A default to the parent entity of the component if it's not defined? Not sure if this would be too tricky to implement / look weird in the editor.

  • Presets would be very nice

  • I think those should be exposed as there are specific scenarios where one is better than the other, for example in page 39 of the bullet manual it suggests that for ragdolls you should use a combination of btConeTwistConstraint and btHingeConstraint.

  • Personally pairs seems easier to reason about.

@Maksims
Copy link
Contributor

Maksims commented May 26, 2020

  • I think that having to explicitly define entity A and entity B is a more clear API, but maybe you can have both by having entity A default to the parent entity of the component if it's not defined? Not sure if this would be too tricky to implement / look weird in the editor.

Many constraint configurations can have only single body defined, that way constraint will be between world and body. Also (for example), constraint entity, might have a spring model related to it, and it will be undesirable for that entity to be a child of one of related bodies.
It is best to avoid "magical" behaviours.

  • Presets would be very nice

+1. I think best would be if Engine as well as Editor would provide "type" for constraint, with more simple controls for simple ones, like ball joint, or hinge joint, and if developer needs - full on 6dof custom one.

@raytranuk
Copy link
Contributor

  • At the moment, a joint component would live on its own entity, separate from entityA and entityB. But should a joint component be added to the same entity as entityA? This would mean the entityA property would be implicit. One benefit of that is that you'd require one fewer entity, so your hierarchy would be simpler. But then a system for offsetting/editing the position/orientation of the joint would be needed.

I think a separate entity per joint is probably best - it also may be clearer if a single rigid (or soft) body has many of joints/constraints?

+1 for presets - and ability to create custom 6 DOF joint - and regarding limits when using a preset, you would want to make specifying limits as intuitive as possible for each type of preset - e.g for btConeTwistConstraint cone direction and cone angle etc would be easier for the user?

@willeastcott willeastcott marked this pull request as ready for review March 21, 2021 01:25
@willeastcott willeastcott requested a review from a team March 21, 2021 01:37
@willeastcott
Copy link
Contributor Author

I've done some more work on this PR. I've added spring support for each linear/angular axis. It's pretty cool. See tweet:

https://twitter.com/willeastcott/status/1373391145166897153

@mvaligursky
Copy link
Contributor

When looking over the introduced API with X, Y and Z, I felt exposing those as Vec3 would be nicer. And for the limits where the value is Vec2, I'd prefer Vec3 lower and Vec3 upper. It would also make the inspector more readable I think.

@mvaligursky
Copy link
Contributor

I'd be also great to create a new issue for the follow up work, so that a good comments from this PR are considered for later - like the different ammo constrain types.

Comment on lines +181 to +182
const rot = new Quat();
rot.setFromMat4(pcTransform);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this avoid temporary allocation?

Comment on lines +184 to +185
const ammoVec = new Ammo.btVector3(pos.x, pos.y, pos.z);
const ammoQuat = new Ammo.btQuaternion(rot.x, rot.y, rot.z, rot.w);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

even these could be static allocations on the class maybe

const aly = this._angularLimitsY;
const alz = this._angularLimitsZ;

const limits = new Ammo.btVector3(alx.x, aly.x, alz.x);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avoid the allocation? few more times in this file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's surprisingly not easy to do that actually. Because Ammo may or may not be loaded. >99% of PlayCanvas apps will not even use the joint component, so doing any static allocations is perhaps more to be avoided here.

Copy link
Contributor

@mvaligursky mvaligursky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approving with some comments and suggestions

@willeastcott
Copy link
Contributor Author

When looking over the introduced API with X, Y and Z, I felt exposing those as Vec3 would be nicer. And for the limits where the value is Vec2, I'd prefer Vec3 lower and Vec3 upper. It would also make the inspector more readable I think.

Thanks for the review, @mvaligursky. So it's tricky. Because often, you just want to enable 1, or maybe 2, degrees of freedom. The current API lets you consider each DoF in turn. And imagine what the Editor UI would look like. For each DoF, there is a boolean to enable the spring. If that's unchecked (the default for each DoF), damping, stiffness, equilibrium can all be hidden. These properties need to be grouped together for each DoF if that's going to work well.

@mvaligursky
Copy link
Contributor

Because often, you just want to enable 1, or maybe 2, degrees of freedom

Hey now that makes sense .. I didn't realize that. I take back my suggestion then.

@raytranuk
Copy link
Contributor

raytranuk commented Mar 23, 2021

Some comments: Would using alternative naming help with understanding what each axis constrains? For example roll or twist for the Z axis and maybe pitch and yaw for Y and X axis. Also, although grouping limits into Vec2 in fine for the underlying implementation - expressing that the first value is the lower limit and second value is the upper limit could be useful in the naming in the API - or at least in the editor inspector? Also, maybe beyond the scope of this PR, what are the plans for in-editor visualization of joint setup?

@willeastcott
Copy link
Contributor Author

@raytranuk We don't use roll, pitch and yaw anywhere else in the API - and Bullet don't use those terms in their public API either.

In the Editor, we can have labels lower and upper in the Vec2 attribute in the UI. Unfortunately, we can't easily have different vector representations in the UI vs the runtime because the tooltip for the UI form should be the API equivalent.

For in-editor visualization, we will need a joint gizmo. Scary job, but yes, we'll need that at some point, once the runtime side is nailed down.

@raytranuk
Copy link
Contributor

@raytranuk We don't use roll, pitch and yaw anywhere else in the API - and Bullet don't use those terms in their public API either.

It does make sense to stay consistent - so that knowledge gained from working with other places that use rotations in the engine will apply here - good tool tips in editor (and eventually good editor gizmos) will be helpful too.

In the Editor, we can have labels lower and upper in the Vec2 attribute in the UI. Unfortunately, we can't easily have different vector representations in the UI vs the runtime because the tooltip for the UI form should be the API equivalent.

It maybe worth considering exposing accessors for lower and upper limits to be set individually - if we feel users would find that useful - but it would mainly be a convenience.

For in-editor visualization, we will need a joint gizmo. Scary job, but yes, we'll need that at some point, once the runtime side is nailed down.

Agree.

Copy link
Contributor

@raytranuk raytranuk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approving

@willeastcott willeastcott merged commit e43ebd6 into master Mar 23, 2021
@willeastcott willeastcott deleted the physics-joints branch March 23, 2021 17:04
@jbromberg
Copy link
Contributor

Is this the recommended way of doing constraints today? I noticed it's not in the public API docs but is in the engine.

Alternatively, there are the constraint scripts written here: https://playcanvas.com/project/618829/overview/physics-constraints

For reference, I'm trying to build a ragdoll.

@LeXXik
Copy link
Contributor

LeXXik commented Dec 14, 2022

It is not recommended currently. A joint must be destroyed before any of the attached rigidbodies are destroyed. At the moment rigidbody component does not consider, if there is a constraint attached to it on body destruction, which will crash Ammo.

@i12345
Copy link

i12345 commented Dec 9, 2023

I have modified a fork of playcanvas [here] to implement multiple joint types using native bullet/ammo implementations (6DOF, spherical/cone-twist, hinge/revolute, slider/prismatic, and fixed). In addition, this version of playcanvas supports bullet multibody articulations. I do not have an example, though multibodies are supposed to be much more stable (than the current rigid bodies) for simulating articulations with many joints.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: physics Physics related issue
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add a JointComponent
8 participants