@react-three/rapier 0.6.8 → 0.6.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,13 +4,22 @@ declare type UseColliderOptionsRequiredArgs<T> = Omit<UseColliderOptions<T>, "ar
4
4
  args: T;
5
5
  children?: ReactNode;
6
6
  };
7
- export declare const CuboidCollider: (props: UseColliderOptionsRequiredArgs<CuboidArgs>) => JSX.Element;
8
- export declare const RoundCuboidCollider: (props: UseColliderOptionsRequiredArgs<RoundCuboidArgs>) => JSX.Element;
9
- export declare const BallCollider: (props: UseColliderOptionsRequiredArgs<BallArgs>) => JSX.Element;
10
- export declare const CapsuleCollider: (props: UseColliderOptionsRequiredArgs<CapsuleArgs>) => JSX.Element;
11
- export declare const HeightfieldCollider: (props: UseColliderOptionsRequiredArgs<HeightfieldArgs>) => JSX.Element;
12
- export declare const TrimeshCollider: (props: UseColliderOptionsRequiredArgs<TrimeshArgs>) => JSX.Element;
13
- export declare const ConeCollider: (props: UseColliderOptionsRequiredArgs<ConeArgs>) => JSX.Element;
14
- export declare const CylinderCollider: (props: UseColliderOptionsRequiredArgs<CylinderArgs>) => JSX.Element;
15
- export declare const ConvexHullCollider: (props: UseColliderOptionsRequiredArgs<ConvexHullArgs>) => JSX.Element;
7
+ export declare type CuboidColliderProps = UseColliderOptionsRequiredArgs<CuboidArgs>;
8
+ export declare type RoundCuboidColliderProps = UseColliderOptionsRequiredArgs<RoundCuboidArgs>;
9
+ export declare type BallColliderProps = UseColliderOptionsRequiredArgs<BallArgs>;
10
+ export declare type CapsuleColliderProps = UseColliderOptionsRequiredArgs<CapsuleArgs>;
11
+ export declare type HeightfieldColliderProps = UseColliderOptionsRequiredArgs<HeightfieldArgs>;
12
+ export declare type TrimeshColliderProps = UseColliderOptionsRequiredArgs<TrimeshArgs>;
13
+ export declare type ConeColliderProps = UseColliderOptionsRequiredArgs<ConeArgs>;
14
+ export declare type CylinderColliderProps = UseColliderOptionsRequiredArgs<CylinderArgs>;
15
+ export declare type ConvexHullColliderProps = UseColliderOptionsRequiredArgs<ConvexHullArgs>;
16
+ export declare const CuboidCollider: (props: CuboidColliderProps) => JSX.Element;
17
+ export declare const RoundCuboidCollider: (props: RoundCuboidColliderProps) => JSX.Element;
18
+ export declare const BallCollider: (props: BallColliderProps) => JSX.Element;
19
+ export declare const CapsuleCollider: (props: CapsuleColliderProps) => JSX.Element;
20
+ export declare const HeightfieldCollider: (props: HeightfieldColliderProps) => JSX.Element;
21
+ export declare const TrimeshCollider: (props: TrimeshColliderProps) => JSX.Element;
22
+ export declare const ConeCollider: (props: ConeColliderProps) => JSX.Element;
23
+ export declare const CylinderCollider: (props: CylinderColliderProps) => JSX.Element;
24
+ export declare const ConvexHullCollider: (props: ConvexHullColliderProps) => JSX.Element;
16
25
  export {};
@@ -7,4 +7,4 @@ export interface InstancedRigidBodiesProps extends Omit<RigidBodyProps, "positio
7
7
  rotations?: Vector3Array[];
8
8
  scales?: Vector3Array[];
9
9
  }
10
- export declare const InstancedRigidBodies: React.ForwardRefExoticComponent<Pick<InstancedRigidBodiesProps, "type" | "canSleep" | "linearDamping" | "angularDamping" | "linearVelocity" | "angularVelocity" | "gravityScale" | "ccd" | "colliders" | "friction" | "restitution" | "onSleep" | "onWake" | "lockRotations" | "lockTranslations" | "enabledRotations" | "enabledTranslations" | "children" | "quaternion" | "attach" | "args" | "key" | "onUpdate" | "up" | "scale" | "matrix" | "layers" | "dispose" | "id" | "uuid" | "name" | "parent" | "modelViewMatrix" | "normalMatrix" | "matrixWorld" | "matrixAutoUpdate" | "matrixWorldNeedsUpdate" | "visible" | "castShadow" | "receiveShadow" | "frustumCulled" | "renderOrder" | "animations" | "userData" | "customDepthMaterial" | "customDistanceMaterial" | "isObject3D" | "onBeforeRender" | "onAfterRender" | "applyMatrix4" | "applyQuaternion" | "setRotationFromAxisAngle" | "setRotationFromEuler" | "setRotationFromMatrix" | "setRotationFromQuaternion" | "rotateOnAxis" | "rotateOnWorldAxis" | "rotateX" | "rotateY" | "rotateZ" | "translateOnAxis" | "translateX" | "translateY" | "translateZ" | "localToWorld" | "worldToLocal" | "lookAt" | "add" | "remove" | "removeFromParent" | "clear" | "getObjectById" | "getObjectByName" | "getObjectByProperty" | "getWorldPosition" | "getWorldQuaternion" | "getWorldScale" | "getWorldDirection" | "raycast" | "traverse" | "traverseVisible" | "traverseAncestors" | "updateMatrix" | "updateMatrixWorld" | "updateWorldMatrix" | "toJSON" | "clone" | "copy" | "addEventListener" | "hasEventListener" | "removeEventListener" | "dispatchEvent" | "onClick" | "onContextMenu" | "onDoubleClick" | "onPointerUp" | "onPointerDown" | "onPointerOver" | "onPointerOut" | "onPointerEnter" | "onPointerLeave" | "onPointerMove" | "onPointerMissed" | "onPointerCancel" | "onWheel" | "positions" | "rotations" | "scales"> & React.RefAttributes<InstancedRigidBodyApi>>;
10
+ export declare const InstancedRigidBodies: React.ForwardRefExoticComponent<InstancedRigidBodiesProps & React.RefAttributes<InstancedRigidBodyApi>>;
@@ -1,7 +1,7 @@
1
1
  import React, { FC, ReactNode } from "react";
2
2
  import type Rapier from "@dimforge/rapier3d-compat";
3
- import { RigidBodyAutoCollider, Vector3Array, WorldApi } from "./types";
4
- import { ColliderHandle, RigidBody, RigidBodyHandle, TempContactManifold } from "@dimforge/rapier3d-compat";
3
+ import { CollisionEnterHandler, CollisionExitHandler, RigidBodyAutoCollider, Vector3Array, WorldApi } from "./types";
4
+ import { ColliderHandle, RigidBodyHandle } from "@dimforge/rapier3d-compat";
5
5
  import { Matrix4, Object3D, Vector3 } from "three";
6
6
  export interface RapierContext {
7
7
  rapier: typeof Rapier;
@@ -18,20 +18,15 @@ export interface RapierContext {
18
18
  colliders: RigidBodyAutoCollider;
19
19
  };
20
20
  rigidBodyEvents: EventMap;
21
+ colliderEvents: EventMap;
21
22
  isPaused: boolean;
22
23
  }
23
24
  export declare const RapierContext: React.Context<RapierContext | undefined>;
24
25
  declare type EventMap = Map<ColliderHandle | RigidBodyHandle, {
25
26
  onSleep?(): void;
26
27
  onWake?(): void;
27
- onCollisionEnter?({ target, manifold, flipped, }: {
28
- target: RigidBody;
29
- manifold: TempContactManifold;
30
- flipped: boolean;
31
- }): void;
32
- onCollisionExit?({ target }: {
33
- target: RigidBody;
34
- }): void;
28
+ onCollisionEnter?: CollisionEnterHandler;
29
+ onCollisionExit?: CollisionExitHandler;
35
30
  }>;
36
31
  interface RapierWorldProps {
37
32
  children: ReactNode;
@@ -66,6 +61,12 @@ interface RapierWorldProps {
66
61
  * @defaultValue false
67
62
  */
68
63
  paused?: boolean;
64
+ /**
65
+ * The update priority at which the physics simulation should run.
66
+ *
67
+ * @defaultValue undefined
68
+ */
69
+ updatePriority?: number;
69
70
  }
70
71
  export declare const Physics: FC<RapierWorldProps>;
71
72
  export {};
@@ -1,7 +1,6 @@
1
1
  import React, { MutableRefObject, RefObject } from "react";
2
2
  import { ReactNode } from "react";
3
3
  import { Object3D } from "three";
4
- import { Object3DProps } from "@react-three/fiber";
5
4
  import { InstancedRigidBodyApi } from "./api";
6
5
  import { InstancedRigidBodiesProps } from "./InstancedRigidBodies";
7
6
  import { RigidBodyApi, UseRigidBodyOptions } from "./types";
@@ -17,7 +16,7 @@ export declare const useRigidBodyContext: () => {
17
16
  hasCollisionEvents: boolean;
18
17
  options: UseRigidBodyOptions | InstancedRigidBodiesProps;
19
18
  };
20
- export interface RigidBodyProps extends UseRigidBodyOptions, Omit<Object3DProps, 'type' | 'position' | 'rotation'> {
19
+ export interface RigidBodyProps extends UseRigidBodyOptions {
21
20
  children?: ReactNode;
22
21
  }
23
- export declare const RigidBody: React.ForwardRefExoticComponent<Pick<RigidBodyProps, "type" | "canSleep" | "linearDamping" | "angularDamping" | "linearVelocity" | "angularVelocity" | "gravityScale" | "ccd" | "position" | "rotation" | "colliders" | "friction" | "restitution" | "onCollisionEnter" | "onCollisionExit" | "onSleep" | "onWake" | "lockRotations" | "lockTranslations" | "enabledRotations" | "enabledTranslations" | "children" | "quaternion" | "attach" | "args" | "key" | "onUpdate" | "up" | "scale" | "matrix" | "layers" | "dispose" | "id" | "uuid" | "name" | "parent" | "modelViewMatrix" | "normalMatrix" | "matrixWorld" | "matrixAutoUpdate" | "matrixWorldNeedsUpdate" | "visible" | "castShadow" | "receiveShadow" | "frustumCulled" | "renderOrder" | "animations" | "userData" | "customDepthMaterial" | "customDistanceMaterial" | "isObject3D" | "onBeforeRender" | "onAfterRender" | "applyMatrix4" | "applyQuaternion" | "setRotationFromAxisAngle" | "setRotationFromEuler" | "setRotationFromMatrix" | "setRotationFromQuaternion" | "rotateOnAxis" | "rotateOnWorldAxis" | "rotateX" | "rotateY" | "rotateZ" | "translateOnAxis" | "translateX" | "translateY" | "translateZ" | "localToWorld" | "worldToLocal" | "lookAt" | "add" | "remove" | "removeFromParent" | "clear" | "getObjectById" | "getObjectByName" | "getObjectByProperty" | "getWorldPosition" | "getWorldQuaternion" | "getWorldScale" | "getWorldDirection" | "raycast" | "traverse" | "traverseVisible" | "traverseAncestors" | "updateMatrix" | "updateMatrixWorld" | "updateWorldMatrix" | "toJSON" | "clone" | "copy" | "addEventListener" | "hasEventListener" | "removeEventListener" | "dispatchEvent" | "onClick" | "onContextMenu" | "onDoubleClick" | "onPointerUp" | "onPointerDown" | "onPointerOver" | "onPointerOut" | "onPointerEnter" | "onPointerLeave" | "onPointerMove" | "onPointerMissed" | "onPointerCancel" | "onWheel"> & React.RefAttributes<import("./api").RigidBodyApi>>;
22
+ export declare const RigidBody: React.ForwardRefExoticComponent<RigidBodyProps & React.RefAttributes<import("./api").RigidBodyApi>>;
@@ -0,0 +1,34 @@
1
+ import { InteractionGroups } from "@dimforge/rapier3d-compat";
2
+ /**
3
+ * Calculates an InteractionGroup bitmask for use in the `collisionGroups` or `solverGroups`
4
+ * properties of RigidBody or Collider components. The first argument represents a list of
5
+ * groups the entity is in (expressed as numbers from 0 to 15). The second argument is a list
6
+ * of groups that will be filtered against. When it is omitted, all groups are filtered against.
7
+ *
8
+ * @example
9
+ * A RigidBody that is member of group 0 and will collide with everything from groups 0 and 1:
10
+ *
11
+ * ```tsx
12
+ * <RigidBody collisionGroups={interactionGroups([0], [0, 1])} />
13
+ * ```
14
+ *
15
+ * A RigidBody that is member of groups 0 and 1 and will collide with everything else:
16
+ *
17
+ * ```tsx
18
+ * <RigidBody collisionGroups={interactionGroups([0, 1])} />
19
+ * ```
20
+ *
21
+ * A RigidBody that is member of groups 0 and 1 and will not collide with anything:
22
+ *
23
+ * ```tsx
24
+ * <RigidBody collisionGroups={interactionGroups([0, 1], [])} />
25
+ * ```
26
+ *
27
+ * Please note that Rapier needs interaction filters to evaluate to true between _both_ colliding
28
+ * entities for collision events to trigger.
29
+ *
30
+ * @param memberships Groups the collider is a member of. (Values can range from 0 to 15.)
31
+ * @param filters Groups the interaction group should filter against. (Values can range from 0 to 15.)
32
+ * @returns An InteractionGroup bitmask.
33
+ */
34
+ export declare const interactionGroups: (memberships: number | number[], filters?: number | number[] | undefined) => InteractionGroups;
@@ -1,5 +1,8 @@
1
1
  export * from "./types";
2
2
  export type { RigidBodyApi, WorldApi, InstancedRigidBodyApi } from "./api";
3
+ export type { RigidBodyProps } from './RigidBody';
4
+ export type { InstancedRigidBodiesProps } from './InstancedRigidBodies';
5
+ export type { CylinderColliderProps, BallColliderProps, CapsuleColliderProps, ConeColliderProps, ConvexHullColliderProps, CuboidColliderProps, HeightfieldColliderProps, RoundCuboidColliderProps, TrimeshColliderProps } from './AnyCollider';
3
6
  export { Physics } from "./Physics";
4
7
  export { RigidBody } from "./RigidBody";
5
8
  export { MeshCollider } from "./MeshCollider";
@@ -7,3 +10,4 @@ export { Debug } from "./Debug";
7
10
  export { InstancedRigidBodies } from "./InstancedRigidBodies";
8
11
  export * from "./AnyCollider";
9
12
  export * from "./hooks";
13
+ export * from "./bitmasks";
@@ -1,5 +1,5 @@
1
1
  import { MutableRefObject } from "react";
2
- import { CoefficientCombineRule, Collider as RapierCollider, RigidBody as RapierRigidBody, TempContactManifold } from "@dimforge/rapier3d-compat";
2
+ import { CoefficientCombineRule, Collider as RapierCollider, InteractionGroups, RigidBody as RapierRigidBody, TempContactManifold } from "@dimforge/rapier3d-compat";
3
3
  import { createColliderApi, createJointApi, createRigidBodyApi, createWorldApi } from "./api";
4
4
  export { CoefficientCombineRule as CoefficientCombineRule } from "@dimforge/rapier3d-compat";
5
5
  export { RapierRigidBody, RapierCollider };
@@ -120,7 +120,35 @@ export interface UseColliderOptions<ColliderArgs> {
120
120
  * The rotation of this collider relative to the rigid body
121
121
  */
122
122
  rotation?: Vector3Array;
123
+ /**
124
+ * Callback when this collider collides with another collider.
125
+ */
126
+ onCollisionEnter?: CollisionEnterHandler;
127
+ /**
128
+ * Callback when this collider stops colliding with another collider.
129
+ */
130
+ onCollisionExit?: CollisionExitHandler;
131
+ /**
132
+ * The bit mask configuring the groups and mask for collision handling.
133
+ */
134
+ collisionGroups?: InteractionGroups;
135
+ /**
136
+ * The bit mask configuring the groups and mask for solver handling.
137
+ */
138
+ solverGroups?: InteractionGroups;
123
139
  }
140
+ export declare type CollisionEnterPayload = {
141
+ target: RapierRigidBody;
142
+ collider: RapierCollider;
143
+ manifold: TempContactManifold;
144
+ flipped: boolean;
145
+ };
146
+ export declare type CollisionExitPayload = {
147
+ target: RapierRigidBody;
148
+ collider: RapierCollider;
149
+ };
150
+ export declare type CollisionEnterHandler = (payload: CollisionEnterPayload) => void;
151
+ export declare type CollisionExitHandler = (payload: CollisionExitPayload) => void;
124
152
  export interface UseRigidBodyOptions {
125
153
  /**
126
154
  * Specify the type of this rigid body
@@ -184,17 +212,21 @@ export interface UseRigidBodyOptions {
184
212
  /**
185
213
  * Callback when this rigidbody collides with another rigidbody
186
214
  */
187
- onCollisionEnter?({}: {
188
- target: RapierRigidBody;
189
- manifold: TempContactManifold;
190
- flipped: boolean;
191
- }): void;
215
+ onCollisionEnter?: CollisionEnterHandler;
192
216
  /**
193
217
  * Callback when this rigidbody stops colliding with another rigidbody
194
218
  */
195
- onCollisionExit?({}: {
196
- target: RapierRigidBody;
197
- }): void;
219
+ onCollisionExit?: CollisionExitHandler;
220
+ /**
221
+ * The default collision groups bitmask for all colliders in this rigid body.
222
+ * Can be customized per-collider.
223
+ */
224
+ collisionGroups?: InteractionGroups;
225
+ /**
226
+ * The default solver groups bitmask for all colliders in this rigid body.
227
+ * Can be customized per-collider.
228
+ */
229
+ solverGroups?: InteractionGroups;
198
230
  onSleep?(): void;
199
231
  onWake?(): void;
200
232
  /**
@@ -4,7 +4,7 @@ import { RigidBodyApi, RigidBodyAutoCollider, RigidBodyShape, RigidBodyTypeStrin
4
4
  export declare const vectorArrayToVector3: (arr: Vector3Array) => Vector3;
5
5
  export declare const vector3ToQuaternion: (v: Vector3) => Quaternion;
6
6
  export declare const rapierVector3ToVector3: ({ x, y, z }: RapierVector3) => Vector3;
7
- export declare const rapierQuaternionToQuaternion: ({ x, y, z, w, }: RapierQuaternion) => Quaternion;
7
+ export declare const rapierQuaternionToQuaternion: ({ x, y, z, w }: RapierQuaternion) => Quaternion;
8
8
  export declare const rigidBodyTypeFromString: (type: RigidBodyTypeString) => number;
9
9
  export declare const decomposeMatrix4: (m: Matrix4) => {
10
10
  position: Vector3;
@@ -8,6 +8,7 @@ var useAsset = require('use-asset');
8
8
  var fiber = require('@react-three/fiber');
9
9
  var three = require('three');
10
10
  var BufferGeometryUtils = require('three/examples/jsm/utils/BufferGeometryUtils');
11
+ var MathUtils = require('three/src/math/MathUtils');
11
12
  var threeStdlib = require('three-stdlib');
12
13
 
13
14
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
@@ -49,7 +50,7 @@ const rapierVector3ToVector3 = ({
49
50
  x,
50
51
  y,
51
52
  z
52
- }) => _vector3.set(x, y, z).clone();
53
+ }) => _vector3.set(x, y, z);
53
54
  const rapierQuaternionToQuaternion = ({
54
55
  x,
55
56
  y,
@@ -95,6 +96,12 @@ const scaleColliderArgs = (shape, args, scale) => {
95
96
  const scaleArray = [scale.x, scale.y, scale.z, scale.x, scale.x];
96
97
  return newArgs.map((arg, index) => scaleArray[index] * arg);
97
98
  };
99
+
100
+ const applyColliderOptions = (collider, options) => {
101
+ if (options.collisionGroups !== undefined) collider.setCollisionGroups(options.collisionGroups);
102
+ if (options.solverGroups !== undefined) collider.setSolverGroups(options.solverGroups);
103
+ };
104
+
98
105
  const createColliderFromOptions = ({
99
106
  options,
100
107
  world,
@@ -148,6 +155,7 @@ const createColliderFromOptions = ({
148
155
  }
149
156
 
150
157
  const collider = world.createCollider(colliderDesc, rigidBody);
158
+ applyColliderOptions(collider, options);
151
159
  return collider;
152
160
  };
153
161
 
@@ -213,6 +221,7 @@ const createCollidersFromChildren = ({
213
221
  });
214
222
  const actualRigidBody = rigidBody ? world.getRigidBody(rigidBody.handle) : undefined;
215
223
  const collider = world.createCollider(desc, actualRigidBody);
224
+ applyColliderOptions(collider, options);
216
225
  colliders.push(collider);
217
226
  }
218
227
  });
@@ -450,7 +459,8 @@ const Physics = ({
450
459
  children,
451
460
  timeStep: _timeStep = 1 / 60,
452
461
  maxSubSteps: _maxSubSteps = 10,
453
- paused: _paused = false
462
+ paused: _paused = false,
463
+ updatePriority
454
464
  }) => {
455
465
  const rapier = useAsset.useAsset(importRapier);
456
466
  const [isPaused, setIsPaused] = React.useState(_paused);
@@ -468,6 +478,7 @@ const Physics = ({
468
478
  });
469
479
  const [rigidBodyStates] = React.useState(() => new Map());
470
480
  const [rigidBodyEvents] = React.useState(() => new Map());
481
+ const [colliderEvents] = React.useState(() => new Map());
471
482
  const [eventQueue] = React.useState(() => new rapier3dCompat.EventQueue(false)); // Init world
472
483
 
473
484
  React.useEffect(() => {
@@ -497,12 +508,12 @@ const Physics = ({
497
508
  world.timestep = _timeStep;
498
509
  /**
499
510
  * Fixed timeStep simulation progression
500
- * @see https://gafferongames.com/post/fix_your_timestep/
511
+ * @see https://gafferongames.com/post/fix_your_timestep/
501
512
  */
502
513
 
503
514
  let previousTranslations = {}; // don't step time forwards if paused
504
515
 
505
- const nowTime = steppingState.time += _paused ? 0 : delta * 1000;
516
+ const nowTime = steppingState.time += _paused ? 0 : MathUtils.clamp(delta, 0, 1) * 1000;
506
517
  const timeStepMs = _timeStep * 1000;
507
518
  const timeSinceLast = nowTime - steppingState.lastTime;
508
519
  steppingState.lastTime = nowTime;
@@ -552,7 +563,7 @@ const Physics = ({
552
563
  }
553
564
 
554
565
  let oldState = previousTranslations[rigidBody.handle];
555
- let newTranslation = rapierVector3ToVector3(rigidBody.translation());
566
+ let newTranslation = rigidBody.translation();
556
567
  let newRotation = rapierQuaternionToQuaternion(rigidBody.rotation());
557
568
  let interpolatedTranslation = oldState ? oldState.translation.lerp(newTranslation, 1) : newTranslation;
558
569
  let interpolatedRotation = oldState ? oldState.rotation.slerp(newRotation, interpolationAlpha) : newRotation;
@@ -577,36 +588,65 @@ const Physics = ({
577
588
 
578
589
  const rigidBody1 = world.getRigidBody(rigidBodyHandle1);
579
590
  const rigidBody2 = world.getRigidBody(rigidBodyHandle2);
580
- const events1 = rigidBodyEvents.get(rigidBodyHandle1);
581
- const events2 = rigidBodyEvents.get(rigidBodyHandle2);
591
+ const rigidBody1Events = rigidBodyEvents.get(rigidBodyHandle1);
592
+ const rigidBoyd2Events = rigidBodyEvents.get(rigidBodyHandle2);
593
+ const collider1Events = colliderEvents.get(collider1.handle);
594
+ const collider2Events = colliderEvents.get(collider2.handle);
582
595
 
583
596
  if (started) {
584
597
  world.contactPair(collider1, collider2, (manifold, flipped) => {
585
- var _events1$onCollisionE, _events2$onCollisionE;
598
+ var _rigidBody1Events$onC, _rigidBoyd2Events$onC, _collider1Events$onCo, _collider2Events$onCo;
586
599
 
587
- events1 === null || events1 === void 0 ? void 0 : (_events1$onCollisionE = events1.onCollisionEnter) === null || _events1$onCollisionE === void 0 ? void 0 : _events1$onCollisionE.call(events1, {
600
+ /* RigidBody events */
601
+ rigidBody1Events === null || rigidBody1Events === void 0 ? void 0 : (_rigidBody1Events$onC = rigidBody1Events.onCollisionEnter) === null || _rigidBody1Events$onC === void 0 ? void 0 : _rigidBody1Events$onC.call(rigidBody1Events, {
588
602
  target: rigidBody2,
603
+ collider: collider2,
589
604
  manifold,
590
605
  flipped
591
606
  });
592
- events2 === null || events2 === void 0 ? void 0 : (_events2$onCollisionE = events2.onCollisionEnter) === null || _events2$onCollisionE === void 0 ? void 0 : _events2$onCollisionE.call(events2, {
607
+ rigidBoyd2Events === null || rigidBoyd2Events === void 0 ? void 0 : (_rigidBoyd2Events$onC = rigidBoyd2Events.onCollisionEnter) === null || _rigidBoyd2Events$onC === void 0 ? void 0 : _rigidBoyd2Events$onC.call(rigidBoyd2Events, {
593
608
  target: rigidBody1,
609
+ collider: collider1,
610
+ manifold,
611
+ flipped
612
+ });
613
+ /* Collider events */
614
+
615
+ collider1Events === null || collider1Events === void 0 ? void 0 : (_collider1Events$onCo = collider1Events.onCollisionEnter) === null || _collider1Events$onCo === void 0 ? void 0 : _collider1Events$onCo.call(collider1Events, {
616
+ target: rigidBody2,
617
+ collider: collider2,
618
+ manifold,
619
+ flipped
620
+ });
621
+ collider2Events === null || collider2Events === void 0 ? void 0 : (_collider2Events$onCo = collider2Events.onCollisionEnter) === null || _collider2Events$onCo === void 0 ? void 0 : _collider2Events$onCo.call(collider2Events, {
622
+ target: rigidBody1,
623
+ collider: collider1,
594
624
  manifold,
595
625
  flipped
596
626
  });
597
627
  });
598
628
  } else {
599
- var _events1$onCollisionE2, _events2$onCollisionE2;
629
+ var _rigidBody1Events$onC2, _rigidBoyd2Events$onC2, _collider1Events$onCo2, _collider2Events$onCo2;
600
630
 
601
- events1 === null || events1 === void 0 ? void 0 : (_events1$onCollisionE2 = events1.onCollisionExit) === null || _events1$onCollisionE2 === void 0 ? void 0 : _events1$onCollisionE2.call(events1, {
602
- target: rigidBody2
631
+ rigidBody1Events === null || rigidBody1Events === void 0 ? void 0 : (_rigidBody1Events$onC2 = rigidBody1Events.onCollisionExit) === null || _rigidBody1Events$onC2 === void 0 ? void 0 : _rigidBody1Events$onC2.call(rigidBody1Events, {
632
+ target: rigidBody2,
633
+ collider: collider2
634
+ });
635
+ rigidBoyd2Events === null || rigidBoyd2Events === void 0 ? void 0 : (_rigidBoyd2Events$onC2 = rigidBoyd2Events.onCollisionExit) === null || _rigidBoyd2Events$onC2 === void 0 ? void 0 : _rigidBoyd2Events$onC2.call(rigidBoyd2Events, {
636
+ target: rigidBody1,
637
+ collider: collider1
603
638
  });
604
- events2 === null || events2 === void 0 ? void 0 : (_events2$onCollisionE2 = events2.onCollisionExit) === null || _events2$onCollisionE2 === void 0 ? void 0 : _events2$onCollisionE2.call(events2, {
605
- target: rigidBody1
639
+ collider1Events === null || collider1Events === void 0 ? void 0 : (_collider1Events$onCo2 = collider1Events.onCollisionExit) === null || _collider1Events$onCo2 === void 0 ? void 0 : _collider1Events$onCo2.call(collider1Events, {
640
+ target: rigidBody2,
641
+ collider: collider2
642
+ });
643
+ collider2Events === null || collider2Events === void 0 ? void 0 : (_collider2Events$onCo2 = collider2Events.onCollisionExit) === null || _collider2Events$onCo2 === void 0 ? void 0 : _collider2Events$onCo2.call(collider2Events, {
644
+ target: rigidBody1,
645
+ collider: collider1
606
646
  });
607
647
  }
608
648
  });
609
- });
649
+ }, updatePriority);
610
650
  const api = React.useMemo(() => createWorldApi(getWorldRef), []);
611
651
  const context = React.useMemo(() => ({
612
652
  rapier,
@@ -617,6 +657,7 @@ const Physics = ({
617
657
  },
618
658
  rigidBodyStates,
619
659
  rigidBodyEvents,
660
+ colliderEvents,
620
661
  isPaused
621
662
  }), [isPaused]);
622
663
  return /*#__PURE__*/React__default["default"].createElement(RapierContext.Provider, {
@@ -1282,22 +1323,26 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
1282
1323
  }, props.children));
1283
1324
  });
1284
1325
 
1285
- const _excluded = ["children"];
1326
+ const _excluded = ["children", "onCollisionEnter", "onCollisionExit"];
1286
1327
 
1287
1328
  const AnyCollider = _ref => {
1288
1329
  let {
1289
- children
1330
+ children,
1331
+ onCollisionEnter,
1332
+ onCollisionExit
1290
1333
  } = _ref,
1291
1334
  props = _objectWithoutProperties(_ref, _excluded);
1292
1335
 
1293
1336
  const {
1294
- world
1337
+ world,
1338
+ colliderEvents
1295
1339
  } = useRapier();
1296
1340
  const rigidBodyContext = useRigidBodyContext();
1297
1341
  const ref = React.useRef(null);
1298
1342
  React.useEffect(() => {
1299
1343
  const scale = ref.current.getWorldScale(new three.Vector3());
1300
- const colliders = []; // If this is an InstancedRigidBody api
1344
+ const colliders = [];
1345
+ const hasCollisionEvents = (rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.hasCollisionEvents) || !!onCollisionEnter || !!onCollisionExit; // If this is an InstancedRigidBody api
1301
1346
 
1302
1347
  if (rigidBodyContext && "at" in rigidBodyContext.api) {
1303
1348
  rigidBodyContext.api.forEach((body, index) => {
@@ -1310,26 +1355,41 @@ const AnyCollider = _ref => {
1310
1355
  }
1311
1356
 
1312
1357
  colliders.push(createColliderFromOptions({
1313
- options: props,
1358
+ options: _objectSpread2({
1359
+ solverGroups: rigidBodyContext.options.solverGroups,
1360
+ collisionGroups: rigidBodyContext.options.collisionGroups
1361
+ }, props),
1314
1362
  world,
1315
1363
  rigidBody: body.raw(),
1316
1364
  scale: instanceScale,
1317
- hasCollisionEvents: rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.hasCollisionEvents
1365
+ hasCollisionEvents
1318
1366
  }));
1319
1367
  });
1320
1368
  } else {
1321
1369
  colliders.push(createColliderFromOptions({
1322
- options: props,
1370
+ options: _objectSpread2({
1371
+ solverGroups: (rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.options.solverGroups) || props.solverGroups,
1372
+ collisionGroups: (rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.options.collisionGroups) || props.collisionGroups
1373
+ }, props),
1323
1374
  world,
1324
1375
  // Initiate with a rigidbody, or undefined, because colliders can exist without a rigid body
1325
1376
  rigidBody: rigidBodyContext && "raw" in rigidBodyContext.api ? rigidBodyContext.api.raw() : undefined,
1326
1377
  scale,
1327
- hasCollisionEvents: rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.hasCollisionEvents
1378
+ hasCollisionEvents
1328
1379
  }));
1329
1380
  }
1381
+ /* Register collision events. */
1382
+
1330
1383
 
1384
+ colliders.forEach(collider => colliderEvents.set(collider.handle, {
1385
+ onCollisionEnter,
1386
+ onCollisionExit
1387
+ }));
1331
1388
  return () => {
1332
- colliders.forEach(collider => world.removeCollider(collider));
1389
+ colliders.forEach(collider => {
1390
+ colliderEvents.delete(collider.handle);
1391
+ world.removeCollider(collider);
1392
+ });
1333
1393
  };
1334
1394
  }, []);
1335
1395
  return /*#__PURE__*/React__default["default"].createElement("object3D", {
@@ -1383,6 +1443,42 @@ const ConvexHullCollider = props => {
1383
1443
  }));
1384
1444
  };
1385
1445
 
1446
+ /**
1447
+ * Calculates an InteractionGroup bitmask for use in the `collisionGroups` or `solverGroups`
1448
+ * properties of RigidBody or Collider components. The first argument represents a list of
1449
+ * groups the entity is in (expressed as numbers from 0 to 15). The second argument is a list
1450
+ * of groups that will be filtered against. When it is omitted, all groups are filtered against.
1451
+ *
1452
+ * @example
1453
+ * A RigidBody that is member of group 0 and will collide with everything from groups 0 and 1:
1454
+ *
1455
+ * ```tsx
1456
+ * <RigidBody collisionGroups={interactionGroups([0], [0, 1])} />
1457
+ * ```
1458
+ *
1459
+ * A RigidBody that is member of groups 0 and 1 and will collide with everything else:
1460
+ *
1461
+ * ```tsx
1462
+ * <RigidBody collisionGroups={interactionGroups([0, 1])} />
1463
+ * ```
1464
+ *
1465
+ * A RigidBody that is member of groups 0 and 1 and will not collide with anything:
1466
+ *
1467
+ * ```tsx
1468
+ * <RigidBody collisionGroups={interactionGroups([0, 1], [])} />
1469
+ * ```
1470
+ *
1471
+ * Please note that Rapier needs interaction filters to evaluate to true between _both_ colliding
1472
+ * entities for collision events to trigger.
1473
+ *
1474
+ * @param memberships Groups the collider is a member of. (Values can range from 0 to 15.)
1475
+ * @param filters Groups the interaction group should filter against. (Values can range from 0 to 15.)
1476
+ * @returns An InteractionGroup bitmask.
1477
+ */
1478
+ const interactionGroups = (memberships, filters) => (bitmask(memberships) << 16) + (filters !== undefined ? bitmask(filters) : 0b1111111111111111);
1479
+
1480
+ const bitmask = groups => [groups].flat().reduce((acc, layer) => acc | 1 << layer, 0);
1481
+
1386
1482
  Object.defineProperty(exports, 'CoefficientCombineRule', {
1387
1483
  enumerable: true,
1388
1484
  get: function () { return rapier3dCompat.CoefficientCombineRule; }
@@ -1409,6 +1505,7 @@ exports.Physics = Physics;
1409
1505
  exports.RigidBody = RigidBody;
1410
1506
  exports.RoundCuboidCollider = RoundCuboidCollider;
1411
1507
  exports.TrimeshCollider = TrimeshCollider;
1508
+ exports.interactionGroups = interactionGroups;
1412
1509
  exports.useFixedJoint = useFixedJoint;
1413
1510
  exports.useImpulseJoint = useImpulseJoint;
1414
1511
  exports.usePrismaticJoint = usePrismaticJoint;
@@ -8,6 +8,7 @@ var useAsset = require('use-asset');
8
8
  var fiber = require('@react-three/fiber');
9
9
  var three = require('three');
10
10
  var BufferGeometryUtils = require('three/examples/jsm/utils/BufferGeometryUtils');
11
+ var MathUtils = require('three/src/math/MathUtils');
11
12
  var threeStdlib = require('three-stdlib');
12
13
 
13
14
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
@@ -49,7 +50,7 @@ const rapierVector3ToVector3 = ({
49
50
  x,
50
51
  y,
51
52
  z
52
- }) => _vector3.set(x, y, z).clone();
53
+ }) => _vector3.set(x, y, z);
53
54
  const rapierQuaternionToQuaternion = ({
54
55
  x,
55
56
  y,
@@ -95,6 +96,12 @@ const scaleColliderArgs = (shape, args, scale) => {
95
96
  const scaleArray = [scale.x, scale.y, scale.z, scale.x, scale.x];
96
97
  return newArgs.map((arg, index) => scaleArray[index] * arg);
97
98
  };
99
+
100
+ const applyColliderOptions = (collider, options) => {
101
+ if (options.collisionGroups !== undefined) collider.setCollisionGroups(options.collisionGroups);
102
+ if (options.solverGroups !== undefined) collider.setSolverGroups(options.solverGroups);
103
+ };
104
+
98
105
  const createColliderFromOptions = ({
99
106
  options,
100
107
  world,
@@ -148,6 +155,7 @@ const createColliderFromOptions = ({
148
155
  }
149
156
 
150
157
  const collider = world.createCollider(colliderDesc, rigidBody);
158
+ applyColliderOptions(collider, options);
151
159
  return collider;
152
160
  };
153
161
 
@@ -213,6 +221,7 @@ const createCollidersFromChildren = ({
213
221
  });
214
222
  const actualRigidBody = rigidBody ? world.getRigidBody(rigidBody.handle) : undefined;
215
223
  const collider = world.createCollider(desc, actualRigidBody);
224
+ applyColliderOptions(collider, options);
216
225
  colliders.push(collider);
217
226
  }
218
227
  });
@@ -450,7 +459,8 @@ const Physics = ({
450
459
  children,
451
460
  timeStep: _timeStep = 1 / 60,
452
461
  maxSubSteps: _maxSubSteps = 10,
453
- paused: _paused = false
462
+ paused: _paused = false,
463
+ updatePriority
454
464
  }) => {
455
465
  const rapier = useAsset.useAsset(importRapier);
456
466
  const [isPaused, setIsPaused] = React.useState(_paused);
@@ -468,6 +478,7 @@ const Physics = ({
468
478
  });
469
479
  const [rigidBodyStates] = React.useState(() => new Map());
470
480
  const [rigidBodyEvents] = React.useState(() => new Map());
481
+ const [colliderEvents] = React.useState(() => new Map());
471
482
  const [eventQueue] = React.useState(() => new rapier3dCompat.EventQueue(false)); // Init world
472
483
 
473
484
  React.useEffect(() => {
@@ -497,12 +508,12 @@ const Physics = ({
497
508
  world.timestep = _timeStep;
498
509
  /**
499
510
  * Fixed timeStep simulation progression
500
- * @see https://gafferongames.com/post/fix_your_timestep/
511
+ * @see https://gafferongames.com/post/fix_your_timestep/
501
512
  */
502
513
 
503
514
  let previousTranslations = {}; // don't step time forwards if paused
504
515
 
505
- const nowTime = steppingState.time += _paused ? 0 : delta * 1000;
516
+ const nowTime = steppingState.time += _paused ? 0 : MathUtils.clamp(delta, 0, 1) * 1000;
506
517
  const timeStepMs = _timeStep * 1000;
507
518
  const timeSinceLast = nowTime - steppingState.lastTime;
508
519
  steppingState.lastTime = nowTime;
@@ -552,7 +563,7 @@ const Physics = ({
552
563
  }
553
564
 
554
565
  let oldState = previousTranslations[rigidBody.handle];
555
- let newTranslation = rapierVector3ToVector3(rigidBody.translation());
566
+ let newTranslation = rigidBody.translation();
556
567
  let newRotation = rapierQuaternionToQuaternion(rigidBody.rotation());
557
568
  let interpolatedTranslation = oldState ? oldState.translation.lerp(newTranslation, 1) : newTranslation;
558
569
  let interpolatedRotation = oldState ? oldState.rotation.slerp(newRotation, interpolationAlpha) : newRotation;
@@ -577,36 +588,65 @@ const Physics = ({
577
588
 
578
589
  const rigidBody1 = world.getRigidBody(rigidBodyHandle1);
579
590
  const rigidBody2 = world.getRigidBody(rigidBodyHandle2);
580
- const events1 = rigidBodyEvents.get(rigidBodyHandle1);
581
- const events2 = rigidBodyEvents.get(rigidBodyHandle2);
591
+ const rigidBody1Events = rigidBodyEvents.get(rigidBodyHandle1);
592
+ const rigidBoyd2Events = rigidBodyEvents.get(rigidBodyHandle2);
593
+ const collider1Events = colliderEvents.get(collider1.handle);
594
+ const collider2Events = colliderEvents.get(collider2.handle);
582
595
 
583
596
  if (started) {
584
597
  world.contactPair(collider1, collider2, (manifold, flipped) => {
585
- var _events1$onCollisionE, _events2$onCollisionE;
598
+ var _rigidBody1Events$onC, _rigidBoyd2Events$onC, _collider1Events$onCo, _collider2Events$onCo;
586
599
 
587
- events1 === null || events1 === void 0 ? void 0 : (_events1$onCollisionE = events1.onCollisionEnter) === null || _events1$onCollisionE === void 0 ? void 0 : _events1$onCollisionE.call(events1, {
600
+ /* RigidBody events */
601
+ rigidBody1Events === null || rigidBody1Events === void 0 ? void 0 : (_rigidBody1Events$onC = rigidBody1Events.onCollisionEnter) === null || _rigidBody1Events$onC === void 0 ? void 0 : _rigidBody1Events$onC.call(rigidBody1Events, {
588
602
  target: rigidBody2,
603
+ collider: collider2,
589
604
  manifold,
590
605
  flipped
591
606
  });
592
- events2 === null || events2 === void 0 ? void 0 : (_events2$onCollisionE = events2.onCollisionEnter) === null || _events2$onCollisionE === void 0 ? void 0 : _events2$onCollisionE.call(events2, {
607
+ rigidBoyd2Events === null || rigidBoyd2Events === void 0 ? void 0 : (_rigidBoyd2Events$onC = rigidBoyd2Events.onCollisionEnter) === null || _rigidBoyd2Events$onC === void 0 ? void 0 : _rigidBoyd2Events$onC.call(rigidBoyd2Events, {
593
608
  target: rigidBody1,
609
+ collider: collider1,
610
+ manifold,
611
+ flipped
612
+ });
613
+ /* Collider events */
614
+
615
+ collider1Events === null || collider1Events === void 0 ? void 0 : (_collider1Events$onCo = collider1Events.onCollisionEnter) === null || _collider1Events$onCo === void 0 ? void 0 : _collider1Events$onCo.call(collider1Events, {
616
+ target: rigidBody2,
617
+ collider: collider2,
618
+ manifold,
619
+ flipped
620
+ });
621
+ collider2Events === null || collider2Events === void 0 ? void 0 : (_collider2Events$onCo = collider2Events.onCollisionEnter) === null || _collider2Events$onCo === void 0 ? void 0 : _collider2Events$onCo.call(collider2Events, {
622
+ target: rigidBody1,
623
+ collider: collider1,
594
624
  manifold,
595
625
  flipped
596
626
  });
597
627
  });
598
628
  } else {
599
- var _events1$onCollisionE2, _events2$onCollisionE2;
629
+ var _rigidBody1Events$onC2, _rigidBoyd2Events$onC2, _collider1Events$onCo2, _collider2Events$onCo2;
600
630
 
601
- events1 === null || events1 === void 0 ? void 0 : (_events1$onCollisionE2 = events1.onCollisionExit) === null || _events1$onCollisionE2 === void 0 ? void 0 : _events1$onCollisionE2.call(events1, {
602
- target: rigidBody2
631
+ rigidBody1Events === null || rigidBody1Events === void 0 ? void 0 : (_rigidBody1Events$onC2 = rigidBody1Events.onCollisionExit) === null || _rigidBody1Events$onC2 === void 0 ? void 0 : _rigidBody1Events$onC2.call(rigidBody1Events, {
632
+ target: rigidBody2,
633
+ collider: collider2
634
+ });
635
+ rigidBoyd2Events === null || rigidBoyd2Events === void 0 ? void 0 : (_rigidBoyd2Events$onC2 = rigidBoyd2Events.onCollisionExit) === null || _rigidBoyd2Events$onC2 === void 0 ? void 0 : _rigidBoyd2Events$onC2.call(rigidBoyd2Events, {
636
+ target: rigidBody1,
637
+ collider: collider1
603
638
  });
604
- events2 === null || events2 === void 0 ? void 0 : (_events2$onCollisionE2 = events2.onCollisionExit) === null || _events2$onCollisionE2 === void 0 ? void 0 : _events2$onCollisionE2.call(events2, {
605
- target: rigidBody1
639
+ collider1Events === null || collider1Events === void 0 ? void 0 : (_collider1Events$onCo2 = collider1Events.onCollisionExit) === null || _collider1Events$onCo2 === void 0 ? void 0 : _collider1Events$onCo2.call(collider1Events, {
640
+ target: rigidBody2,
641
+ collider: collider2
642
+ });
643
+ collider2Events === null || collider2Events === void 0 ? void 0 : (_collider2Events$onCo2 = collider2Events.onCollisionExit) === null || _collider2Events$onCo2 === void 0 ? void 0 : _collider2Events$onCo2.call(collider2Events, {
644
+ target: rigidBody1,
645
+ collider: collider1
606
646
  });
607
647
  }
608
648
  });
609
- });
649
+ }, updatePriority);
610
650
  const api = React.useMemo(() => createWorldApi(getWorldRef), []);
611
651
  const context = React.useMemo(() => ({
612
652
  rapier,
@@ -617,6 +657,7 @@ const Physics = ({
617
657
  },
618
658
  rigidBodyStates,
619
659
  rigidBodyEvents,
660
+ colliderEvents,
620
661
  isPaused
621
662
  }), [isPaused]);
622
663
  return /*#__PURE__*/React__default["default"].createElement(RapierContext.Provider, {
@@ -1282,22 +1323,26 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
1282
1323
  }, props.children));
1283
1324
  });
1284
1325
 
1285
- const _excluded = ["children"];
1326
+ const _excluded = ["children", "onCollisionEnter", "onCollisionExit"];
1286
1327
 
1287
1328
  const AnyCollider = _ref => {
1288
1329
  let {
1289
- children
1330
+ children,
1331
+ onCollisionEnter,
1332
+ onCollisionExit
1290
1333
  } = _ref,
1291
1334
  props = _objectWithoutProperties(_ref, _excluded);
1292
1335
 
1293
1336
  const {
1294
- world
1337
+ world,
1338
+ colliderEvents
1295
1339
  } = useRapier();
1296
1340
  const rigidBodyContext = useRigidBodyContext();
1297
1341
  const ref = React.useRef(null);
1298
1342
  React.useEffect(() => {
1299
1343
  const scale = ref.current.getWorldScale(new three.Vector3());
1300
- const colliders = []; // If this is an InstancedRigidBody api
1344
+ const colliders = [];
1345
+ const hasCollisionEvents = (rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.hasCollisionEvents) || !!onCollisionEnter || !!onCollisionExit; // If this is an InstancedRigidBody api
1301
1346
 
1302
1347
  if (rigidBodyContext && "at" in rigidBodyContext.api) {
1303
1348
  rigidBodyContext.api.forEach((body, index) => {
@@ -1310,26 +1355,41 @@ const AnyCollider = _ref => {
1310
1355
  }
1311
1356
 
1312
1357
  colliders.push(createColliderFromOptions({
1313
- options: props,
1358
+ options: _objectSpread2({
1359
+ solverGroups: rigidBodyContext.options.solverGroups,
1360
+ collisionGroups: rigidBodyContext.options.collisionGroups
1361
+ }, props),
1314
1362
  world,
1315
1363
  rigidBody: body.raw(),
1316
1364
  scale: instanceScale,
1317
- hasCollisionEvents: rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.hasCollisionEvents
1365
+ hasCollisionEvents
1318
1366
  }));
1319
1367
  });
1320
1368
  } else {
1321
1369
  colliders.push(createColliderFromOptions({
1322
- options: props,
1370
+ options: _objectSpread2({
1371
+ solverGroups: (rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.options.solverGroups) || props.solverGroups,
1372
+ collisionGroups: (rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.options.collisionGroups) || props.collisionGroups
1373
+ }, props),
1323
1374
  world,
1324
1375
  // Initiate with a rigidbody, or undefined, because colliders can exist without a rigid body
1325
1376
  rigidBody: rigidBodyContext && "raw" in rigidBodyContext.api ? rigidBodyContext.api.raw() : undefined,
1326
1377
  scale,
1327
- hasCollisionEvents: rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.hasCollisionEvents
1378
+ hasCollisionEvents
1328
1379
  }));
1329
1380
  }
1381
+ /* Register collision events. */
1382
+
1330
1383
 
1384
+ colliders.forEach(collider => colliderEvents.set(collider.handle, {
1385
+ onCollisionEnter,
1386
+ onCollisionExit
1387
+ }));
1331
1388
  return () => {
1332
- colliders.forEach(collider => world.removeCollider(collider));
1389
+ colliders.forEach(collider => {
1390
+ colliderEvents.delete(collider.handle);
1391
+ world.removeCollider(collider);
1392
+ });
1333
1393
  };
1334
1394
  }, []);
1335
1395
  return /*#__PURE__*/React__default["default"].createElement("object3D", {
@@ -1383,6 +1443,42 @@ const ConvexHullCollider = props => {
1383
1443
  }));
1384
1444
  };
1385
1445
 
1446
+ /**
1447
+ * Calculates an InteractionGroup bitmask for use in the `collisionGroups` or `solverGroups`
1448
+ * properties of RigidBody or Collider components. The first argument represents a list of
1449
+ * groups the entity is in (expressed as numbers from 0 to 15). The second argument is a list
1450
+ * of groups that will be filtered against. When it is omitted, all groups are filtered against.
1451
+ *
1452
+ * @example
1453
+ * A RigidBody that is member of group 0 and will collide with everything from groups 0 and 1:
1454
+ *
1455
+ * ```tsx
1456
+ * <RigidBody collisionGroups={interactionGroups([0], [0, 1])} />
1457
+ * ```
1458
+ *
1459
+ * A RigidBody that is member of groups 0 and 1 and will collide with everything else:
1460
+ *
1461
+ * ```tsx
1462
+ * <RigidBody collisionGroups={interactionGroups([0, 1])} />
1463
+ * ```
1464
+ *
1465
+ * A RigidBody that is member of groups 0 and 1 and will not collide with anything:
1466
+ *
1467
+ * ```tsx
1468
+ * <RigidBody collisionGroups={interactionGroups([0, 1], [])} />
1469
+ * ```
1470
+ *
1471
+ * Please note that Rapier needs interaction filters to evaluate to true between _both_ colliding
1472
+ * entities for collision events to trigger.
1473
+ *
1474
+ * @param memberships Groups the collider is a member of. (Values can range from 0 to 15.)
1475
+ * @param filters Groups the interaction group should filter against. (Values can range from 0 to 15.)
1476
+ * @returns An InteractionGroup bitmask.
1477
+ */
1478
+ const interactionGroups = (memberships, filters) => (bitmask(memberships) << 16) + (filters !== undefined ? bitmask(filters) : 0b1111111111111111);
1479
+
1480
+ const bitmask = groups => [groups].flat().reduce((acc, layer) => acc | 1 << layer, 0);
1481
+
1386
1482
  Object.defineProperty(exports, 'CoefficientCombineRule', {
1387
1483
  enumerable: true,
1388
1484
  get: function () { return rapier3dCompat.CoefficientCombineRule; }
@@ -1409,6 +1505,7 @@ exports.Physics = Physics;
1409
1505
  exports.RigidBody = RigidBody;
1410
1506
  exports.RoundCuboidCollider = RoundCuboidCollider;
1411
1507
  exports.TrimeshCollider = TrimeshCollider;
1508
+ exports.interactionGroups = interactionGroups;
1412
1509
  exports.useFixedJoint = useFixedJoint;
1413
1510
  exports.useImpulseJoint = useImpulseJoint;
1414
1511
  exports.usePrismaticJoint = usePrismaticJoint;
@@ -5,6 +5,7 @@ import { useAsset } from 'use-asset';
5
5
  import { useFrame } from '@react-three/fiber';
6
6
  import { Quaternion, Euler, Vector3, Object3D, Matrix4, InstancedMesh, MeshBasicMaterial, Color, PlaneGeometry, ConeBufferGeometry, CapsuleBufferGeometry, CylinderBufferGeometry, BufferGeometry, BufferAttribute, SphereBufferGeometry, BoxBufferGeometry, DynamicDrawUsage } from 'three';
7
7
  import { mergeVertices } from 'three/examples/jsm/utils/BufferGeometryUtils';
8
+ import { clamp } from 'three/src/math/MathUtils';
8
9
  import { RoundedBoxGeometry } from 'three-stdlib';
9
10
 
10
11
  const _quaternion = new Quaternion();
@@ -24,7 +25,7 @@ const rapierVector3ToVector3 = ({
24
25
  x,
25
26
  y,
26
27
  z
27
- }) => _vector3.set(x, y, z).clone();
28
+ }) => _vector3.set(x, y, z);
28
29
  const rapierQuaternionToQuaternion = ({
29
30
  x,
30
31
  y,
@@ -70,6 +71,12 @@ const scaleColliderArgs = (shape, args, scale) => {
70
71
  const scaleArray = [scale.x, scale.y, scale.z, scale.x, scale.x];
71
72
  return newArgs.map((arg, index) => scaleArray[index] * arg);
72
73
  };
74
+
75
+ const applyColliderOptions = (collider, options) => {
76
+ if (options.collisionGroups !== undefined) collider.setCollisionGroups(options.collisionGroups);
77
+ if (options.solverGroups !== undefined) collider.setSolverGroups(options.solverGroups);
78
+ };
79
+
73
80
  const createColliderFromOptions = ({
74
81
  options,
75
82
  world,
@@ -123,6 +130,7 @@ const createColliderFromOptions = ({
123
130
  }
124
131
 
125
132
  const collider = world.createCollider(colliderDesc, rigidBody);
133
+ applyColliderOptions(collider, options);
126
134
  return collider;
127
135
  };
128
136
 
@@ -188,6 +196,7 @@ const createCollidersFromChildren = ({
188
196
  });
189
197
  const actualRigidBody = rigidBody ? world.getRigidBody(rigidBody.handle) : undefined;
190
198
  const collider = world.createCollider(desc, actualRigidBody);
199
+ applyColliderOptions(collider, options);
191
200
  colliders.push(collider);
192
201
  }
193
202
  });
@@ -425,7 +434,8 @@ const Physics = ({
425
434
  children,
426
435
  timeStep: _timeStep = 1 / 60,
427
436
  maxSubSteps: _maxSubSteps = 10,
428
- paused: _paused = false
437
+ paused: _paused = false,
438
+ updatePriority
429
439
  }) => {
430
440
  const rapier = useAsset(importRapier);
431
441
  const [isPaused, setIsPaused] = useState(_paused);
@@ -443,6 +453,7 @@ const Physics = ({
443
453
  });
444
454
  const [rigidBodyStates] = useState(() => new Map());
445
455
  const [rigidBodyEvents] = useState(() => new Map());
456
+ const [colliderEvents] = useState(() => new Map());
446
457
  const [eventQueue] = useState(() => new EventQueue(false)); // Init world
447
458
 
448
459
  useEffect(() => {
@@ -472,12 +483,12 @@ const Physics = ({
472
483
  world.timestep = _timeStep;
473
484
  /**
474
485
  * Fixed timeStep simulation progression
475
- * @see https://gafferongames.com/post/fix_your_timestep/
486
+ * @see https://gafferongames.com/post/fix_your_timestep/
476
487
  */
477
488
 
478
489
  let previousTranslations = {}; // don't step time forwards if paused
479
490
 
480
- const nowTime = steppingState.time += _paused ? 0 : delta * 1000;
491
+ const nowTime = steppingState.time += _paused ? 0 : clamp(delta, 0, 1) * 1000;
481
492
  const timeStepMs = _timeStep * 1000;
482
493
  const timeSinceLast = nowTime - steppingState.lastTime;
483
494
  steppingState.lastTime = nowTime;
@@ -527,7 +538,7 @@ const Physics = ({
527
538
  }
528
539
 
529
540
  let oldState = previousTranslations[rigidBody.handle];
530
- let newTranslation = rapierVector3ToVector3(rigidBody.translation());
541
+ let newTranslation = rigidBody.translation();
531
542
  let newRotation = rapierQuaternionToQuaternion(rigidBody.rotation());
532
543
  let interpolatedTranslation = oldState ? oldState.translation.lerp(newTranslation, 1) : newTranslation;
533
544
  let interpolatedRotation = oldState ? oldState.rotation.slerp(newRotation, interpolationAlpha) : newRotation;
@@ -552,36 +563,65 @@ const Physics = ({
552
563
 
553
564
  const rigidBody1 = world.getRigidBody(rigidBodyHandle1);
554
565
  const rigidBody2 = world.getRigidBody(rigidBodyHandle2);
555
- const events1 = rigidBodyEvents.get(rigidBodyHandle1);
556
- const events2 = rigidBodyEvents.get(rigidBodyHandle2);
566
+ const rigidBody1Events = rigidBodyEvents.get(rigidBodyHandle1);
567
+ const rigidBoyd2Events = rigidBodyEvents.get(rigidBodyHandle2);
568
+ const collider1Events = colliderEvents.get(collider1.handle);
569
+ const collider2Events = colliderEvents.get(collider2.handle);
557
570
 
558
571
  if (started) {
559
572
  world.contactPair(collider1, collider2, (manifold, flipped) => {
560
- var _events1$onCollisionE, _events2$onCollisionE;
573
+ var _rigidBody1Events$onC, _rigidBoyd2Events$onC, _collider1Events$onCo, _collider2Events$onCo;
561
574
 
562
- events1 === null || events1 === void 0 ? void 0 : (_events1$onCollisionE = events1.onCollisionEnter) === null || _events1$onCollisionE === void 0 ? void 0 : _events1$onCollisionE.call(events1, {
575
+ /* RigidBody events */
576
+ rigidBody1Events === null || rigidBody1Events === void 0 ? void 0 : (_rigidBody1Events$onC = rigidBody1Events.onCollisionEnter) === null || _rigidBody1Events$onC === void 0 ? void 0 : _rigidBody1Events$onC.call(rigidBody1Events, {
563
577
  target: rigidBody2,
578
+ collider: collider2,
564
579
  manifold,
565
580
  flipped
566
581
  });
567
- events2 === null || events2 === void 0 ? void 0 : (_events2$onCollisionE = events2.onCollisionEnter) === null || _events2$onCollisionE === void 0 ? void 0 : _events2$onCollisionE.call(events2, {
582
+ rigidBoyd2Events === null || rigidBoyd2Events === void 0 ? void 0 : (_rigidBoyd2Events$onC = rigidBoyd2Events.onCollisionEnter) === null || _rigidBoyd2Events$onC === void 0 ? void 0 : _rigidBoyd2Events$onC.call(rigidBoyd2Events, {
568
583
  target: rigidBody1,
584
+ collider: collider1,
585
+ manifold,
586
+ flipped
587
+ });
588
+ /* Collider events */
589
+
590
+ collider1Events === null || collider1Events === void 0 ? void 0 : (_collider1Events$onCo = collider1Events.onCollisionEnter) === null || _collider1Events$onCo === void 0 ? void 0 : _collider1Events$onCo.call(collider1Events, {
591
+ target: rigidBody2,
592
+ collider: collider2,
593
+ manifold,
594
+ flipped
595
+ });
596
+ collider2Events === null || collider2Events === void 0 ? void 0 : (_collider2Events$onCo = collider2Events.onCollisionEnter) === null || _collider2Events$onCo === void 0 ? void 0 : _collider2Events$onCo.call(collider2Events, {
597
+ target: rigidBody1,
598
+ collider: collider1,
569
599
  manifold,
570
600
  flipped
571
601
  });
572
602
  });
573
603
  } else {
574
- var _events1$onCollisionE2, _events2$onCollisionE2;
604
+ var _rigidBody1Events$onC2, _rigidBoyd2Events$onC2, _collider1Events$onCo2, _collider2Events$onCo2;
575
605
 
576
- events1 === null || events1 === void 0 ? void 0 : (_events1$onCollisionE2 = events1.onCollisionExit) === null || _events1$onCollisionE2 === void 0 ? void 0 : _events1$onCollisionE2.call(events1, {
577
- target: rigidBody2
606
+ rigidBody1Events === null || rigidBody1Events === void 0 ? void 0 : (_rigidBody1Events$onC2 = rigidBody1Events.onCollisionExit) === null || _rigidBody1Events$onC2 === void 0 ? void 0 : _rigidBody1Events$onC2.call(rigidBody1Events, {
607
+ target: rigidBody2,
608
+ collider: collider2
609
+ });
610
+ rigidBoyd2Events === null || rigidBoyd2Events === void 0 ? void 0 : (_rigidBoyd2Events$onC2 = rigidBoyd2Events.onCollisionExit) === null || _rigidBoyd2Events$onC2 === void 0 ? void 0 : _rigidBoyd2Events$onC2.call(rigidBoyd2Events, {
611
+ target: rigidBody1,
612
+ collider: collider1
578
613
  });
579
- events2 === null || events2 === void 0 ? void 0 : (_events2$onCollisionE2 = events2.onCollisionExit) === null || _events2$onCollisionE2 === void 0 ? void 0 : _events2$onCollisionE2.call(events2, {
580
- target: rigidBody1
614
+ collider1Events === null || collider1Events === void 0 ? void 0 : (_collider1Events$onCo2 = collider1Events.onCollisionExit) === null || _collider1Events$onCo2 === void 0 ? void 0 : _collider1Events$onCo2.call(collider1Events, {
615
+ target: rigidBody2,
616
+ collider: collider2
617
+ });
618
+ collider2Events === null || collider2Events === void 0 ? void 0 : (_collider2Events$onCo2 = collider2Events.onCollisionExit) === null || _collider2Events$onCo2 === void 0 ? void 0 : _collider2Events$onCo2.call(collider2Events, {
619
+ target: rigidBody1,
620
+ collider: collider1
581
621
  });
582
622
  }
583
623
  });
584
- });
624
+ }, updatePriority);
585
625
  const api = useMemo(() => createWorldApi(getWorldRef), []);
586
626
  const context = useMemo(() => ({
587
627
  rapier,
@@ -592,6 +632,7 @@ const Physics = ({
592
632
  },
593
633
  rigidBodyStates,
594
634
  rigidBodyEvents,
635
+ colliderEvents,
595
636
  isPaused
596
637
  }), [isPaused]);
597
638
  return /*#__PURE__*/React.createElement(RapierContext.Provider, {
@@ -1257,22 +1298,26 @@ const InstancedRigidBodies = /*#__PURE__*/forwardRef((props, ref) => {
1257
1298
  }, props.children));
1258
1299
  });
1259
1300
 
1260
- const _excluded = ["children"];
1301
+ const _excluded = ["children", "onCollisionEnter", "onCollisionExit"];
1261
1302
 
1262
1303
  const AnyCollider = _ref => {
1263
1304
  let {
1264
- children
1305
+ children,
1306
+ onCollisionEnter,
1307
+ onCollisionExit
1265
1308
  } = _ref,
1266
1309
  props = _objectWithoutProperties(_ref, _excluded);
1267
1310
 
1268
1311
  const {
1269
- world
1312
+ world,
1313
+ colliderEvents
1270
1314
  } = useRapier();
1271
1315
  const rigidBodyContext = useRigidBodyContext();
1272
1316
  const ref = useRef(null);
1273
1317
  useEffect(() => {
1274
1318
  const scale = ref.current.getWorldScale(new Vector3());
1275
- const colliders = []; // If this is an InstancedRigidBody api
1319
+ const colliders = [];
1320
+ const hasCollisionEvents = (rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.hasCollisionEvents) || !!onCollisionEnter || !!onCollisionExit; // If this is an InstancedRigidBody api
1276
1321
 
1277
1322
  if (rigidBodyContext && "at" in rigidBodyContext.api) {
1278
1323
  rigidBodyContext.api.forEach((body, index) => {
@@ -1285,26 +1330,41 @@ const AnyCollider = _ref => {
1285
1330
  }
1286
1331
 
1287
1332
  colliders.push(createColliderFromOptions({
1288
- options: props,
1333
+ options: _objectSpread2({
1334
+ solverGroups: rigidBodyContext.options.solverGroups,
1335
+ collisionGroups: rigidBodyContext.options.collisionGroups
1336
+ }, props),
1289
1337
  world,
1290
1338
  rigidBody: body.raw(),
1291
1339
  scale: instanceScale,
1292
- hasCollisionEvents: rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.hasCollisionEvents
1340
+ hasCollisionEvents
1293
1341
  }));
1294
1342
  });
1295
1343
  } else {
1296
1344
  colliders.push(createColliderFromOptions({
1297
- options: props,
1345
+ options: _objectSpread2({
1346
+ solverGroups: (rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.options.solverGroups) || props.solverGroups,
1347
+ collisionGroups: (rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.options.collisionGroups) || props.collisionGroups
1348
+ }, props),
1298
1349
  world,
1299
1350
  // Initiate with a rigidbody, or undefined, because colliders can exist without a rigid body
1300
1351
  rigidBody: rigidBodyContext && "raw" in rigidBodyContext.api ? rigidBodyContext.api.raw() : undefined,
1301
1352
  scale,
1302
- hasCollisionEvents: rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.hasCollisionEvents
1353
+ hasCollisionEvents
1303
1354
  }));
1304
1355
  }
1356
+ /* Register collision events. */
1357
+
1305
1358
 
1359
+ colliders.forEach(collider => colliderEvents.set(collider.handle, {
1360
+ onCollisionEnter,
1361
+ onCollisionExit
1362
+ }));
1306
1363
  return () => {
1307
- colliders.forEach(collider => world.removeCollider(collider));
1364
+ colliders.forEach(collider => {
1365
+ colliderEvents.delete(collider.handle);
1366
+ world.removeCollider(collider);
1367
+ });
1308
1368
  };
1309
1369
  }, []);
1310
1370
  return /*#__PURE__*/React.createElement("object3D", {
@@ -1358,4 +1418,40 @@ const ConvexHullCollider = props => {
1358
1418
  }));
1359
1419
  };
1360
1420
 
1361
- export { BallCollider, CapsuleCollider, ConeCollider, ConvexHullCollider, CuboidCollider, CylinderCollider, Debug, HeightfieldCollider, InstancedRigidBodies, MeshCollider, Physics, RigidBody, RoundCuboidCollider, TrimeshCollider, useFixedJoint, useImpulseJoint, usePrismaticJoint, useRapier, useRevoluteJoint, useRigidBody, useSphericalJoint };
1421
+ /**
1422
+ * Calculates an InteractionGroup bitmask for use in the `collisionGroups` or `solverGroups`
1423
+ * properties of RigidBody or Collider components. The first argument represents a list of
1424
+ * groups the entity is in (expressed as numbers from 0 to 15). The second argument is a list
1425
+ * of groups that will be filtered against. When it is omitted, all groups are filtered against.
1426
+ *
1427
+ * @example
1428
+ * A RigidBody that is member of group 0 and will collide with everything from groups 0 and 1:
1429
+ *
1430
+ * ```tsx
1431
+ * <RigidBody collisionGroups={interactionGroups([0], [0, 1])} />
1432
+ * ```
1433
+ *
1434
+ * A RigidBody that is member of groups 0 and 1 and will collide with everything else:
1435
+ *
1436
+ * ```tsx
1437
+ * <RigidBody collisionGroups={interactionGroups([0, 1])} />
1438
+ * ```
1439
+ *
1440
+ * A RigidBody that is member of groups 0 and 1 and will not collide with anything:
1441
+ *
1442
+ * ```tsx
1443
+ * <RigidBody collisionGroups={interactionGroups([0, 1], [])} />
1444
+ * ```
1445
+ *
1446
+ * Please note that Rapier needs interaction filters to evaluate to true between _both_ colliding
1447
+ * entities for collision events to trigger.
1448
+ *
1449
+ * @param memberships Groups the collider is a member of. (Values can range from 0 to 15.)
1450
+ * @param filters Groups the interaction group should filter against. (Values can range from 0 to 15.)
1451
+ * @returns An InteractionGroup bitmask.
1452
+ */
1453
+ const interactionGroups = (memberships, filters) => (bitmask(memberships) << 16) + (filters !== undefined ? bitmask(filters) : 0b1111111111111111);
1454
+
1455
+ const bitmask = groups => [groups].flat().reduce((acc, layer) => acc | 1 << layer, 0);
1456
+
1457
+ export { BallCollider, CapsuleCollider, ConeCollider, ConvexHullCollider, CuboidCollider, CylinderCollider, Debug, HeightfieldCollider, InstancedRigidBodies, MeshCollider, Physics, RigidBody, RoundCuboidCollider, TrimeshCollider, interactionGroups, useFixedJoint, useImpulseJoint, usePrismaticJoint, useRapier, useRevoluteJoint, useRigidBody, useSphericalJoint };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-three/rapier",
3
- "version": "0.6.8",
3
+ "version": "0.6.9",
4
4
  "source": "src/index.ts",
5
5
  "main": "dist/react-three-rapier.cjs.js",
6
6
  "module": "dist/react-three-rapier.esm.js",
@@ -20,12 +20,12 @@
20
20
  "peerDependencies": {
21
21
  "@react-three/fiber": "^8.0.12",
22
22
  "react": "^18.0.0",
23
- "three": "^0.139.2",
24
- "three-stdlib": "^2.15.0"
23
+ "three": ">=0.139.2"
25
24
  },
26
25
  "dependencies": {
27
26
  "@dimforge/rapier3d-compat": "0.9.0",
28
- "use-asset": "^1.0.4"
27
+ "use-asset": "^1.0.4",
28
+ "three-stdlib": "^2.15.0"
29
29
  },
30
30
  "repository": "https://github.com/pmndrs/react-three-rapier/tree/master/packages/react-three-rapier"
31
31
  }
package/readme.md CHANGED
@@ -1,10 +1,10 @@
1
1
  <p align="center">
2
- <img src="https://raw.githubusercontent.com/pmndrs/react-three-rapier/HEAD/packages/react-three-rapier/misc/hero.svg" alt="@react-three/rapier" />
2
+ <a href="#"><img src="https://raw.githubusercontent.com/pmndrs/react-three-rapier/HEAD/packages/react-three-rapier/misc/hero.svg" alt="@react-three/rapier" /></a>
3
3
  </p>
4
4
 
5
5
  <p align="center">
6
- <img src="https://img.shields.io/npm/v/@react-three/rapier?style=for-the-badge&colorA=0099DA&colorB=ffffff" />
7
- <img src="https://img.shields.io/discord/740090768164651008?style=for-the-badge&colorA=0099DA&colorB=ffffff&label=discord&logo=discord&logoColor=ffffff)](https://discord.gg/ZZjjNvJ" />
6
+ <a href="https://www.npmjs.com/package/@react-three/rapier"><img src="https://img.shields.io/npm/v/@react-three/rapier?style=for-the-badge&colorA=0099DA&colorB=ffffff" /></a>
7
+ <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>
8
8
  </p>
9
9
 
10
10
  <p align="center">⚠️ Under heavy development. All APIs are subject to change. ⚠️</p>
@@ -141,7 +141,7 @@ const Scene = () => {
141
141
  instancedApi.at(40).applyImpulse({ x: 0, y: 10, z: 0 });
142
142
 
143
143
  // Or update all instances as if they were in an array
144
- instancedApi.forEach((api) => {
144
+ instancedApi.forEach(api => {
145
145
  api.applyImpulse({ x: 0, y: 10, z: 0 });
146
146
  });
147
147
  }, []);
@@ -153,13 +153,13 @@ const Scene = () => {
153
153
  const rotations = Array.from({ length: COUNT }, (_, index) => [
154
154
  Math.random(),
155
155
  Math.random(),
156
- Math.random(),
156
+ Math.random()
157
157
  ]);
158
158
 
159
159
  const scales = Array.from({ length: COUNT }, (_, index) => [
160
160
  Math.random(),
161
161
  Math.random(),
162
- Math.random(),
162
+ Math.random()
163
163
  ]);
164
164
 
165
165
  return (
@@ -212,7 +212,7 @@ const Scene = () => {
212
212
 
213
213
  ## Events
214
214
 
215
- You can subscribe collision and state events on the RigidBody.
215
+ You can subscribe to collision and state events on the RigidBody:
216
216
 
217
217
  ```tsx
218
218
  const RigidBottle = () => {
@@ -235,6 +235,60 @@ return (
235
235
  }
236
236
  ```
237
237
 
238
+ You may also subscribe to collision events on individual colliders:
239
+
240
+ ```tsx
241
+ <CuboidCollider
242
+ onCollisionEnter={payload => {
243
+ /* ... */
244
+ }}
245
+ onCollisionExit={payload => {
246
+ /* ... */
247
+ }}
248
+ />
249
+ ```
250
+
251
+ The `payload` object for all collision callbacks contains the following properties:
252
+
253
+ - `target`
254
+ The other rigidbody that was involved in the collision event.
255
+ - `collider`
256
+ The other collider that was involved in the collision event.
257
+ - `manifold` (enter only)
258
+ The [contact manifold](https://rapier.rs/javascript3d/classes/TempContactManifold.html) generated by the collision event.
259
+ - `flipped` (enter only)
260
+ `true` if the data in the `manifold` [is flipped](https://rapier.rs/javascript3d/classes/World.html#contactPair).
261
+
262
+ ### Configuring collision and solver groups
263
+
264
+ Both `<RigidBody>` as well as all collider components allow you to configure `collisionsGroups` and `solverGroups` properties that configures which groups the colliders are in, and what other groups they should interact with in potential collision and solving events (you will find more details on this in the [Rapier documentation](https://rapier.rs/docs/user_guides/javascript/colliders/#collision-groups-and-solver-groups).)
265
+
266
+ Since these are set as bitmasks and bitmasks can get a bit unwieldy to generate, this library provides a helper called `interactionGroups` that can be used to generate bitmasks from numbers and arrays of groups, where groups are identified using numbers from 0 to 15.
267
+
268
+ The first argument is the group, or an array of groups, that the collider is a member of; the second argument is the group, or an array of groups, that the collider should interact with.
269
+
270
+ Here the collider is in group 0, and interacts with colliders from groups 0, 1 and 2:
271
+
272
+ ```tsx
273
+ <CapsuleCollider collisionGroups={interactionGroups(0, [0, 1, 2])} />
274
+ ```
275
+
276
+ This collider is in multiple groups, but only interacts with colliders from a single group:
277
+
278
+ ```tsx
279
+ <CapsuleCollider collisionGroups={interactionGroups([0, 5], 7)} />
280
+ ```
281
+
282
+ When the second argument is omitted, the collider will interact with all groups:
283
+
284
+ ```tsx
285
+ <CapsuleCollider collisionGroups={interactionGroups(12)} />
286
+ ```
287
+
288
+ > **Note** Please remember that in Rapier, for a collision (or solving) event to occur, both colliders involved in the event must match the related interaction groups -- a one-way match will be ignored.
289
+
290
+ > **Note** By default, colliders are members of all groups, and will interact with all other groups.
291
+
238
292
  ## Joints
239
293
 
240
294
  WIP
@@ -255,7 +309,7 @@ In order, but also not necessarily:
255
309
  - [x] Colliders outside RigidBodies
256
310
  - [x] InstancedMesh support
257
311
  - [x] Timestep improvements for determinism
258
- - [ ] Normalize and improve collision events (add events to single Colliders, InstancedRigidBodies, etc)
312
+ - [x] Normalize and improve collision events (add events to single Colliders)
313
+ - [ ] Add collision events to InstancedRigidBodies
259
314
  - [ ] Docs
260
315
  - [ ] CodeSandbox examples
261
- - [ ] Helpers, for things like Vehicle, Rope, Player, etc