Rapier.js

repository·master·Indexed 20 days ago

https://github.com/dimforge/rapier.js

A high-performance 2D and 3D physics engine written in Rust and compiled to WebAssembly for JavaScript/TypeScript environments. It provides comprehensive modules for dynamics (rigid bodies, impulse and multibody joints), geometry (collision detection, shapes, raycasting), and specialized controllers including character, PID, and 3D ray cast vehicle controllers. Available in standard, SIMD-optimized, deterministic, and compatibility builds via NPM.

Tokens
11.4K
Snippets
40
Records
49
Agent score
68%

What's inside rapier.js

  1. Prepare build folders using prepare_builds

    master

    The prepare_builds package is used to create a folder structure ready for compilation by taking specific parameters. It is a workspace member that can be executed via cargo or through provided shell scripts.

    # Using cargo to run the package with specific flags
    cargo run -p prepare_builds -- -d dim2 -f simd
    
    # Using provided shell scripts to prepare and build all projects
    ./builds/prepare_builds/prepare_all_projects.sh && ./builds/prepare_builds/build_all_projects.sh
  2. Use compatibility builds for bundler support

    master

    If your bundler has difficulty handling the .wasm files included in the standard packages, you can use the -compat versions. These versions embed the .wasm file directly into the .js source files using base64 encoding.

    Note: This increases the package size but provides much wider compatibility with various bundlers.

    Append `-compat` to your chosen build:
    - `@dimforge/rapier2d-compat`
    - `@dimforge/rapier2d-simd-compat`
    - `@dimforge/rapier2d-deterministic-compat`
    - `@dimforge/rapier3d-compat`
    - `@dimforge/rapier3d-simd-compat`
    - `@dimforge/rapier3d-deterministic-compat`
  3. Implement custom physics hooks in JavaScript

    master

    You can customize how Rapier handles collisions and intersections by providing custom callback functions for filter_contact_pair and filter_intersection_pair. These hooks are passed to the physics engine to decide whether certain interactions should be ignored or how they should be flagged.

    filter_intersection_pair

    This hook is used to determine if two colliders should trigger an intersection event.

    • Arguments: Receives the handles for collider1 and collider2, and optionally the handles for rigid_body1 and rigid_body2 (if the colliders are attached to bodies).
    • Return Value: A boolean. Return true to allow the intersection, or false to ignore it.

    filter_contact_pair

    This hook is used to modify the solver flags for a contact pair, allowing you to change how the physics engine resolves the collision.

    • Arguments: Receives the handles for collider1 and collider2, and optionally the handles for rigid_body1 and rigid_body2.
    • Return Value: A number representing SolverFlags. This value is treated as a bitmask to set specific solver behaviors.

    Note: The modify_solver_contacts interface is currently a placeholder in the JS bindings and does not yet support full contact modification.

    // Example of what the JS callback signatures look like conceptually:
    
    // filter_intersection_pair(colliderHandle1, colliderHandle2, rbHandle1?, rbHandle2?): boolean
    const myIntersectionFilter = (c1, c2, rb1, rb2) => {
      return c1 !== c2; // Simple example logic
    };
    
    // filter_contact_pair(colliderHandle1, colliderHandle2, rbHandle1?, rbHandle2?): number (SolverFlags)
    const myContactFilter = (c1, c2, rb1, rb2) => {
      return 0; // Return SolverFlags bits
    };
  4. Select the appropriate Rapier NPM package

    master

    Rapier provides several specialized NPM packages for 2D and 3D physics simulation. Choose the one that best fits your performance, browser support, and determinism requirements:

    Standard Builds

    • @dimforge/rapier2d or @dimforge/rapier3d: The main builds for 2D or 3D physics. They offer high performance and wide browser support. Note that these do not guarantee cross-platform determinism, though they are locally deterministic on the same machine.

    SIMD Optimized Builds

    • @dimforge/rapier2d-simd or @dimforge/rapier3d-simd: These builds include internal SIMD optimizations for increased performance. They require a browser with simd128 support.

    Deterministic Builds

    • @dimforge/rapier2d-deterministic or @dimforge/rapier3d-deterministic: These builds prioritize cross-platform deterministic execution of the physics simulation. They are less optimized than the standard or SIMD builds.
  5. Use RawEventQueue to collect physics events

    master

    The RawEventQueue is used to collect and process collision and contact force events generated by the physics engine.

    Initialization

    When creating a new RawEventQueue, you can specify the autoDrain parameter:

    • autoDrain: true (Recommended): The collector is automatically cleared before each world.step(). This prevents unbounded memory growth.
    • autoDrain: false: You must manually clear the events using .clear() to avoid excessive RAM usage.

    Processing Collision Events

    Use drainCollisionEvents(f) to iterate through collision events. The provided JavaScript function f will be called with three arguments:

    1. handle1 (integer): The handle of the first collider.
    2. handle2 (integer): The handle of the second collider.
    3. started (boolean): true if the collision started, false if it stopped.

    Processing Contact Force Events

    Use drainContactForceEvents(f) to iterate through contact force events. The provided JavaScript function f will be called with a RawContactForceEvent object as its single argument.

    // Example setup
    const eventQueue = new RawEventQueue(true);
    
    // In your physics loop
    world.step(eventQueue.collector);
    
    // Handle collisions
    eventQueue.drainCollisionEvents((handle1, handle2, started) => {
      if (started) {
        console.log(`Collision started between ${handle1} and ${handle2}`);
      } else {
        console.log(`Collision stopped between ${handle1} and ${handle2}`);
      }
    });
    
    // Handle contact forces
    eventQueue.drainContactForceEvents((event) => {
      console.log(`Force magnitude: ${event.total_force_magnitude()}`);
    });
  6. Create a Fixed joint

    master

    A fixed joint removes all degrees of freedom between the affected bodies, effectively locking them together in a specific relative orientation and position.

    Requires anchor vectors and axes (rotation) for both bodies.

    const joint = RawGenericJoint.fixed(
      anchor1, 
      axes1, 
      anchor2, 
      axes2
    );
  7. Convert Index to FlatHandle

    master

    The flat_handle function converts a Rapier Index into a FlatHandle (f64). This is typically used when preparing handles to be sent to JavaScript, as it packs the index and generation into a single 64-bit float.

    flat_handle(id: Index) -> FlatHandle

    // Example of flattening an Index for JS interoperability
    let index: Index = ...;
    let flat_id = flat_handle(index);
  8. Iterate over joint handles in RawMultibodyJointSet

    master

    You can iterate over joint handles using callback functions passed from JavaScript. This is useful for performing batch operations or searching for specific joints.

    • forEachJointHandle(f): Applies the provided JavaScript function f to the FlatHandle of every joint managed by the set. The function f is called with one argument: the integer handle.
    • forEachJointAttachedToRigidBody(body, f): Applies the provided JavaScript function f to the FlatHandle of every joint attached to a specific rigid body. The body parameter is the FlatHandle of the rigid body.
    // Iterate over all joints in the set
    jointSet.forEachJointHandle((handle: number) => {
        console.log("Found joint handle:", handle);
    });
    
    // Iterate over joints attached to a specific body
    const bodyHandle = 12345; // Example handle
    jointSet.forEachJointAttachedToRigidBody(bodyHandle, (handle: number) => {
        console.log("Found joint attached to body:", handle);
    });
  9. Query contact and intersection pairs with RawNarrowPhase

    master

    The RawNarrowPhase object allows you to query physical interactions between colliders. You can iterate through all contact pairs involving a specific collider using a callback, or check for specific intersections and contact pairs between two colliders.

    • contact_pairs_with(handle1, callback): Executes a provided JavaScript function for every collider that is in contact with handle1. The callback receives the FlatHandle of the other collider.
    • intersection_pairs_with(handle1, callback): Executes a provided JavaScript function for every collider that intersects with handle1. The callback receives the FlatHandle of the other collider.
    • contact_pair(handle1, handle2): Returns a RawContactPair if the two specified colliders are in contact, otherwise returns null.
    • intersection_pair(handle1, handle2): Returns a boolean indicating if the two specified colliders intersect.
    // Example: Iterating through all colliders in contact with a specific handle
    rawNarrowPhase.contact_pairs_with(myColliderHandle, (otherHandle) => {
      console.log("In contact with:", otherHandle);
    });
    
    // Example: Checking if two specific colliders intersect
    const isIntersecting = rawNarrowPhase.intersection_pair(handleA, handleB);
  10. Create a Spring or Rope joint

    master

    Rapier provides specialized builders for spring and rope behaviors via RawGenericJoint:

    • Spring: Uses rest_length, stiffness, and damping to simulate a spring connection between two anchors.
    • Rope: Uses length to constrain the distance between two anchors.
    // Spring joint
    const spring = RawGenericJoint.spring(restLength, stiffness, damping, anchor1, anchor2);
    
    // Rope joint
    const rope = RawGenericJoint.rope(length, anchor1, anchor2);