JSPM

  • Created
  • Published
  • Downloads 5
  • Score
    100M100P100Q40704F
  • License MIT

Fast physics hooks for use in react-three-fiber. Powered by web-workers and wasm.

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

npm npm npm

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-ammojs

Built 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/wasm Content-Type in their own deployment. There should be a bundle available without the inlined wasm for that use-case.

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-origin

use-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
  1. yarn add @craco/craco --dev
  2. Replace react-scripts with craco in your package.json (see @craco/craco documentation)
  3. Add craco.config.js to 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",
    },
  },
};
  1. Run yarn link in use-ammojs root directory
  2. Run yarn link use-ammojs in your project's directory
  3. Run yarn start in use-ammojs to start the development bundler
  4. Build and run your project as usual