Package Exports
- use-ammojs
- use-ammojs/dist/index.js
- use-ammojs/dist/use-ammojs.esm.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (use-ammojs) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
use-ammojs
Fast Physics hooks for use with react-three-fiber.
Achieved by running the ammo.js physics library in a web-worker. Ammo itself is a WebAssembly wrapper around the powerful Bullet Physics engine. Data is synced with SharedArrayBuffers having minimal impact on the main thread.
yarn add use-ammojs
npm i use-ammojsBuilt on top of three-ammo and its related work.
Examples
API Demos
Stress Tests
⚠️ Note that the codesandbox examples do not support SharedArrayBuffers due to missing cross-origin isolation and use regular ArrayBuffers as a fallback. Currently the debug-drawer has no ArrayBuffer fallback implemented and will not render anything.
Why not use use-cannon instead?
use-cannon is great and a inspiration for this package, but it is missing features like soft-bodies and lacks performance in scenes with large triangle meshes. ammo.js is a direct wrapper around the powerful Bullet Physics engine, which solves these problems.
At the time of writing however use-cannon is more mature and great for most projects.
Roadmap
Main goals:
- Create a Physics World as a React context and simulate it in a web-worker
- Sync three objects to physics Rigid Bodies
- Add Rigid Body support
- Add Soft Body support
- Volumes/Cloth from Triangle Mesh
- Ropes
- Support textures on Soft Bodies
- Deformables
- Add Constraints between Rigid Bodies
- Add Constraints to Soft Bodies (ability to pin nodes in place or to Rigid Bodies)
- Improve Physics API
- Make all props reactive
- Expose more methods trough the hook (e.g. setPosition/applyImpulse/more...)
- Support collision callbacks
- Add Examples to the documentation
- Set up Benchmarks to compare cannon, ammo with ArrayBuffers and ammo with SharedArrayBuffers
Low priority goals (for unchecked tasks):
- Automatic refresh rate detection and performance throttling (i.e. match the simulation rate to the requestAnimationFrame-rate and throttle performance if simulation steps take too long)
- Add Raycast queries
- One-time (async) ray-tests
- Continuous queries trough a fixed scene component to mitigate worker latency (TODO: check if necessary)
- Use ArrayBuffers as a fallback for missing cross-origin isolation
- Rigid Bodies
- Soft Bodies
- Debug Rendering
- Simulation managment
- Configurable simulation speed
- Expose performance info
- Integrate to @react-three/drei Stats component
- Automatically pause simulation if tab is out of focus or not rendering (as option)
- Improve the automatic shape detection (set shapeType automatically based on the three Mesh type)
- Raycast Vehicle API
- Support for instanced objects
- Support and document manual bundling of the wasm file
- Currently the wasm library is inlined with a base64 string for ease of use. Users who want to save a few bytes can serve it as a seperate file with the
application/wasmContent-Type in their own deployment. There should be a bundle available without the inlined wasm for that use-case.
- Currently the wasm library is inlined with a base64 string for ease of use. Users who want to save a few bytes can serve it as a seperate file with the
Quick Start
1. Wrap your scene in a Physics Provider
import { Physics } from "use-ammojs";
<Physics drawDebug>[...]</Physics>;2.a Make objects physical (Rigid Bodies)
Automatically parse Shape parameters from the three Mesh (courtesy of three-to-ammo):
import { Box } from "@react-three/drei";
import { useRigidBody, ShapeType } from "use-ammojs";
function MyBox() {
const [ref] = useRigidBody(() => ({
mass: 1,
position: [0, 2, 4],
shapeType: ShapeType.BOX,
}));
return (
<Box ref={ref}>
<meshBasicMaterial attach="material" color="red" />
</Box>
);
}or define Collision Shapes manually:
const [playerCapsuleRef] = useRigidBody(() => ({
bodyType: BodyType.DYNAMIC,
shapeType: ShapeType.CAPSULE,
angularFactor: new Vector3(0, 0, 0),
shapeConfig: {
fit: ShapeFit.MANUAL,
halfExtents: new Vector3(0.3, 0.6, 0.3),
},
}));or add collisions to an imported gltf scene:
useRigidBody(
() => ({
shapeType: ShapeType.MESH,
bodyType: BodyType.STATIC,
}),
gltf.scene
);2.a Make objects squishy (Soft Bodies)
const [ref] = useSoftBody(() => ({
type: SoftBodyType.TRIMESH,
}));
return (
<Sphere position={[0, 2, 7]} args={[1, 16, 16]} ref={ref}>
<meshPhysicalMaterial attach="material" color="blue" />
</Sphere>
);2.c Add Constraints
TODO;3.a Raycasts
const { rayTest } = useAmmo();
[...]
const hits = await rayTest({
from: new Vector3(0, 5, 7),
to: new Vector3(0, -1, 7),
multiple: true
})
if (hits.length) {
console.log(hits[0].object.name, hits[0].hitPosition)
}3.b Update Motion State
const [playerRef, api] = useRigidBody(() => ({
bodyType: BodyType.DYNAMIC,
shapeType: ShapeType.CAPSULE,
angularFactor: new Vector3(0, 0, 0),
shapeConfig: {
fit: ShapeFit.MANUAL,
halfExtents: new Vector3(0.3, 0.6, 0.3),
},
}));
function handleRespawn() {
api.setPosition(new Vector3(0, 0, 0));
api.setLinearVelocity(new Vector3(0, 0, 0));
}Documentation
Components
<Physics />Phyiscs Context. Use to wrap all physical objects within the same physics world.
<PhysicsStats />Shows a stats.js panel with physics timing info. Use within a <Physics /> Context
Hooks
const { rayTest } = useAmmo();Utility funcionts available anywhere in the <Physics /> context.
const [ref, api] = useRigidBody();const [ref, api] = useSoftBody();Cross-origin isolation
To use SharedArrayBuffers for better communication between the main-thread and the web-worker-thread, a cross-origin isolated environment is necessary in modern browsers.
This requires sending the following HTTP headers in the response of the main html document (Learn more):
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-originuse-ammojs will fallback to using ArrayBuffers and postMessage() transfers if SharedArrayBuffers are not available. This is not as bad as a full copy on each transfer, but it does not allow the data to be availble on both threads at the same time.
Developing locally using use-ammojs
Setting up react-scripts to work with yarn link using @craco/craco
yarn add @craco/craco --dev- Replace
react-scriptswithcracoin yourpackage.json(see @craco/craco documentation) - Add
craco.config.jsto project root:
const path = require("path");
module.exports = {
webpack: {
configure: (webpackConfig) => {
// Fix that prevents a duplicate react library being imported when using a linked yarn package
webpackConfig.resolve.alias = {
...webpackConfig.resolve.alias,
react: path.resolve("./node_modules/react"),
"@react-three/fiber": path.resolve("./node_modules/@react-three/fiber"),
three: path.resolve("./node_modules/three"),
};
return webpackConfig;
},
},
// Make sure SharedArrayBuffers are available locally
devServer: {
headers: {
"Cross-Origin-Embedder-Policy": "require-corp",
"Cross-Origin-Opener-Policy": "same-origin",
},
},
};- Run
yarn linkin use-ammojs root directory - Run
yarn link use-ammojsin your project's directory - Run
yarn startin use-ammojs to start the development bundler - Build and run your project as usual