@react-three/rapier 0.2.0 → 0.4.0

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.
@@ -1,9 +1,9 @@
1
- import React, { useRef, useState, useLayoutEffect, useMemo, createContext, useContext, useEffect, forwardRef, useImperativeHandle, memo } from 'react';
1
+ import React, { useRef, useState, useEffect, useMemo, createContext, useContext, forwardRef, useImperativeHandle, memo } from 'react';
2
2
  import { useAsset } from 'use-asset';
3
3
  import { useFrame } from '@react-three/fiber';
4
- import { Vector3, Quaternion, Object3D, Euler, CylinderBufferGeometry, BufferGeometry, BufferAttribute, SphereBufferGeometry, BoxBufferGeometry } from 'three';
5
- import { ColliderDesc, CoefficientCombineRule, ShapeType } from '@dimforge/rapier3d-compat';
4
+ import { ColliderDesc, CoefficientCombineRule, ActiveEvents, EventQueue, ShapeType } from '@dimforge/rapier3d-compat';
6
5
  export { CoefficientCombineRule, Collider as RapierCollider, RigidBody as RapierRigidBody } from '@dimforge/rapier3d-compat';
6
+ import { Vector3, Quaternion, Object3D, Euler, CylinderBufferGeometry, BufferGeometry, BufferAttribute, SphereBufferGeometry, BoxBufferGeometry } from 'three';
7
7
 
8
8
  const vectorArrayToObject = arr => {
9
9
  const [x, y, z] = arr;
@@ -38,11 +38,11 @@ const scaleColliderArgs = (shape, args, scale) => {
38
38
  const scaleArray = [scale.x, scale.y, scale.z];
39
39
  return newArgs.map((arg, index) => scaleArray[index] * arg);
40
40
  };
41
- const createColliderFromOptions = (options, world, rigidBodyHandle, scale = {
41
+ const createColliderFromOptions = (options, world, rigidBody, scale = {
42
42
  x: 1,
43
43
  y: 1,
44
44
  z: 1
45
- }) => {
45
+ }, hasCollisionEvents = false) => {
46
46
  var _options$shape, _options$args, _options$restitution, _options$restitutionC, _options$friction, _options$frictionComb;
47
47
 
48
48
  const mass = (options === null || options === void 0 ? void 0 : options.mass) || 1;
@@ -60,7 +60,12 @@ const createColliderFromOptions = (options, world, rigidBodyHandle, scale = {
60
60
  y: ry,
61
61
  z: rz,
62
62
  w: 1
63
- }).setRestitution((_options$restitution = options === null || options === void 0 ? void 0 : options.restitution) !== null && _options$restitution !== void 0 ? _options$restitution : 0).setRestitutionCombineRule((_options$restitutionC = options === null || options === void 0 ? void 0 : options.restitutionCombineRule) !== null && _options$restitutionC !== void 0 ? _options$restitutionC : CoefficientCombineRule.Average).setFriction((_options$friction = options === null || options === void 0 ? void 0 : options.friction) !== null && _options$friction !== void 0 ? _options$friction : 0.7).setFrictionCombineRule((_options$frictionComb = options === null || options === void 0 ? void 0 : options.frictionCombineRule) !== null && _options$frictionComb !== void 0 ? _options$frictionComb : CoefficientCombineRule.Average); // If any of the mass properties are specified, add mass properties
63
+ }).setRestitution((_options$restitution = options === null || options === void 0 ? void 0 : options.restitution) !== null && _options$restitution !== void 0 ? _options$restitution : 0).setRestitutionCombineRule((_options$restitutionC = options === null || options === void 0 ? void 0 : options.restitutionCombineRule) !== null && _options$restitutionC !== void 0 ? _options$restitutionC : CoefficientCombineRule.Average).setFriction((_options$friction = options === null || options === void 0 ? void 0 : options.friction) !== null && _options$friction !== void 0 ? _options$friction : 0.7).setFrictionCombineRule((_options$frictionComb = options === null || options === void 0 ? void 0 : options.frictionCombineRule) !== null && _options$frictionComb !== void 0 ? _options$frictionComb : CoefficientCombineRule.Average);
64
+
65
+ if (hasCollisionEvents) {
66
+ colliderDesc = colliderDesc.setActiveEvents(ActiveEvents.COLLISION_EVENTS);
67
+ } // If any of the mass properties are specified, add mass properties
68
+
64
69
 
65
70
  if (options !== null && options !== void 0 && options.mass || options !== null && options !== void 0 && options.centerOfMass || options !== null && options !== void 0 && options.principalAngularInertia) {
66
71
  colliderDesc.setDensity(0);
@@ -80,10 +85,10 @@ const createColliderFromOptions = (options, world, rigidBodyHandle, scale = {
80
85
  });
81
86
  }
82
87
 
83
- const collider = world.createCollider(colliderDesc, rigidBodyHandle);
88
+ const collider = world.createCollider(colliderDesc, rigidBody);
84
89
  return collider;
85
90
  };
86
- const createCollidersFromChildren = (object, rigidBody, type, world) => {
91
+ const createCollidersFromChildren = (object, rigidBody, type, world, hasCollisionEvents = false) => {
87
92
  const colliders = [];
88
93
  let desc;
89
94
  let offset = new Vector3();
@@ -156,7 +161,12 @@ const createCollidersFromChildren = (object, rigidBody, type, world) => {
156
161
  z: rz,
157
162
  w: rw
158
163
  });
159
- const collider = world.createCollider(desc, rigidBody.handle);
164
+
165
+ if (hasCollisionEvents) {
166
+ desc.setActiveEvents(ActiveEvents.COLLISION_EVENTS);
167
+ }
168
+
169
+ const collider = world.createCollider(desc, rigidBody);
160
170
  colliders.push(collider);
161
171
  }
162
172
  });
@@ -224,6 +234,31 @@ const createRigidBodyApi = ref => {
224
234
  w
225
235
  } = ref.current().rotation();
226
236
  return new Quaternion(x, y, z, w);
237
+ },
238
+
239
+ setNextKinematicRotation({
240
+ x,
241
+ y,
242
+ z
243
+ }) {
244
+ ref.current().setNextKinematicRotation({
245
+ x,
246
+ y,
247
+ z,
248
+ w: 1
249
+ });
250
+ },
251
+
252
+ setNextKinematicTranslation({
253
+ x,
254
+ y,
255
+ z
256
+ }) {
257
+ ref.current().setNextKinematicTranslation({
258
+ x,
259
+ y,
260
+ z
261
+ });
227
262
  }
228
263
 
229
264
  };
@@ -245,10 +280,10 @@ const createWorldApi = ref => {
245
280
  getCollider: handle => ref.current().getCollider(handle),
246
281
  getRigidBody: handle => ref.current().getRigidBody(handle),
247
282
  createRigidBody: desc => ref.current().createRigidBody(desc),
248
- createCollider: (desc, rigidBodyHandle) => ref.current().createCollider(desc, rigidBodyHandle),
283
+ createCollider: (desc, rigidBody) => ref.current().createCollider(desc, rigidBody),
249
284
  removeRigidBody: rigidBody => ref.current().removeRigidBody(rigidBody),
250
285
  removeCollider: collider => ref.current().removeCollider(collider, true),
251
- createImpulseJoint: (params, rigidBodyA, rigidBodyB) => ref.current().createImpulseJoint(params, rigidBodyA, rigidBodyB),
286
+ createImpulseJoint: (params, rigidBodyA, rigidBodyB) => ref.current().createImpulseJoint(params, rigidBodyA, rigidBodyB, true),
252
287
  removeImpulseJoint: joint => ref.current().removeImpulseJoint(joint, true),
253
288
  forEachCollider: callback => ref.current().forEachCollider(callback)
254
289
  };
@@ -291,9 +326,11 @@ const Physics = ({
291
326
  return worldRef.current;
292
327
  });
293
328
  const [colliderMeshes] = useState(() => new Map());
294
- const [rigidBodyMeshes] = useState(() => new Map()); // Init world
329
+ const [rigidBodyMeshes] = useState(() => new Map());
330
+ const [rigidBodyEvents] = useState(() => new Map());
331
+ const [eventQueue] = useState(() => new EventQueue(false)); // Init world
295
332
 
296
- useLayoutEffect(() => {
333
+ useEffect(() => {
297
334
  const world = getWorldRef.current();
298
335
  return () => {
299
336
  if (world) {
@@ -311,10 +348,27 @@ const Physics = ({
311
348
  const now = performance.now();
312
349
  const delta = Math.min(100, now - time.current);
313
350
  world.timestep = delta / 1000;
314
- world.step(); // Update meshes
351
+ world.step(eventQueue); // Update meshes
315
352
 
316
353
  rigidBodyMeshes.forEach((mesh, handle) => {
317
354
  const rigidBody = world.getRigidBody(handle);
355
+ const events = rigidBodyEvents.get(handle);
356
+
357
+ if (events !== null && events !== void 0 && events.onSleep || events !== null && events !== void 0 && events.onWake) {
358
+ if (rigidBody.isSleeping() && !mesh.userData.isSleeping) {
359
+ var _events$onSleep;
360
+
361
+ events === null || events === void 0 ? void 0 : (_events$onSleep = events.onSleep) === null || _events$onSleep === void 0 ? void 0 : _events$onSleep.call(events);
362
+ }
363
+
364
+ if (!rigidBody.isSleeping() && mesh.userData.isSleeping) {
365
+ var _events$onWake;
366
+
367
+ events === null || events === void 0 ? void 0 : (_events$onWake = events.onWake) === null || _events$onWake === void 0 ? void 0 : _events$onWake.call(events);
368
+ }
369
+
370
+ mesh.userData.isSleeping = rigidBody.isSleeping();
371
+ }
318
372
 
319
373
  if (!rigidBody || rigidBody.isSleeping() || rigidBody.isFixed() || !mesh.parent) {
320
374
  return;
@@ -342,6 +396,50 @@ const Physics = ({
342
396
  o.updateMatrix();
343
397
  mesh.position.setFromMatrixPosition(o.matrix);
344
398
  mesh.rotation.setFromRotationMatrix(o.matrix);
399
+ }); // Collision events
400
+
401
+ eventQueue.drainCollisionEvents((handle1, handle2, started) => {
402
+ var _collider1$parent, _collider2$parent;
403
+
404
+ const collider1 = world.getCollider(handle1);
405
+ const collider2 = world.getCollider(handle2);
406
+ const rigidBodyHandle1 = (_collider1$parent = collider1.parent()) === null || _collider1$parent === void 0 ? void 0 : _collider1$parent.handle;
407
+ const rigidBodyHandle2 = (_collider2$parent = collider2.parent()) === null || _collider2$parent === void 0 ? void 0 : _collider2$parent.handle;
408
+
409
+ if (!collider1 || !collider2 || !rigidBodyHandle1 || !rigidBodyHandle2) {
410
+ return;
411
+ }
412
+
413
+ const rigidBody1 = world.getRigidBody(rigidBodyHandle1);
414
+ const rigidBody2 = world.getRigidBody(rigidBodyHandle2);
415
+ const events1 = rigidBodyEvents.get(rigidBodyHandle1);
416
+ const events2 = rigidBodyEvents.get(rigidBodyHandle2);
417
+
418
+ if (started) {
419
+ world.contactPair(collider1, collider2, (manifold, flipped) => {
420
+ var _events1$onCollisionE, _events2$onCollisionE;
421
+
422
+ events1 === null || events1 === void 0 ? void 0 : (_events1$onCollisionE = events1.onCollisionEnter) === null || _events1$onCollisionE === void 0 ? void 0 : _events1$onCollisionE.call(events1, {
423
+ target: rigidBody2,
424
+ manifold,
425
+ flipped
426
+ });
427
+ events2 === null || events2 === void 0 ? void 0 : (_events2$onCollisionE = events2.onCollisionEnter) === null || _events2$onCollisionE === void 0 ? void 0 : _events2$onCollisionE.call(events2, {
428
+ target: rigidBody1,
429
+ manifold,
430
+ flipped
431
+ });
432
+ });
433
+ } else {
434
+ var _events1$onCollisionE2, _events2$onCollisionE2;
435
+
436
+ events1 === null || events1 === void 0 ? void 0 : (_events1$onCollisionE2 = events1.onCollisionExit) === null || _events1$onCollisionE2 === void 0 ? void 0 : _events1$onCollisionE2.call(events1, {
437
+ target: rigidBody2
438
+ });
439
+ events2 === null || events2 === void 0 ? void 0 : (_events2$onCollisionE2 = events2.onCollisionExit) === null || _events2$onCollisionE2 === void 0 ? void 0 : _events2$onCollisionE2.call(events2, {
440
+ target: rigidBody1
441
+ });
442
+ }
345
443
  });
346
444
  time.current = now;
347
445
  });
@@ -354,7 +452,8 @@ const Physics = ({
354
452
  gravity: _gravity
355
453
  },
356
454
  colliderMeshes,
357
- rigidBodyMeshes
455
+ rigidBodyMeshes,
456
+ rigidBodyEvents
358
457
  }), []);
359
458
  return /*#__PURE__*/React.createElement(RapierContext.Provider, {
360
459
  value: context
@@ -410,7 +509,8 @@ const useRigidBody = (options = {}) => {
410
509
  rapier,
411
510
  world,
412
511
  rigidBodyMeshes,
413
- physicsOptions
512
+ physicsOptions,
513
+ rigidBodyEvents
414
514
  } = useRapier();
415
515
  const ref = useRef(); // Create rigidbody
416
516
 
@@ -431,7 +531,7 @@ const useRigidBody = (options = {}) => {
431
531
  z: avz
432
532
  }).setGravityScale(gravityScale).setCanSleep(canSleep).setCcdEnabled(ccdEnabled);
433
533
  const rigidBody = world.createRigidBody(desc);
434
- rigidBodyRef.current = rigidBody;
534
+ rigidBodyRef.current = world.getRigidBody(rigidBody.handle);
435
535
  }
436
536
 
437
537
  return rigidBodyRef.current;
@@ -445,8 +545,10 @@ const useRigidBody = (options = {}) => {
445
545
 
446
546
  if (!ref.current) {
447
547
  ref.current = new Object3D();
448
- } // Get intitial world transforms
548
+ } // isSleeping used for onSleep and onWake events
549
+
449
550
 
551
+ ref.current.userData.isSleeping = false; // Get intitial world transforms
450
552
 
451
553
  const worldPosition = ref.current.getWorldPosition(new Vector3());
452
554
  const worldRotation = ref.current.getWorldQuaternion(new Quaternion());
@@ -464,6 +566,7 @@ const useRigidBody = (options = {}) => {
464
566
  y: worldPosition.y + y * scale.y,
465
567
  z: worldPosition.z + z * scale.z
466
568
  }, false);
569
+ console.log(rigidBody.isKinematic());
467
570
  const eulerAngles = new Euler(rx, ry, rz, 'XYZ');
468
571
  const rotation = new Quaternion().setFromEuler(eulerAngles).multiply(worldRotation);
469
572
  rigidBody.setRotation({
@@ -475,16 +578,31 @@ const useRigidBody = (options = {}) => {
475
578
  rigidBody.resetForces(false);
476
579
  rigidBody.resetTorques(false);
477
580
  const colliderSetting = (_ref = (_options$colliders = options === null || options === void 0 ? void 0 : options.colliders) !== null && _options$colliders !== void 0 ? _options$colliders : physicsOptions.colliders) !== null && _ref !== void 0 ? _ref : false;
478
- const autoColliders = colliderSetting !== false ? createCollidersFromChildren(ref.current, rigidBody, colliderSetting, world) : [];
581
+ const hasCollisionEvents = !!(options.onCollisionEnter || options.onCollisionExit);
582
+ const autoColliders = colliderSetting !== false ? createCollidersFromChildren(ref.current, rigidBody, colliderSetting, world, hasCollisionEvents) : [];
479
583
  rigidBody.wakeUp();
480
584
  rigidBodyMeshes.set(rigidBody.handle, ref.current);
481
585
  return () => {
586
+ const actualBody = world.getRigidBody(rigidBody.handle);
587
+ world.removeRigidBody(actualBody);
482
588
  autoColliders.forEach(collider => world.removeCollider(collider));
483
- world.removeRigidBody(rigidBody);
484
589
  rigidBodyRef.current = undefined;
485
590
  rigidBodyMeshes.delete(rigidBody.handle);
486
591
  };
487
- }, []);
592
+ }, []); // Events
593
+
594
+ useEffect(() => {
595
+ const rigidBody = getRigidBodyRef.current();
596
+ rigidBodyEvents.set(rigidBody.handle, {
597
+ onCollisionEnter: options === null || options === void 0 ? void 0 : options.onCollisionEnter,
598
+ onCollisionExit: options === null || options === void 0 ? void 0 : options.onCollisionExit,
599
+ onSleep: options === null || options === void 0 ? void 0 : options.onSleep,
600
+ onWake: options === null || options === void 0 ? void 0 : options.onWake
601
+ });
602
+ return () => {
603
+ rigidBodyEvents.delete(rigidBody.handle);
604
+ };
605
+ }, [options.onCollisionEnter, options.onCollisionExit]);
488
606
  const api = useMemo(() => createRigidBodyApi(getRigidBodyRef), []);
489
607
  return [ref, api];
490
608
  };
@@ -496,7 +614,7 @@ const useCollider = (body, options = {}) => {
496
614
  const objectRef = useRef();
497
615
  const getColliderRef = useRef(() => {
498
616
  if (!colliderRef.current) {
499
- colliderRef.current = createColliderFromOptions(options, world, body.handle);
617
+ colliderRef.current = createColliderFromOptions(options, world, world.getRigidBody(body.handle));
500
618
  }
501
619
 
502
620
  return colliderRef.current;
@@ -522,7 +640,7 @@ const useRigidBodyWithCollider = (rigidBodyOptions, colliderOptions) => {
522
640
  }
523
641
 
524
642
  const scale = ref.current.getWorldScale(new Vector3());
525
- const collider = createColliderFromOptions(colliderOptions, world, rigidBody.handle, scale);
643
+ const collider = createColliderFromOptions(colliderOptions, world, world.getRigidBody(rigidBody.handle), scale);
526
644
  return () => {
527
645
  world.removeCollider(collider);
528
646
  };
@@ -678,7 +796,6 @@ const useImpulseJoint = (body1, body2, params) => {
678
796
  const joint = getJointRef.current();
679
797
  return () => {
680
798
  if (joint) {
681
- console.log('remove joint', joint);
682
799
  world.removeImpulseJoint(joint);
683
800
  jointRef.current = undefined;
684
801
  }
@@ -798,7 +915,7 @@ const _excluded = ["children"],
798
915
  _excluded2 = ["children"];
799
916
  const RigidBodyContext = /*#__PURE__*/createContext(undefined);
800
917
 
801
- const useParentRigidBody = () => useContext(RigidBodyContext); // RigidBody
918
+ const useRigidBodyContext = () => useContext(RigidBodyContext); // RigidBody
802
919
 
803
920
 
804
921
  const RigidBody = /*#__PURE__*/forwardRef((_ref, ref) => {
@@ -810,7 +927,7 @@ const RigidBody = /*#__PURE__*/forwardRef((_ref, ref) => {
810
927
  const [object, rigidBody] = useRigidBody(props);
811
928
  useImperativeHandle(ref, () => rigidBody);
812
929
  return /*#__PURE__*/React.createElement(RigidBodyContext.Provider, {
813
- value: [object, rigidBody]
930
+ value: [object, rigidBody, !!(props.onCollisionEnter || props.onCollisionExit)]
814
931
  }, /*#__PURE__*/React.createElement("object3D", {
815
932
  ref: object
816
933
  }, children));
@@ -825,11 +942,11 @@ const AnyCollider = _ref2 => {
825
942
  const {
826
943
  world
827
944
  } = useRapier();
828
- const [, rigidBody] = useParentRigidBody();
945
+ const [, rigidBody, hasCollisionEvents] = useRigidBodyContext();
829
946
  const ref = useRef(null);
830
947
  useEffect(() => {
831
948
  const scale = ref.current.getWorldScale(new Vector3());
832
- const collider = createColliderFromOptions(props, world, rigidBody.handle, scale);
949
+ const collider = createColliderFromOptions(props, world, world.getRigidBody(rigidBody.handle), scale, hasCollisionEvents);
833
950
  return () => {
834
951
  world.removeCollider(collider);
835
952
  };
@@ -886,20 +1003,20 @@ const ConvexHullCollider = props => {
886
1003
  };
887
1004
 
888
1005
  const geometryFromCollider = collider => {
889
- switch (collider.shapeType()) {
1006
+ switch (collider.shape.type) {
890
1007
  case ShapeType.Cuboid:
891
1008
  {
892
1009
  const {
893
1010
  x,
894
1011
  y,
895
1012
  z
896
- } = collider.halfExtents();
1013
+ } = collider.shape.halfExtents;
897
1014
  return new BoxBufferGeometry(x * 2 + 0.01, y * 2 + 0.01, z * 2 + 0.01);
898
1015
  }
899
1016
 
900
1017
  case ShapeType.Ball:
901
1018
  {
902
- const r = collider.radius();
1019
+ const r = collider.shape.radius;
903
1020
  return new SphereBufferGeometry(r + +0.01, 8, 8);
904
1021
  }
905
1022
 
@@ -907,10 +1024,12 @@ const geometryFromCollider = collider => {
907
1024
  {
908
1025
  var _g$index;
909
1026
 
910
- const v = collider.vertices();
911
- const i = collider.indices();
912
- const g = new BufferGeometry();
913
- g.setAttribute("position", new BufferAttribute(v, 3));
1027
+ const v = collider.shape.vertices;
1028
+ const i = collider.shape.indices;
1029
+ const g = new BufferGeometry(); // Vertices are not always a float3darray (???), so we need to convert them
1030
+
1031
+ const safeVerts = Float32Array.from(v);
1032
+ g.setAttribute("position", new BufferAttribute(safeVerts, 3));
914
1033
  (_g$index = g.index) === null || _g$index === void 0 ? void 0 : _g$index.set(i);
915
1034
  g.setDrawRange(0, g.attributes.position.array.length / 3 - 1);
916
1035
  return g;
@@ -918,16 +1037,18 @@ const geometryFromCollider = collider => {
918
1037
 
919
1038
  case ShapeType.ConvexPolyhedron:
920
1039
  {
921
- const cv = collider.vertices();
1040
+ const cv = collider.shape.vertices; // Vertices are not always a float3darray (???), so we need to convert them
1041
+
1042
+ const safeVerts = Float32Array.from(cv);
922
1043
  const cg = new BufferGeometry();
923
- cg.setAttribute("position", new BufferAttribute(cv, 3));
1044
+ cg.setAttribute("position", new BufferAttribute(safeVerts, 3));
924
1045
  return cg;
925
1046
  }
926
1047
 
927
1048
  case ShapeType.Cylinder:
928
1049
  {
929
- const r = collider.radius();
930
- const h = collider.halfHeight();
1050
+ const r = collider.shape.radius;
1051
+ const h = collider.shape.halfHeight;
931
1052
  const g = new CylinderBufferGeometry(r, r, h);
932
1053
  return g;
933
1054
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-three/rapier",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "source": "src/index.ts",
5
5
  "main": "dist/react-three-rapier.cjs.js",
6
6
  "module": "dist/react-three-rapier.esm.js",
@@ -23,7 +23,7 @@
23
23
  "three": "^0.139.2"
24
24
  },
25
25
  "dependencies": {
26
- "@dimforge/rapier3d-compat": "^0.8.0-alpha.2",
26
+ "@dimforge/rapier3d-compat": "0.8.1",
27
27
  "use-asset": "^1.0.4"
28
28
  },
29
29
  "repository": "https://github.com/pmndrs/react-three-rapier/tree/master/packages/react-three-rapier"
package/readme.md ADDED
@@ -0,0 +1,176 @@
1
+ <h1 align="center">@react-three/rapier 🗡</h1>
2
+
3
+ <p align="center">⚠️ Under heavy development. All APIs are subject to change. ⚠️</p>
4
+
5
+ ## Usage
6
+
7
+ ```tsx
8
+ import { Box } from "@react-three/drei";
9
+ import { Canvas } from "@react-three/fiber";
10
+ import { Physics, RigidBody } from "@react-three/rapier";
11
+
12
+ const App = () => {
13
+ return (
14
+ <Canvas>
15
+ <Physics>
16
+ <RigidBody>
17
+ <Box />
18
+ </RigidBody>
19
+ </Physics>
20
+ </Canvas>
21
+ );
22
+ };
23
+ ```
24
+
25
+ ## Automatic colliders
26
+
27
+ RigidBodies generate automatic colliders by default for all meshes that it contains. You can control the default collider by setting the `colliders` prop on a `<RigidBody />`, or change it globally by setting `colliders` on `<Physics />`. Setting `colliders={false}` disables auto-generation.
28
+
29
+ Supported values:
30
+
31
+ - `"cuboid"`, creates a CuboidCollider based on the bounding box of the mesh
32
+ - `"ball"`, creates a SphereCollider based on the bounding sphere of the mesh
33
+ - `"trimesh"`, creates a TrimeshCollider based on the mesh's geometry -- note trimeshes are massless by default (https://rapier.rs/docs/user_guides/javascript/common_mistakes#rigid-body-isnt-affected-by-gravity)
34
+ - `"hull"`, creates a ConvexHullCollider based on the mesh's geometry
35
+ - `false`, disables auto-generation
36
+
37
+ Generate ConvexHull colliders for all meshes in a RigidBody by default:
38
+
39
+ ```tsx
40
+ const Scene = () => (
41
+ <Physics colliders="hull">
42
+ <RigidBody>
43
+ <Box />
44
+ </RigidBody>
45
+ <RigidBody position={[0, 10, 0]}>
46
+ <Sphere />
47
+ </RigidBody>
48
+ </Physics>
49
+ );
50
+ ```
51
+
52
+ Turn off automatic collider generation globally, but apply auto generation locally:
53
+
54
+ ```tsx
55
+ const Scene = () => (
56
+ <Physics colliders={false}>
57
+ <RigidBody colliders="cuboid">
58
+ <Box />
59
+ </RigidBody>
60
+
61
+ <RigidBody position={[0, 10, 0]} colliders="ball">
62
+ <Sphere />
63
+ </RigidBody>
64
+
65
+ <RigidBody position={[0, 10, 0]}>
66
+ <Sphere />
67
+ <BallCollider args={0.5} />
68
+ <BallCollider args={0.5} position={[1, 0, 0]} />
69
+ </RigidBody>
70
+ </Physics>
71
+ );
72
+ ```
73
+
74
+ Objects work inside other transformed objects as well. Simulation runs in world space and is transformed to the objects local space, so that things act as you'd expect.
75
+
76
+ ```tsx
77
+ import { Box } from "@react-three/drei";
78
+ import { RigidBody, CuboidCollider } from "@react-three/rapier";
79
+
80
+ const Scene = () => {
81
+ return (
82
+ <group position={[2, 5, 0]} rotation={[0, 0.3, 2]}>
83
+ <RigidBody>
84
+ <Box />
85
+ <CuboidCollider args={[0.5, 0.5, 0.5]} />
86
+ </RigidBody>
87
+ </group>
88
+ );
89
+ };
90
+ ```
91
+
92
+ ## Debug
93
+
94
+ Use the Debug component to see live representations of all colliders in a scene.
95
+
96
+ > Note: Experimental. Not all shapes are supported. Unsupported shapes are always represented by cubes.
97
+
98
+ ```tsx
99
+ import { Box, Sphere } from "@react-three/drei";
100
+ import { RigidBody, Debug } from "@react-three/rapier";
101
+
102
+ const Scene = () => {
103
+ return (
104
+ <Physics>
105
+ <Debug />
106
+
107
+ <RigidBody>
108
+ <Box />
109
+ </RigidBody>
110
+ <RigidBody>
111
+ <Sphere />
112
+ </RigidBody>
113
+ </Physics>
114
+ );
115
+ };
116
+ ```
117
+
118
+ ## Events
119
+
120
+ You can subscribe collision and state events on the RigidBody.
121
+
122
+ ```tsx
123
+ const RigidBottle = () => {
124
+ const [isAsleep, setIsAsleep] = useState(false);
125
+
126
+ return (
127
+ <RigidBody
128
+ colliders="hull"
129
+ onSleep={() => setIsAsleep(true)}
130
+ onWake={() => setIsAsleep(false)}
131
+ onCollision={({manifold}) => {
132
+ console.log('Collision at world position ', manifold.solverContactPoint(0))
133
+ }}
134
+ >
135
+ <Sphere>
136
+ <meshPhysicalMaterial color={isAsleep ? 'white' : 'blue'}>
137
+ </Sphere>
138
+ </RigidBody>
139
+ )
140
+ }
141
+ ```
142
+
143
+ ## Hooks
144
+
145
+ You can also use hooks to generate rigid bodies and colliders, but it's not encouraged.
146
+
147
+ ```tsx
148
+ import { Box } from "@react-three/drei";
149
+ import { useCuboid } from "@react-three/rapier";
150
+
151
+ const RigidBox = () => {
152
+ // Generates a RigidBody and attaches a BoxCollider to it, returns a ref
153
+ const [box, rigidBody, collider] = useCuboid(
154
+ { position: [1, 1, 1] },
155
+ { args: [0.5, 0.5, 0.5] }
156
+ );
157
+
158
+ return <Box ref={box} />;
159
+ };
160
+ ```
161
+
162
+ ## Roadmap?
163
+
164
+ In order, but also not necessarily:
165
+
166
+ - [x] Draft of all base shapes
167
+ - [x] Draft of all base joints
168
+ - [x] Nested objects retain world transforms
169
+ - [x] Nested objects retain correct collider scale
170
+ - [x] Automatic colliders based on rigidbody children
171
+ - [ ] Translation and rotational constraints
172
+ - [x] Collision events
173
+ - [ ] InstancedMesh support
174
+ - [ ] Docs
175
+ - [ ] CodeSandbox examples
176
+ - [ ] Helpers, for things like Vehicle, Rope, Player, etc