Skip to content

Commit

Permalink
docs(joints): add joint information to readme (#224)
Browse files Browse the repository at this point in the history
* docs(joints): add joint information to readme

* chore(changeset): add changeset
  • Loading branch information
wiledal authored Dec 30, 2022
1 parent 2460c76 commit 09cbbde
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 36 deletions.
5 changes: 5 additions & 0 deletions .changeset/rare-oranges-repeat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@react-three/rapier": patch
---

Add joints to readme
5 changes: 5 additions & 0 deletions .changeset/shaggy-numbers-hammer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@react-three/rapier": patch
---

useFixedJoint now allows `w` to be set for the local space orientation
154 changes: 151 additions & 3 deletions packages/react-three-rapier/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,17 @@
<a href="https://discord.gg/ZZjjNvJ"><img src="https://img.shields.io/discord/740090768164651008?style=for-the-badge&colorA=0099DA&colorB=ffffff&label=discord&logo=discord&logoColor=ffffff" /></a>
</p>

<p align="center">⚠️ Under heavy development. All APIs are subject to change. ⚠️</p>
<p align="center">⚠️ Under heavy development. All APIs are subject to change. ⚠️
<br />
For contributions, please read the <a href="https://github.com/pmndrs/react-three-rapier/blob/main/packages/react-three-rapier/CONTRIBUTING.md">Contribution Guide</a>.
</p>

---

`react-three/rapier` (or `r3/rapier`) is a wrapper library around the Rapier (https://rapier.rs/docs/user_guides/javascript) WASM-based physics engine, designed to slot seamlessly into a `react-three/fiber` pipeline.

The goal of this library to is to provide a fast physics engine with minimal friction and small, straight forward API.

For contributions, please read the [contributing guide](https://github.com/pmndrs/react-three-rapier/blob/main/packages/react-three-rapier/CONTRIBUTING.md).

## Basic Usage

Expand Down Expand Up @@ -58,6 +66,11 @@ const App = () => {
- [Configuring Time Step Size](#configuring-time-step-size)
- [Manual stepping](#manual-stepping)
- [Joints](#joints)
- [Fixed Joint](#fixed-joint)
- [Spherical Joint](#spherical-joint)
- [Revolute Joint](#revolute-joint)
- [Prismatic Joint](#prismatic-joint)
- [Joint APIs](#joint-apis)
- [Joints Example](#joints-example)

---
Expand Down Expand Up @@ -500,8 +513,143 @@ step(1 / 60);
```

## Joints
Joints can be made between two `RigidBodies` to provide a way to restrict a motion of a body in relation to another.
> Read more about joints in Rapier: https://rapier.rs/docs/user_guides/javascript/joints
Joints are available in `r3/rapier` as hooks.

There are 4 different joint types available:
- Fixed (two bodies are fixed together)
- Spherical (two bodies are connected by a ball and socket, for things like arms or chains)
- Revolute (two bodies are connected by a hinge, for things like doors or wheels)
- Prismatic (two bodies are connected by a sliding joint, for things like pistons or sliders)

### Fixed Joint
A fixed joint ensures that two rigid-bodies don't move relative to each other. Fixed joints are characterized by one local frame (represented by an isometry) on each rigid-body. The fixed-joint makes these frames coincide in world-space.

```tsx
const JointedThing = () => {
const joint = useFixedJoint(
bodyA,
bodyB,
[
[0, 0, 0], // Position of the joint in bodyA's local space
[0, 0, 0, 1], // Orientation of the joint in bodyA's local space
[0, 0, 0], // Position of the joint in bodyB's local space
[0, 0, 0, 1], // Orientation of the joint in bodyB's local space
]);

return (
<group>
<RigidBody ref={bodyA}>
<mesh />
</RigidBody>
<RigidBody ref={bodyB}>
<mesh />
</RigidBody>
</group>
);
}
```

### Spherical Joint
The spherical joint ensures that two points on the local-spaces of two rigid-bodies always coincide (it prevents any relative translational motion at this points).

```tsx
const JointedThing = () => {
const joint = useSphericalJoint(
bodyA,
bodyB,
[
[0, 0, 0], // Position of the joint in bodyA's local space
[0, 0, 0], // Position of the joint in bodyB's local space
]);

return (
<group>
<RigidBody ref={bodyA}>
<mesh />
</RigidBody>
<RigidBody ref={bodyB}>
<mesh />
</RigidBody>
</group>
);
}
```

### Revolute Joint
The revolute joint prevents any relative movement between two rigid-bodies, except for relative rotations along one axis. This is typically used to simulate wheels, fans, etc.

```tsx
const JointedThing = () => {
const joint = useRevoluteJoint(
bodyA,
bodyB,
[
[0, 0, 0], // Position of the joint in bodyA's local space
[0, 0, 0], // Position of the joint in bodyB's local space
[0, 0, 0], // Axis of the joint, expressed in the local-space of the rigid-bodies it is attached to.
]);

return (
<group>
<RigidBody ref={bodyA}>
<mesh />
</RigidBody>
<RigidBody ref={bodyB}>
<mesh />
</RigidBody>
</group>
);
}
```

### Prismatic Joint
The prismatic joint prevents any relative movement between two rigid-bodies, except for relative translations along one axis.

```tsx
const JointedThing = () => {
const joint = usePrismaticJoint(
bodyA,
bodyB,
[
[0, 0, 0], // Position of the joint in bodyA's local space
[0, 0, 0], // Position of the joint in bodyB's local space
[0, 0, 0], // Axis of the joint, expressed in the local-space of the rigid-bodies it is attached to.
]);

return (
<group>
<RigidBody ref={bodyA}>
<mesh />
</RigidBody>
<RigidBody ref={bodyB}>
<mesh />
</RigidBody>
</group>
);
}
```

### Joint APIs
Joints can be controlled imperatively similarily to how `RigidBody` components can be controlled.

```tsx
const JointedThing = () => {
const joint = useSphericalJoint(...)

useEffect(() => {
joint.configureMotorVelocity(1, 0)

// Disable contacts between the two joint bodies
joint.raw().setContactsEnabled(false)
}, [])

return ...
}
```

- WIP

### Joints Example
<a href="https://codesandbox.io/s/react-three-rapier-joints-mhhbd4"><img src="https://raw.githubusercontent.com/pmndrs/react-three-rapier/HEAD/packages/react-three-rapier/misc/example-joints.jpg" width="240" /></a>
48 changes: 20 additions & 28 deletions packages/react-three-rapier/src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import {

import { ColliderProps, RigidBodyProps } from ".";
import { createJointApi, createRigidBodyApi } from "./api";
import { vectorArrayToVector3 } from "./utils";
import { tupleToObject, vectorArrayToVector3 } from "./utils";
import { createColliderPropsFromChildren } from "./utils-collider";
import {
createRigidBodyState,
Expand Down Expand Up @@ -202,9 +202,9 @@ export const useFixedJoint: UseImpulseJoint<FixedJointParams> = (
body2,
rapier.JointData.fixed(
vectorArrayToVector3(body1Anchor),
{ ...vectorArrayToVector3(body1LocalFrame), w: 1 },
tupleToObject(body1LocalFrame, ["x", "y", "z", "w"] as const),
vectorArrayToVector3(body2Anchor),
{ ...vectorArrayToVector3(body2LocalFrame), w: 1 }
tupleToObject(body2LocalFrame, ["x", "y", "z", "w"] as const)
)
);
};
Expand Down Expand Up @@ -245,19 +245,15 @@ export const useRevoluteJoint: UseImpulseJoint<RevoluteJointParams> = (
const { rapier } = useRapier();

const params = rapier.JointData.revolute(
vectorArrayToVector3(body1Anchor),
vectorArrayToVector3(body2Anchor),
vectorArrayToVector3(axis)
)
if (limits){
params.limitsEnabled = true
params.limits = limits
}
return useImpulseJoint<RevoluteImpulseJoint>(
body1,
body2,
params
vectorArrayToVector3(body1Anchor),
vectorArrayToVector3(body2Anchor),
vectorArrayToVector3(axis)
);
if (limits) {
params.limitsEnabled = true;
params.limits = limits;
}
return useImpulseJoint<RevoluteImpulseJoint>(body1, body2, params);
};

/**
Expand All @@ -268,21 +264,17 @@ export const useRevoluteJoint: UseImpulseJoint<RevoluteJointParams> = (
export const usePrismaticJoint: UseImpulseJoint<PrismaticJointParams> = (
body1,
body2,
[body1Anchor, body2Anchor, axis, limits],
[body1Anchor, body2Anchor, axis, limits]
) => {
const { rapier } = useRapier();
const params = rapier.JointData.prismatic(
vectorArrayToVector3(body1Anchor),
vectorArrayToVector3(body2Anchor),
vectorArrayToVector3(axis)
)
if (limits){
params.limitsEnabled = true
params.limits = limits
}
return useImpulseJoint<PrismaticImpulseJoint>(
body1,
body2,
params
vectorArrayToVector3(body1Anchor),
vectorArrayToVector3(body2Anchor),
vectorArrayToVector3(axis)
);
if (limits) {
params.limitsEnabled = true;
params.limits = limits;
}
return useImpulseJoint<PrismaticImpulseJoint>(body1, body2, params);
};
9 changes: 5 additions & 4 deletions packages/react-three-rapier/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ export type ColliderShape =
| "roundConvexMesh";

export type Vector3Array = [x: number, y: number, z: number];
export type Vector4Array = [x: number, y: number, z: number, w: number];
export type Boolean3Array = [x: boolean, y: boolean, z: boolean];

export interface UseColliderOptions<ColliderArgs extends Array<unknown>> {
Expand Down Expand Up @@ -431,24 +432,24 @@ export type SphericalJointParams = [

export type FixedJointParams = [
body1Anchor: Vector3Array,
body1LocalFrame: Vector3Array,
body1LocalFrame: Vector4Array,
body2Anchor: Vector3Array,
body2LocalFrame: Vector3Array
body2LocalFrame: Vector4Array
];

export type PrismaticJointParams = [
body1Anchor: Vector3Array,
body1LocalFrame: Vector3Array,
body2Anchor: Vector3Array,
body2LocalFrame: Vector3Array,
limits?: [min: number, max: number]
limits?: [min: number, max: number]
];

export type RevoluteJointParams = [
body1Anchor: Vector3Array,
body2Anchor: Vector3Array,
axis: Vector3Array,
limits?: [min: number, max: number]
limits?: [min: number, max: number]
];

export type RigidBodyApiRef = MutableRefObject<undefined | null | RigidBodyApi>;
Expand Down
20 changes: 19 additions & 1 deletion packages/react-three-rapier/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
} from "@dimforge/rapier3d-compat";
import { useRef } from "react";

import { Euler, Quaternion, Vector3 } from "three";
import { Euler, Quaternion, Shape, Vector3 } from "three";
import { _euler, _quaternion, _vector3 } from "./shared-objects";
import { RigidBodyTypeString, Vector3Array } from "./types";

Expand All @@ -13,6 +13,24 @@ export const vectorArrayToVector3 = (arr: Vector3Array) => {
return new Vector3(x, y, z);
};

export const tupleToObject = <
T extends readonly any[],
K extends readonly string[]
>(
tuple: T,
keys: K
) => {
return keys.reduce(
(obj, key, i) => {
obj[key as K[number]] = tuple[i];
return obj;
},
{} as {
[Key in K[number]]: T[number];
}
);
};

export const vector3ToQuaternion = (v: Vector3) => {
return _quaternion.setFromEuler(_euler.setFromVector3(v));
};
Expand Down

0 comments on commit 09cbbde

Please sign in to comment.