@react-three/rapier 0.12.2 → 0.13.1

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.
Files changed (26) hide show
  1. package/dist/declarations/src/{AnyCollider.d.ts → components/AnyCollider.d.ts} +51 -11
  2. package/dist/declarations/src/{Attractor.d.ts → components/Attractor.d.ts} +0 -0
  3. package/dist/declarations/src/{Debug.d.ts → components/Debug.d.ts} +0 -0
  4. package/dist/declarations/src/components/InstancedRigidBodies.d.ts +12 -0
  5. package/dist/declarations/src/{MeshCollider.d.ts → components/MeshCollider.d.ts} +5 -1
  6. package/dist/declarations/src/{Physics.d.ts → components/Physics.d.ts} +10 -3
  7. package/dist/declarations/src/{RigidBody.d.ts → components/RigidBody.d.ts} +10 -8
  8. package/dist/declarations/src/{hooks.d.ts → hooks/hooks.d.ts} +17 -5
  9. package/dist/declarations/src/{joints.d.ts → hooks/joints.d.ts} +19 -8
  10. package/dist/declarations/src/hooks/use-imperative-instance.d.ts +4 -0
  11. package/dist/declarations/src/index.d.ts +18 -17
  12. package/dist/declarations/src/types.d.ts +10 -10
  13. package/dist/declarations/src/utils/api.d.ts +18 -0
  14. package/dist/declarations/src/{interaction-groups.d.ts → utils/interaction-groups.d.ts} +0 -0
  15. package/dist/declarations/src/{shared-objects.d.ts → utils/shared-objects.d.ts} +0 -0
  16. package/dist/declarations/src/utils/three-object-helpers.d.ts +29 -0
  17. package/dist/declarations/src/{utils-collider.d.ts → utils/utils-collider.d.ts} +6 -7
  18. package/dist/declarations/src/{utils-rigidbody.d.ts → utils/utils-rigidbody.d.ts} +6 -6
  19. package/dist/declarations/src/{utils.d.ts → utils/utils.d.ts} +2 -2
  20. package/dist/react-three-rapier.cjs.dev.js +546 -589
  21. package/dist/react-three-rapier.cjs.prod.js +546 -589
  22. package/dist/react-three-rapier.esm.js +547 -593
  23. package/package.json +12 -12
  24. package/readme.md +165 -52
  25. package/dist/declarations/src/InstancedRigidBodies.d.ts +0 -10
  26. package/dist/declarations/src/api.d.ts +0 -179
@@ -1,8 +1,8 @@
1
- import { ColliderDesc, ActiveEvents, RigidBodyDesc, EventQueue } from '@dimforge/rapier3d-compat';
1
+ import { ActiveEvents, ColliderDesc, EventQueue, RigidBodyDesc } from '@dimforge/rapier3d-compat';
2
2
  export { CoefficientCombineRule, Collider as RapierCollider, RigidBody as RapierRigidBody } from '@dimforge/rapier3d-compat';
3
3
  import { useFrame, useThree } from '@react-three/fiber';
4
- import React, { useRef, useMemo, useEffect, useContext, useState, memo, createContext, useCallback, forwardRef, useImperativeHandle, useLayoutEffect } from 'react';
5
- import { Quaternion, Euler, Vector3, Object3D, Matrix4, MathUtils, InstancedMesh, BufferAttribute, DynamicDrawUsage } from 'three';
4
+ import React, { useRef, useMemo, useEffect, useContext, useState, memo, createContext, useCallback, forwardRef, useImperativeHandle, Fragment } from 'react';
5
+ import { Quaternion, Euler, Vector3, Object3D, Matrix4, MathUtils, BufferAttribute, DynamicDrawUsage } from 'three';
6
6
  import { useAsset } from 'use-asset';
7
7
  import { mergeVertices, VertexNormalsHelper } from 'three-stdlib';
8
8
 
@@ -47,10 +47,44 @@ function _objectSpread2(target) {
47
47
  return target;
48
48
  }
49
49
 
50
+ const createWorldApi = ref => {
51
+ return {
52
+ raw: () => ref.current(),
53
+ getCollider: handle => ref.current().getCollider(handle),
54
+ getRigidBody: handle => ref.current().getRigidBody(handle),
55
+ createRigidBody: desc => ref.current().createRigidBody(desc),
56
+ createCollider: (desc, rigidBody) => ref.current().createCollider(desc, rigidBody),
57
+ removeRigidBody: rigidBody => {
58
+ if (!ref.current().bodies.contains(rigidBody.handle)) return;
59
+ ref.current().removeRigidBody(rigidBody);
60
+ },
61
+ removeCollider: (collider, wakeUp = true) => {
62
+ if (!ref.current().colliders.contains(collider.handle)) return;
63
+ ref.current().removeCollider(collider, wakeUp);
64
+ },
65
+ createImpulseJoint: (params, rigidBodyA, rigidBodyB, wakeUp = true) => ref.current().createImpulseJoint(params, rigidBodyA, rigidBodyB, wakeUp),
66
+ removeImpulseJoint: (joint, wakeUp = true) => {
67
+ if (!ref.current().impulseJoints.contains(joint.handle)) return;
68
+ ref.current().removeImpulseJoint(joint, wakeUp);
69
+ },
70
+ forEachCollider: callback => ref.current().forEachCollider(callback),
71
+ setGravity: ({
72
+ x,
73
+ y,
74
+ z
75
+ }) => ref.current().gravity = {
76
+ x,
77
+ y,
78
+ z
79
+ },
80
+ debugRender: () => ref.current().debugRender()
81
+ };
82
+ };
83
+
50
84
  const _quaternion = new Quaternion();
51
85
  new Euler();
52
86
  const _vector3 = new Vector3();
53
- const _object3d = new Object3D();
87
+ new Object3D();
54
88
  const _matrix4 = new Matrix4();
55
89
  const _position = new Vector3();
56
90
  const _rotation = new Quaternion();
@@ -66,11 +100,6 @@ const tupleToObject = (tuple, keys) => {
66
100
  return obj;
67
101
  }, {});
68
102
  };
69
- const rapierVector3ToVector3 = ({
70
- x,
71
- y,
72
- z
73
- }) => _vector3.set(x, y, z);
74
103
  const rapierQuaternionToQuaternion = ({
75
104
  x,
76
105
  y,
@@ -124,152 +153,6 @@ function useConst(initialValue) {
124
153
  return ref.current.value;
125
154
  }
126
155
 
127
- const createRigidBodyApi = ref => {
128
- return {
129
- raw: () => ref.current(),
130
-
131
- get handle() {
132
- return ref.current().handle;
133
- },
134
-
135
- mass: () => ref.current().mass(),
136
-
137
- applyImpulse(impulseVector, wakeUp = true) {
138
- ref.current().applyImpulse(impulseVector, wakeUp);
139
- },
140
-
141
- applyTorqueImpulse(torqueVector, wakeUp = true) {
142
- ref.current().applyTorqueImpulse(torqueVector, wakeUp);
143
- },
144
-
145
- applyImpulseAtPoint: (impulseVector, impulsePoint, wakeUp = true) => ref.current().applyImpulseAtPoint(impulseVector, impulsePoint, wakeUp),
146
- addForce: (force, wakeUp = true) => ref.current().addForce(force, wakeUp),
147
- addForceAtPoint: (force, point, wakeUp = true) => ref.current().addForceAtPoint(force, point, wakeUp),
148
- addTorque: (torque, wakeUp = true) => ref.current().addTorque(torque, wakeUp),
149
-
150
- translation() {
151
- return rapierVector3ToVector3(ref.current().translation());
152
- },
153
-
154
- setTranslation: (translation, wakeUp = true) => ref.current().setTranslation(translation, wakeUp),
155
-
156
- rotation() {
157
- const {
158
- x,
159
- y,
160
- z,
161
- w
162
- } = ref.current().rotation();
163
- return new Quaternion(x, y, z, w);
164
- },
165
-
166
- setRotation: (rotation, wakeUp = true) => {
167
- ref.current().setRotation(rotation, wakeUp);
168
- },
169
-
170
- linvel() {
171
- const {
172
- x,
173
- y,
174
- z
175
- } = ref.current().linvel();
176
- return new Vector3(x, y, z);
177
- },
178
-
179
- setLinvel: (velocity, wakeUp = true) => ref.current().setLinvel(velocity, wakeUp),
180
-
181
- angvel() {
182
- const {
183
- x,
184
- y,
185
- z
186
- } = ref.current().angvel();
187
- return new Vector3(x, y, z);
188
- },
189
-
190
- setAngvel: (velocity, wakeUp = true) => ref.current().setAngvel(velocity, wakeUp),
191
-
192
- linearDamping() {
193
- return ref.current().linearDamping();
194
- },
195
-
196
- setLinearDamping: factor => ref.current().setLinearDamping(factor),
197
-
198
- angularDamping() {
199
- return ref.current().angularDamping();
200
- },
201
-
202
- setAngularDamping: factor => ref.current().setAngularDamping(factor),
203
- setNextKinematicRotation: rotation => {
204
- ref.current().setNextKinematicRotation(rotation);
205
- },
206
- setNextKinematicTranslation: translation => ref.current().setNextKinematicTranslation(translation),
207
- resetForces: (wakeUp = true) => ref.current().resetForces(wakeUp),
208
- resetTorques: (wakeUp = true) => ref.current().resetTorques(wakeUp),
209
- lockRotations: (locked, wakeUp = true) => ref.current().lockRotations(locked, wakeUp),
210
- lockTranslations: (locked, wakeUp = true) => ref.current().lockTranslations(locked, wakeUp),
211
- setEnabledRotations: (x, y, z, wakeUp = true) => ref.current().setEnabledRotations(x, y, z, wakeUp),
212
- setEnabledTranslations: (x, y, z, wakeUp = true) => ref.current().setEnabledTranslations(x, y, z, wakeUp)
213
- };
214
- };
215
- const createInstancedRigidBodiesApi = bodiesGetter => ({
216
- at: index => bodiesGetter.current()[index].api,
217
-
218
- forEach(callback) {
219
- return bodiesGetter.current().map(b => b.api).forEach(callback);
220
- },
221
-
222
- get count() {
223
- return bodiesGetter.current().length;
224
- }
225
-
226
- });
227
- const createWorldApi = ref => {
228
- return {
229
- raw: () => ref.current(),
230
- getCollider: handle => ref.current().getCollider(handle),
231
- getRigidBody: handle => ref.current().getRigidBody(handle),
232
- createRigidBody: desc => ref.current().createRigidBody(desc),
233
- createCollider: (desc, rigidBody) => ref.current().createCollider(desc, rigidBody),
234
- removeRigidBody: rigidBody => {
235
- if (!ref.current().bodies.contains(rigidBody.handle)) return;
236
- ref.current().removeRigidBody(rigidBody);
237
- },
238
- removeCollider: (collider, wakeUp = true) => {
239
- if (!ref.current().colliders.contains(collider.handle)) return;
240
- ref.current().removeCollider(collider, wakeUp);
241
- },
242
- createImpulseJoint: (params, rigidBodyA, rigidBodyB, wakeUp = true) => ref.current().createImpulseJoint(params, rigidBodyA, rigidBodyB, wakeUp),
243
- removeImpulseJoint: (joint, wakeUp = true) => {
244
- if (!ref.current().impulseJoints.contains(joint.handle)) return;
245
- ref.current().removeImpulseJoint(joint, wakeUp);
246
- },
247
- forEachCollider: callback => ref.current().forEachCollider(callback),
248
- setGravity: ({
249
- x,
250
- y,
251
- z
252
- }) => ref.current().gravity = {
253
- x,
254
- y,
255
- z
256
- },
257
- debugRender: () => ref.current().debugRender()
258
- };
259
- };
260
- const createJointApi = ref => {
261
- return {
262
- raw: () => ref.current(),
263
-
264
- get handle() {
265
- return ref.current().handle;
266
- },
267
-
268
- configureMotorPosition: (targetPos, stiffness, damping) => ref.current().configureMotorPosition(targetPos, stiffness, damping),
269
- configureMotorVelocity: (targetVel, damping) => ref.current().configureMotorVelocity(targetVel, damping)
270
- };
271
- };
272
-
273
156
  const scaleColliderArgs = (shape, args, scale) => {
274
157
  const newArgs = args.slice(); // Heightfield uses a vector
275
158
 
@@ -291,11 +174,11 @@ const scaleColliderArgs = (shape, args, scale) => {
291
174
  const scaleArray = [scale.x, scale.y, scale.z, scale.x, scale.x];
292
175
  return newArgs.map((arg, index) => scaleArray[index] * arg);
293
176
  };
294
- const createColliderFromOptions = (options, world, scale, rigidBody) => {
177
+ const createColliderFromOptions = (options, world, scale, getRigidBody) => {
295
178
  const scaledArgs = scaleColliderArgs(options.shape, options.args, scale); // @ts-ignore
296
179
 
297
180
  const desc = ColliderDesc[options.shape](...scaledArgs);
298
- return world.createCollider(desc, rigidBody);
181
+ return world.createCollider(desc, getRigidBody === null || getRigidBody === void 0 ? void 0 : getRigidBody());
299
182
  };
300
183
  const massPropertiesConflictError = "Please pick ONLY ONE of the `density`, `mass` and `massProperties` options.";
301
184
 
@@ -399,15 +282,14 @@ const setColliderOptions = (collider, options, states) => {
399
282
  setColliderMassOptions(collider, options);
400
283
  }
401
284
  };
402
- const useUpdateColliderOptions = (collidersRef, props, states) => {
285
+ const useUpdateColliderOptions = (getCollider, props, states) => {
403
286
  // TODO: Improve this, split each prop into its own effect
404
287
  const mutablePropsAsFlatArray = useMemo(() => mutableColliderOptionKeys.flatMap(key => {
405
288
  return vectorToTuple(props[key]);
406
289
  }), [props]);
407
290
  useEffect(() => {
408
- collidersRef.current.forEach(collider => {
409
- setColliderOptions(collider, props, states);
410
- });
291
+ const collider = getCollider();
292
+ setColliderOptions(collider, props, states);
411
293
  }, mutablePropsAsFlatArray);
412
294
  };
413
295
 
@@ -530,7 +412,7 @@ const getColliderArgsFromGeometry = (geometry, colliders) => {
530
412
  offset: new Vector3()
531
413
  };
532
414
  };
533
- const useColliderEvents = (collidersRef, props, events) => {
415
+ const useColliderEvents = (getCollider, props, events) => {
534
416
  const {
535
417
  onCollisionEnter,
536
418
  onCollisionExit,
@@ -539,9 +421,9 @@ const useColliderEvents = (collidersRef, props, events) => {
539
421
  onContactForce
540
422
  } = props;
541
423
  useEffect(() => {
542
- var _collidersRef$current;
424
+ const collider = getCollider();
543
425
 
544
- (_collidersRef$current = collidersRef.current) === null || _collidersRef$current === void 0 ? void 0 : _collidersRef$current.forEach(collider => {
426
+ if (collider) {
545
427
  const hasCollisionEvent = !!(onCollisionEnter || onCollisionExit || onIntersectionEnter || onIntersectionExit);
546
428
  const hasContactForceEvent = !!onContactForce;
547
429
 
@@ -560,265 +442,87 @@ const useColliderEvents = (collidersRef, props, events) => {
560
442
  onIntersectionExit,
561
443
  onContactForce
562
444
  });
563
- });
564
- return () => {
565
- var _collidersRef$current2;
445
+ }
566
446
 
567
- (_collidersRef$current2 = collidersRef.current) === null || _collidersRef$current2 === void 0 ? void 0 : _collidersRef$current2.forEach(collider => events.delete(collider.handle));
447
+ return () => {
448
+ if (collider) {
449
+ events.delete(collider.handle);
450
+ }
568
451
  };
569
452
  }, [onCollisionEnter, onCollisionExit, onIntersectionEnter, onIntersectionExit, onContactForce]);
570
453
  };
571
454
 
572
- const rigidBodyDescFromOptions = options => {
573
- var _options$canSleep;
455
+ const useMutableCallback = fn => {
456
+ const ref = useRef(fn);
457
+ useEffect(() => {
458
+ ref.current = fn;
459
+ }, [fn]);
460
+ return ref;
461
+ }; // External hooks
574
462
 
575
- const type = rigidBodyTypeFromString((options === null || options === void 0 ? void 0 : options.type) || "dynamic");
576
- const desc = new RigidBodyDesc(type); // Apply immutable options
463
+ /**
464
+ * Exposes the Rapier context, and world
465
+ * @category Hooks
466
+ */
577
467
 
578
- desc.canSleep = (_options$canSleep = options === null || options === void 0 ? void 0 : options.canSleep) !== null && _options$canSleep !== void 0 ? _options$canSleep : true;
579
- return desc;
580
- };
581
- const createRigidBodyState = ({
582
- rigidBody,
583
- object,
584
- setMatrix,
585
- getMatrix,
586
- worldScale
587
- }) => {
588
- object.updateWorldMatrix(true, false);
589
- const invertedWorldMatrix = object.parent.matrixWorld.clone().invert();
590
- return {
591
- object,
592
- rigidBody,
593
- invertedWorldMatrix,
594
- setMatrix: setMatrix ? setMatrix : matrix => {
595
- object.matrix.copy(matrix);
596
- },
597
- getMatrix: getMatrix ? getMatrix : matrix => matrix.copy(object.matrix),
598
- scale: worldScale || object.getWorldScale(_scale).clone(),
599
- isSleeping: false
600
- };
601
- };
602
- const mutableRigidBodyOptions = {
603
- gravityScale: (rb, value) => {
604
- rb.setGravityScale(value, true);
605
- },
606
- linearDamping: (rb, value) => {
607
- rb.setLinearDamping(value);
608
- },
609
- angularDamping: (rb, value) => {
610
- rb.setAngularDamping(value);
611
- },
612
- enabledRotations: (rb, [x, y, z]) => {
613
- rb.setEnabledRotations(x, y, z, true);
614
- },
615
- enabledTranslations: (rb, [x, y, z]) => {
616
- rb.setEnabledTranslations(x, y, z, true);
617
- },
618
- lockRotations: (rb, value) => {
619
- rb.lockRotations(value, true);
620
- },
621
- lockTranslations: (rb, value) => {
622
- rb.lockTranslations(value, true);
623
- },
624
- angularVelocity: (rb, [x, y, z]) => {
625
- rb.setAngvel({
626
- x,
627
- y,
628
- z
629
- }, true);
630
- },
631
- linearVelocity: (rb, [x, y, z]) => {
632
- rb.setLinvel({
633
- x,
634
- y,
635
- z
636
- }, true);
637
- },
638
- ccd: (rb, value) => {
639
- rb.enableCcd(value);
640
- },
641
- userData: (rb, value) => {
642
- rb.userData = value;
643
- },
644
468
 
645
- type(rb, value) {
646
- rb.setBodyType(rigidBodyTypeFromString(value));
647
- },
469
+ const useRapier = () => {
470
+ return useContext(rapierContext);
471
+ };
472
+ /**
473
+ * Registers a callback to be called before the physics step
474
+ * @category Hooks
475
+ */
648
476
 
649
- position: () => {},
650
- rotation: () => {},
651
- quaternion: () => {},
652
- scale: () => {}
477
+ const useBeforePhysicsStep = callback => {
478
+ const {
479
+ beforeStepCallbacks
480
+ } = useRapier();
481
+ const ref = useMutableCallback(callback);
482
+ useEffect(() => {
483
+ beforeStepCallbacks.add(ref);
484
+ return () => {
485
+ beforeStepCallbacks.delete(ref);
486
+ };
487
+ }, []);
653
488
  };
654
- const mutableRigidBodyOptionKeys = Object.keys(mutableRigidBodyOptions);
655
- const setRigidBodyOptions = (rigidBody, options, states, updateTranslations = true) => {
656
- if (!rigidBody) {
657
- return;
658
- }
489
+ /**
490
+ * Registers a callback to be called after the physics step
491
+ * @category Hooks
492
+ */
659
493
 
660
- const state = states.get(rigidBody.handle);
494
+ const useAfterPhysicsStep = callback => {
495
+ const {
496
+ afterStepCallbacks
497
+ } = useRapier();
498
+ const ref = useMutableCallback(callback);
499
+ useEffect(() => {
500
+ afterStepCallbacks.add(ref);
501
+ return () => {
502
+ afterStepCallbacks.delete(ref);
503
+ };
504
+ }, []);
505
+ }; // Internal hooks
661
506
 
662
- if (state) {
663
- if (updateTranslations) {
664
- state.object.updateWorldMatrix(true, false);
507
+ /**
508
+ * @internal
509
+ */
665
510
 
666
- _matrix4.copy(state.object.matrixWorld).decompose(_position, _rotation, _scale);
511
+ const useChildColliderProps = (ref, options, ignoreMeshColliders = true) => {
512
+ const [colliderProps, setColliderProps] = useState([]);
513
+ useEffect(() => {
514
+ const object = ref.current;
667
515
 
668
- rigidBody.setTranslation(_position, false);
669
- rigidBody.setRotation(_rotation, false);
516
+ if (object && options.colliders !== false) {
517
+ setColliderProps(createColliderPropsFromChildren({
518
+ object: ref.current,
519
+ options,
520
+ ignoreMeshColliders
521
+ }));
670
522
  }
671
-
672
- mutableRigidBodyOptionKeys.forEach(key => {
673
- if (key in options) {
674
- mutableRigidBodyOptions[key](rigidBody, options[key]);
675
- }
676
- });
677
- }
678
- };
679
- const useUpdateRigidBodyOptions = (rigidBodyRef, props, states, updateTranslations = true) => {
680
- // TODO: Improve this, split each prop into its own effect
681
- const mutablePropsAsFlatArray = useMemo(() => mutableRigidBodyOptionKeys.flatMap(key => {
682
- return vectorToTuple(props[key]);
683
- }), [props]);
684
- useEffect(() => {
685
- if (Array.isArray(rigidBodyRef.current)) {
686
- for (const rigidBody of rigidBodyRef.current) {
687
- setRigidBodyOptions(rigidBody, props, states, updateTranslations);
688
- }
689
- } else if (rigidBodyRef.current) {
690
- setRigidBodyOptions(rigidBodyRef.current, props, states, updateTranslations);
691
- }
692
- }, mutablePropsAsFlatArray);
693
- };
694
- const useRigidBodyEvents = (rigidBodyRef, props, events) => {
695
- const {
696
- onWake,
697
- onSleep,
698
- onCollisionEnter,
699
- onCollisionExit,
700
- onIntersectionEnter,
701
- onIntersectionExit
702
- } = props;
703
- const eventHandlers = {
704
- onWake,
705
- onSleep,
706
- onCollisionEnter,
707
- onCollisionExit,
708
- onIntersectionEnter,
709
- onIntersectionExit
710
- };
711
- useEffect(() => {
712
- if (Array.isArray(rigidBodyRef.current)) {
713
- for (const rigidBody of rigidBodyRef.current) {
714
- events.set(rigidBody.handle, eventHandlers);
715
- }
716
- } else if (rigidBodyRef.current) {
717
- events.set(rigidBodyRef.current.handle, eventHandlers);
718
- }
719
-
720
- return () => {
721
- if (Array.isArray(rigidBodyRef.current)) {
722
- for (const rigidBody of rigidBodyRef.current) {
723
- events.delete(rigidBody.handle);
724
- }
725
- } else if (rigidBodyRef.current) {
726
- events.delete(rigidBodyRef.current.handle);
727
- }
728
- };
729
- }, [onWake, onSleep, onCollisionEnter, onCollisionExit, onIntersectionEnter, onIntersectionExit]);
730
- };
731
-
732
- const useRapier = () => {
733
- return useContext(rapierContext);
734
- };
735
- const useBeforePhysicsStep = callback => {
736
- const {
737
- beforeStepCallbacks
738
- } = useRapier();
739
- useEffect(() => {
740
- beforeStepCallbacks.add(callback);
741
- return () => {
742
- beforeStepCallbacks.delete(callback);
743
- };
744
- }, []);
745
- };
746
- const useAfterPhysicsStep = callback => {
747
- const {
748
- afterStepCallbacks
749
- } = useRapier();
750
- useEffect(() => {
751
- afterStepCallbacks.add(callback);
752
- return () => {
753
- afterStepCallbacks.delete(callback);
754
- };
755
- }, []);
756
- }; // Internal hooks
757
-
758
- const useChildColliderProps = (ref, options, ignoreMeshColliders = true) => {
759
- const [colliderProps, setColliderProps] = useState([]);
760
- useEffect(() => {
761
- const object = ref.current;
762
-
763
- if (object && options.colliders !== false) {
764
- setColliderProps(createColliderPropsFromChildren({
765
- object: ref.current,
766
- options,
767
- ignoreMeshColliders
768
- }));
769
- }
770
- }, [options.colliders]);
771
- return colliderProps;
772
- };
773
- const useRigidBody = (options = {}) => {
774
- const {
775
- world,
776
- rigidBodyStates,
777
- physicsOptions,
778
- rigidBodyEvents
779
- } = useRapier();
780
- const ref = useRef();
781
- const mergedOptions = useMemo(() => {
782
- return _objectSpread2(_objectSpread2(_objectSpread2({}, physicsOptions), options), {}, {
783
- children: undefined
784
- });
785
- }, [physicsOptions, options]);
786
- const childColliderProps = useChildColliderProps(ref, mergedOptions); // Create rigidbody
787
-
788
- const rigidBodyRef = useRef();
789
- const getRigidBodyRef = useRef(() => {
790
- if (!rigidBodyRef.current) {
791
- const desc = rigidBodyDescFromOptions(options);
792
- const rigidBody = world.createRigidBody(desc);
793
- rigidBodyRef.current = world.getRigidBody(rigidBody.handle);
794
- }
795
-
796
- return rigidBodyRef.current;
797
- }); // Setup
798
-
799
- useEffect(() => {
800
- const rigidBody = getRigidBodyRef.current();
801
- rigidBodyRef.current = rigidBody;
802
-
803
- if (!ref.current) {
804
- ref.current = new Object3D();
805
- }
806
-
807
- rigidBodyStates.set(rigidBody.handle, createRigidBodyState({
808
- rigidBody,
809
- object: ref.current
810
- }));
811
- return () => {
812
- world.removeRigidBody(rigidBody);
813
- rigidBodyStates.delete(rigidBody.handle);
814
- rigidBodyRef.current = undefined;
815
- };
816
- }, []);
817
- useUpdateRigidBodyOptions(rigidBodyRef, mergedOptions, rigidBodyStates);
818
- useRigidBodyEvents(rigidBodyRef, mergedOptions, rigidBodyEvents);
819
- const api = useMemo(() => createRigidBodyApi(getRigidBodyRef), []);
820
- return [ref, api, childColliderProps];
821
- };
523
+ }, [options.colliders]);
524
+ return colliderProps;
525
+ };
822
526
 
823
527
  const calcForceByType = {
824
528
  static: (s, m2, r, d, G) => s,
@@ -936,6 +640,10 @@ const importRapier = async () => {
936
640
  return r;
937
641
  };
938
642
 
643
+ /**
644
+ * The main physics component used to create a physics world.
645
+ * @category Components
646
+ */
939
647
  const Physics = ({
940
648
  colliders: _colliders = "cuboid",
941
649
  gravity: _gravity = [0, -9.81, 0],
@@ -1037,12 +745,12 @@ const Physics = ({
1037
745
  }); // Trigger beforeStep callbacks
1038
746
 
1039
747
  beforeStepCallbacks.forEach(callback => {
1040
- callback(api);
748
+ callback.current(api);
1041
749
  });
1042
750
  world.step(eventQueue); // Trigger afterStep callbacks
1043
751
 
1044
752
  afterStepCallbacks.forEach(callback => {
1045
- callback(api);
753
+ callback.current(api);
1046
754
  });
1047
755
  };
1048
756
 
@@ -1095,7 +803,7 @@ const Physics = ({
1095
803
  state.isSleeping = rigidBody.isSleeping();
1096
804
  }
1097
805
 
1098
- if (!rigidBody || rigidBody.isSleeping() || !state.setMatrix) {
806
+ if (!rigidBody || rigidBody.isSleeping() && !("isInstancedMesh" in state.object) || !state.setMatrix) {
1099
807
  return;
1100
808
  } // New states
1101
809
 
@@ -1109,7 +817,7 @@ const Physics = ({
1109
817
  _matrix4.compose(previousState.position, rapierQuaternionToQuaternion(previousState.rotation), state.scale).premultiply(state.invertedWorldMatrix).decompose(_position, _rotation, _scale); // Apply previous tick position
1110
818
 
1111
819
 
1112
- if (!(state.object instanceof InstancedMesh)) {
820
+ if (state.meshType == "mesh") {
1113
821
  state.object.position.copy(_position);
1114
822
  state.object.quaternion.copy(_rotation);
1115
823
  }
@@ -1118,9 +826,8 @@ const Physics = ({
1118
826
 
1119
827
  _matrix4.compose(t, rapierQuaternionToQuaternion(r), state.scale).premultiply(state.invertedWorldMatrix).decompose(_position, _rotation, _scale);
1120
828
 
1121
- if (state.object instanceof InstancedMesh) {
829
+ if (state.meshType == "instancedMesh") {
1122
830
  state.setMatrix(_matrix4);
1123
- state.object.instanceMatrix.needsUpdate = true;
1124
831
  } else {
1125
832
  // Interpolate to new position
1126
833
  state.object.position.lerp(_position, interpolationAlpha);
@@ -1304,8 +1011,87 @@ function _objectWithoutProperties(source, excluded) {
1304
1011
  return target;
1305
1012
  }
1306
1013
 
1014
+ /**
1015
+ * Initiate an instance and return a safe getter
1016
+ */
1017
+
1018
+ const useImperativeInstance = (createFn, destroyFn) => {
1019
+ const ref = useRef();
1020
+ const refGetter = useMemo(() => () => {
1021
+ if (!ref.current) {
1022
+ ref.current = createFn();
1023
+ }
1024
+
1025
+ return ref.current;
1026
+ }, []);
1027
+ useEffect(() => {
1028
+ const instance = refGetter();
1029
+ return () => {
1030
+ destroyFn(instance);
1031
+ ref.current = undefined;
1032
+ };
1033
+ }, []);
1034
+ return refGetter;
1035
+ };
1036
+
1037
+ /**
1038
+ * Takes an object resembling a Vector3 and returs a Three.Vector3
1039
+ * @category Math helpers
1040
+ */
1041
+
1042
+ const vec3 = ({
1043
+ x,
1044
+ y,
1045
+ z
1046
+ } = {
1047
+ x: 0,
1048
+ y: 0,
1049
+ z: 0
1050
+ }) => {
1051
+ return new Vector3(x, y, z);
1052
+ };
1053
+ /**
1054
+ * Takes an object resembling a Quaternion and returs a Three.Quaternion
1055
+ * @category Math helpers
1056
+ */
1057
+
1058
+ const quat = ({
1059
+ x,
1060
+ y,
1061
+ z,
1062
+ w
1063
+ } = {
1064
+ x: 0,
1065
+ y: 0,
1066
+ z: 0,
1067
+ w: 1
1068
+ }) => {
1069
+ return new Quaternion(x, y, z, w);
1070
+ };
1071
+ /**
1072
+ * Takes an object resembling an Euler and returs a Three.Euler
1073
+ * @category Math helpers
1074
+ */
1075
+
1076
+ const euler = ({
1077
+ x,
1078
+ y,
1079
+ z
1080
+ } = {
1081
+ x: 0,
1082
+ y: 0,
1083
+ z: 0
1084
+ }) => {
1085
+ return new Euler(x, y, z);
1086
+ };
1087
+
1307
1088
  // Colliders
1308
- const AnyCollider = /*#__PURE__*/memo( /*#__PURE__*/React.forwardRef((props, forwardedRef) => {
1089
+
1090
+ /**
1091
+ * A collider is a shape that can be attached to a rigid body to define its physical properties.
1092
+ * @internal
1093
+ */
1094
+ const AnyCollider = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props, forwardedRef) => {
1309
1095
  const {
1310
1096
  children,
1311
1097
  position,
@@ -1321,52 +1107,21 @@ const AnyCollider = /*#__PURE__*/memo( /*#__PURE__*/React.forwardRef((props, for
1321
1107
  } = useRapier();
1322
1108
  const rigidBodyContext = useRigidBodyContext();
1323
1109
  const ref = useRef(null);
1324
- const collidersRef = useMemo(() => {
1325
- if (forwardedRef !== null) {
1326
- return forwardedRef;
1327
- }
1328
-
1329
- const result = /*#__PURE__*/React.createRef();
1330
- result.current = [];
1331
- return result;
1332
- }, []);
1333
- useEffect(() => {
1334
- const object = ref.current;
1335
- const worldScale = object.getWorldScale(new Vector3());
1336
- const colliders = []; // If this is an InstancedRigidBody api
1337
-
1338
- if (rigidBodyContext && "at" in rigidBodyContext.api) {
1339
- rigidBodyContext.api.forEach((body, index) => {
1340
- var _rigidBodyContext$opt, _rigidBodyContext$opt2;
1341
-
1342
- let instanceScale = worldScale;
1343
-
1344
- if ("scales" in rigidBodyContext.options && rigidBodyContext !== null && rigidBodyContext !== void 0 && (_rigidBodyContext$opt = rigidBodyContext.options) !== null && _rigidBodyContext$opt !== void 0 && (_rigidBodyContext$opt2 = _rigidBodyContext$opt.scales) !== null && _rigidBodyContext$opt2 !== void 0 && _rigidBodyContext$opt2[index]) {
1345
- instanceScale = instanceScale.clone().multiply(vectorArrayToVector3(rigidBodyContext.options.scales[index]));
1346
- }
1347
-
1348
- const collider = createColliderFromOptions(props, world, instanceScale, body.raw());
1349
- colliderStates.set(collider.handle, createColliderState(collider, object, rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.ref.current));
1350
- colliders.push(collider);
1351
- });
1352
- } else {
1353
- const collider = createColliderFromOptions(props, world, worldScale, rigidBodyContext && (rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.api).raw());
1354
- colliderStates.set(collider.handle, createColliderState(collider, object, rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.ref.current));
1355
- colliders.push(collider);
1356
- }
1357
-
1358
- collidersRef.current = colliders;
1359
- return () => {
1360
- colliders.forEach(collider => {
1361
- world.removeCollider(collider);
1362
- });
1363
- };
1364
- }, []);
1110
+ const getInstance = useImperativeInstance(() => {
1111
+ const worldScale = ref.current.getWorldScale(vec3());
1112
+ const collider = createColliderFromOptions(props, world, worldScale, rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.getRigidBody);
1113
+ colliderStates.set(collider.handle, createColliderState(collider, ref.current, rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.ref.current));
1114
+ return collider;
1115
+ }, collider => {
1116
+ colliderStates.delete(collider.handle);
1117
+ world.removeCollider(collider);
1118
+ });
1119
+ useImperativeHandle(forwardedRef, () => getInstance());
1365
1120
  const mergedProps = useMemo(() => {
1366
1121
  return _objectSpread2(_objectSpread2({}, rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.options), props);
1367
1122
  }, [props, rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.options]);
1368
- useUpdateColliderOptions(collidersRef, mergedProps, colliderStates);
1369
- useColliderEvents(collidersRef, mergedProps, colliderEvents);
1123
+ useUpdateColliderOptions(getInstance, mergedProps, colliderStates);
1124
+ useColliderEvents(getInstance, mergedProps, colliderEvents);
1370
1125
  return /*#__PURE__*/React.createElement("object3D", {
1371
1126
  position: position,
1372
1127
  rotation: rotation,
@@ -1376,54 +1131,99 @@ const AnyCollider = /*#__PURE__*/memo( /*#__PURE__*/React.forwardRef((props, for
1376
1131
  name: name
1377
1132
  }, children);
1378
1133
  }));
1134
+
1135
+ /**
1136
+ * A cuboid collider shape
1137
+ * @category Colliders
1138
+ */
1379
1139
  const CuboidCollider = /*#__PURE__*/React.forwardRef((props, ref) => {
1380
1140
  return /*#__PURE__*/React.createElement(AnyCollider, _extends({}, props, {
1381
1141
  shape: "cuboid",
1382
1142
  ref: ref
1383
1143
  }));
1384
1144
  });
1145
+ /**
1146
+ * A round cuboid collider shape
1147
+ * @category Colliders
1148
+ */
1149
+
1385
1150
  const RoundCuboidCollider = /*#__PURE__*/React.forwardRef((props, ref) => {
1386
1151
  return /*#__PURE__*/React.createElement(AnyCollider, _extends({}, props, {
1387
1152
  shape: "roundCuboid",
1388
1153
  ref: ref
1389
1154
  }));
1390
1155
  });
1156
+ /**
1157
+ * A ball collider shape
1158
+ * @category Colliders
1159
+ */
1160
+
1391
1161
  const BallCollider = /*#__PURE__*/React.forwardRef((props, ref) => {
1392
1162
  return /*#__PURE__*/React.createElement(AnyCollider, _extends({}, props, {
1393
1163
  shape: "ball",
1394
1164
  ref: ref
1395
1165
  }));
1396
1166
  });
1167
+ /**
1168
+ * A capsule collider shape
1169
+ * @category Colliders
1170
+ */
1171
+
1397
1172
  const CapsuleCollider = /*#__PURE__*/React.forwardRef((props, ref) => {
1398
1173
  return /*#__PURE__*/React.createElement(AnyCollider, _extends({}, props, {
1399
1174
  shape: "capsule",
1400
1175
  ref: ref
1401
1176
  }));
1402
1177
  });
1178
+ /**
1179
+ * A heightfield collider shape
1180
+ * @category Colliders
1181
+ */
1182
+
1403
1183
  const HeightfieldCollider = /*#__PURE__*/React.forwardRef((props, ref) => {
1404
1184
  return /*#__PURE__*/React.createElement(AnyCollider, _extends({}, props, {
1405
1185
  shape: "heightfield",
1406
1186
  ref: ref
1407
1187
  }));
1408
1188
  });
1189
+ /**
1190
+ * A trimesh collider shape
1191
+ * @category Colliders
1192
+ */
1193
+
1409
1194
  const TrimeshCollider = /*#__PURE__*/React.forwardRef((props, ref) => {
1410
1195
  return /*#__PURE__*/React.createElement(AnyCollider, _extends({}, props, {
1411
1196
  shape: "trimesh",
1412
1197
  ref: ref
1413
1198
  }));
1414
1199
  });
1200
+ /**
1201
+ * A cone collider shape
1202
+ * @category Colliders
1203
+ */
1204
+
1415
1205
  const ConeCollider = /*#__PURE__*/React.forwardRef((props, ref) => {
1416
1206
  return /*#__PURE__*/React.createElement(AnyCollider, _extends({}, props, {
1417
1207
  shape: "cone",
1418
1208
  ref: ref
1419
1209
  }));
1420
1210
  });
1211
+ /**
1212
+ * A cylinder collider shape
1213
+ * @category Colliders
1214
+ */
1215
+
1421
1216
  const CylinderCollider = /*#__PURE__*/React.forwardRef((props, ref) => {
1422
1217
  return /*#__PURE__*/React.createElement(AnyCollider, _extends({}, props, {
1423
1218
  shape: "cylinder",
1424
1219
  ref: ref
1425
1220
  }));
1426
1221
  });
1222
+ /**
1223
+ * A convex hull collider shape
1224
+ * @category Colliders
1225
+ */
1226
+
1427
1227
  const ConvexHullCollider = /*#__PURE__*/React.forwardRef((props, ref) => {
1428
1228
  return /*#__PURE__*/React.createElement(AnyCollider, _extends({}, props, {
1429
1229
  shape: "convexHull",
@@ -1440,31 +1240,212 @@ ConeCollider.displayName = "ConeCollider";
1440
1240
  CylinderCollider.displayName = "CylinderCollider";
1441
1241
  ConvexHullCollider.displayName = "ConvexHullCollider";
1442
1242
 
1443
- const _excluded$1 = ["children", "type", "position", "rotation", "scale", "quaternion"];
1243
+ const rigidBodyDescFromOptions = options => {
1244
+ var _options$canSleep;
1245
+
1246
+ const type = rigidBodyTypeFromString((options === null || options === void 0 ? void 0 : options.type) || "dynamic");
1247
+ const desc = new RigidBodyDesc(type); // Apply immutable options
1248
+
1249
+ desc.canSleep = (_options$canSleep = options === null || options === void 0 ? void 0 : options.canSleep) !== null && _options$canSleep !== void 0 ? _options$canSleep : true;
1250
+ return desc;
1251
+ };
1252
+ const createRigidBodyState = ({
1253
+ rigidBody,
1254
+ object,
1255
+ setMatrix,
1256
+ getMatrix,
1257
+ worldScale,
1258
+ meshType: _meshType = "mesh"
1259
+ }) => {
1260
+ object.updateWorldMatrix(true, false);
1261
+ const invertedWorldMatrix = object.parent.matrixWorld.clone().invert();
1262
+ return {
1263
+ object,
1264
+ rigidBody,
1265
+ invertedWorldMatrix,
1266
+ setMatrix: setMatrix ? setMatrix : matrix => {
1267
+ object.matrix.copy(matrix);
1268
+ },
1269
+ getMatrix: getMatrix ? getMatrix : matrix => matrix.copy(object.matrix),
1270
+ scale: worldScale || object.getWorldScale(_scale).clone(),
1271
+ isSleeping: false,
1272
+ meshType: _meshType
1273
+ };
1274
+ };
1275
+ const mutableRigidBodyOptions = {
1276
+ gravityScale: (rb, value) => {
1277
+ rb.setGravityScale(value, true);
1278
+ },
1279
+ linearDamping: (rb, value) => {
1280
+ rb.setLinearDamping(value);
1281
+ },
1282
+ angularDamping: (rb, value) => {
1283
+ rb.setAngularDamping(value);
1284
+ },
1285
+ enabledRotations: (rb, [x, y, z]) => {
1286
+ rb.setEnabledRotations(x, y, z, true);
1287
+ },
1288
+ enabledTranslations: (rb, [x, y, z]) => {
1289
+ rb.setEnabledTranslations(x, y, z, true);
1290
+ },
1291
+ lockRotations: (rb, value) => {
1292
+ rb.lockRotations(value, true);
1293
+ },
1294
+ lockTranslations: (rb, value) => {
1295
+ rb.lockTranslations(value, true);
1296
+ },
1297
+ angularVelocity: (rb, [x, y, z]) => {
1298
+ rb.setAngvel({
1299
+ x,
1300
+ y,
1301
+ z
1302
+ }, true);
1303
+ },
1304
+ linearVelocity: (rb, [x, y, z]) => {
1305
+ rb.setLinvel({
1306
+ x,
1307
+ y,
1308
+ z
1309
+ }, true);
1310
+ },
1311
+ ccd: (rb, value) => {
1312
+ rb.enableCcd(value);
1313
+ },
1314
+ userData: (rb, value) => {
1315
+ rb.userData = value;
1316
+ },
1317
+
1318
+ type(rb, value) {
1319
+ rb.setBodyType(rigidBodyTypeFromString(value), true);
1320
+ },
1321
+
1322
+ position: () => {},
1323
+ rotation: () => {},
1324
+ quaternion: () => {},
1325
+ scale: () => {}
1326
+ };
1327
+ const mutableRigidBodyOptionKeys = Object.keys(mutableRigidBodyOptions);
1328
+ const setRigidBodyOptions = (rigidBody, options, states, updateTranslations = true) => {
1329
+ if (!rigidBody) {
1330
+ return;
1331
+ }
1332
+
1333
+ const state = states.get(rigidBody.handle);
1334
+
1335
+ if (state) {
1336
+ if (updateTranslations) {
1337
+ state.object.updateWorldMatrix(true, false);
1338
+
1339
+ _matrix4.copy(state.object.matrixWorld).decompose(_position, _rotation, _scale);
1340
+
1341
+ rigidBody.setTranslation(_position, false);
1342
+ rigidBody.setRotation(_rotation, false);
1343
+ }
1344
+
1345
+ mutableRigidBodyOptionKeys.forEach(key => {
1346
+ if (key in options) {
1347
+ mutableRigidBodyOptions[key](rigidBody, options[key]);
1348
+ }
1349
+ });
1350
+ }
1351
+ };
1352
+ const useUpdateRigidBodyOptions = (getRigidBody, props, states, updateTranslations = true) => {
1353
+ // TODO: Improve this, split each prop into its own effect
1354
+ const mutablePropsAsFlatArray = useMemo(() => mutableRigidBodyOptionKeys.flatMap(key => {
1355
+ return vectorToTuple(props[key]);
1356
+ }), [props]);
1357
+ useEffect(() => {
1358
+ const rigidBody = getRigidBody();
1359
+ setRigidBodyOptions(rigidBody, props, states, updateTranslations);
1360
+ }, mutablePropsAsFlatArray);
1361
+ };
1362
+ const useRigidBodyEvents = (getRigidBody, props, events) => {
1363
+ const {
1364
+ onWake,
1365
+ onSleep,
1366
+ onCollisionEnter,
1367
+ onCollisionExit,
1368
+ onIntersectionEnter,
1369
+ onIntersectionExit
1370
+ } = props;
1371
+ const eventHandlers = {
1372
+ onWake,
1373
+ onSleep,
1374
+ onCollisionEnter,
1375
+ onCollisionExit,
1376
+ onIntersectionEnter,
1377
+ onIntersectionExit
1378
+ };
1379
+ useEffect(() => {
1380
+ const rigidBody = getRigidBody();
1381
+ events.set(rigidBody.handle, eventHandlers);
1382
+ return () => {
1383
+ events.delete(rigidBody.handle);
1384
+ };
1385
+ }, [onWake, onSleep, onCollisionEnter, onCollisionExit, onIntersectionEnter, onIntersectionExit]);
1386
+ };
1387
+
1388
+ const _excluded$1 = ["children", "type", "position", "rotation", "scale", "quaternion", "transformState"];
1444
1389
  const RigidBodyContext = /*#__PURE__*/createContext(undefined);
1445
1390
  const useRigidBodyContext = () => useContext(RigidBodyContext);
1446
- const RigidBody = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props, ref) => {
1391
+
1392
+ /**
1393
+ * A rigid body is a physical object that can be simulated by the physics engine.
1394
+ * @category Components
1395
+ */
1396
+ const RigidBody = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props, forwardedRef) => {
1447
1397
  const {
1448
1398
  children,
1449
1399
  type,
1450
1400
  position,
1451
1401
  rotation,
1452
1402
  scale,
1453
- quaternion
1403
+ quaternion,
1404
+ transformState
1454
1405
  } = props,
1455
1406
  objectProps = _objectWithoutProperties(props, _excluded$1);
1456
1407
 
1457
- const [object, api, childColliderProps] = useRigidBody(props);
1458
- useImperativeHandle(ref, () => api);
1459
- const contextValue = useMemo(() => ({
1460
- ref: object,
1461
- api,
1462
- options: props
1463
- }), [object, api, props]);
1408
+ const ref = useRef(null);
1409
+ const {
1410
+ world,
1411
+ rigidBodyStates,
1412
+ physicsOptions,
1413
+ rigidBodyEvents
1414
+ } = useRapier();
1415
+ const mergedOptions = useMemo(() => {
1416
+ return _objectSpread2(_objectSpread2(_objectSpread2({}, physicsOptions), props), {}, {
1417
+ children: undefined
1418
+ });
1419
+ }, [physicsOptions, props]);
1420
+ const childColliderProps = useChildColliderProps(ref, mergedOptions); // Create rigidbody
1421
+
1422
+ const getInstance = useImperativeInstance(() => {
1423
+ const desc = rigidBodyDescFromOptions(mergedOptions);
1424
+ const rigidBody = world.createRigidBody(desc);
1425
+ const state = createRigidBodyState({
1426
+ rigidBody,
1427
+ object: ref.current
1428
+ });
1429
+ rigidBodyStates.set(rigidBody.handle, props.transformState ? props.transformState(state) : state);
1430
+ return rigidBody;
1431
+ }, rigidBody => {
1432
+ world.removeRigidBody(rigidBody);
1433
+ rigidBodyStates.delete(rigidBody.handle);
1434
+ });
1435
+ useUpdateRigidBodyOptions(getInstance, mergedOptions, rigidBodyStates);
1436
+ useRigidBodyEvents(getInstance, mergedOptions, rigidBodyEvents);
1437
+ useImperativeHandle(forwardedRef, () => getInstance());
1438
+ const contextValue = useMemo(() => {
1439
+ return {
1440
+ ref,
1441
+ getRigidBody: getInstance,
1442
+ options: mergedOptions
1443
+ };
1444
+ }, [mergedOptions]);
1464
1445
  return /*#__PURE__*/React.createElement(RigidBodyContext.Provider, {
1465
1446
  value: contextValue
1466
1447
  }, /*#__PURE__*/React.createElement("object3D", _extends({
1467
- ref: object
1448
+ ref: ref
1468
1449
  }, objectProps, {
1469
1450
  position: position,
1470
1451
  rotation: rotation,
@@ -1476,14 +1457,17 @@ const RigidBody = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props, ref) => {
1476
1457
  }));
1477
1458
  RigidBody.displayName = "RigidBody";
1478
1459
 
1460
+ /**
1461
+ * A mesh collider is a collider that is automatically generated from the geometry of the children.
1462
+ * @category Colliders
1463
+ */
1479
1464
  const MeshCollider = /*#__PURE__*/memo(props => {
1480
1465
  const {
1481
1466
  children,
1482
1467
  type
1483
1468
  } = props;
1484
1469
  const {
1485
- physicsOptions,
1486
- world
1470
+ physicsOptions
1487
1471
  } = useRapier();
1488
1472
  const object = useRef(null);
1489
1473
  const {
@@ -1597,155 +1581,119 @@ const Debug = /*#__PURE__*/memo(() => {
1597
1581
  }, attractor))));
1598
1582
  });
1599
1583
 
1600
- const _excluded = ["positions", "rotations", "children"];
1601
- const InstancedRigidBodies = /*#__PURE__*/forwardRef((props, ref) => {
1602
- const {
1603
- world,
1604
- rigidBodyStates,
1605
- physicsOptions,
1606
- rigidBodyEvents
1607
- } = useRapier();
1584
+ const _excluded = ["children", "instances", "colliderNodes", "position", "rotation", "quaternion", "scale"];
1585
+ const InstancedRigidBodies = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props, ref) => {
1608
1586
  const object = useRef(null);
1587
+ const instancedWrapper = useRef(null);
1609
1588
 
1610
1589
  const {
1611
- positions,
1612
- rotations,
1613
- children
1590
+ // instanced props
1591
+ children,
1592
+ instances,
1593
+ colliderNodes = [],
1594
+ // wrapper object props
1595
+ position,
1596
+ rotation,
1597
+ quaternion,
1598
+ scale
1614
1599
  } = props,
1615
- options = _objectWithoutProperties(props, _excluded);
1600
+ rigidBodyProps = _objectWithoutProperties(props, _excluded);
1616
1601
 
1617
- const instancesRef = useRef([]);
1618
- const rigidBodyRefs = useRef([]);
1619
- const instancesRefGetter = useRef(() => {
1620
- if (!instancesRef.current) {
1621
- instancesRef.current = [];
1622
- }
1602
+ const rigidBodyApis = useRef([]);
1603
+ useImperativeHandle(ref, () => rigidBodyApis.current, [instances]);
1604
+ const childColliderProps = useChildColliderProps(object, _objectSpread2(_objectSpread2({}, props), {}, {
1605
+ children: undefined
1606
+ }));
1623
1607
 
1624
- return instancesRef.current;
1625
- });
1626
- const mergedOptions = useMemo(() => {
1627
- return _objectSpread2(_objectSpread2({}, physicsOptions), options);
1628
- }, [physicsOptions, options]);
1629
- const childColliderProps = useChildColliderProps(object, mergedOptions);
1630
- useLayoutEffect(() => {
1631
- object.current.updateWorldMatrix(true, false);
1632
- const instances = instancesRefGetter.current();
1633
- const invertedWorld = object.current.matrixWorld.clone().invert();
1634
- object.current.traverseVisible(mesh => {
1635
- if (mesh instanceof InstancedMesh) {
1636
- mesh.instanceMatrix.setUsage(DynamicDrawUsage);
1637
- const worldScale = mesh.getWorldScale(_scale);
1638
-
1639
- for (let index = 0; index < mesh.count; index++) {
1640
- var _options$scales;
1641
-
1642
- const desc = rigidBodyDescFromOptions(props);
1643
- const rigidBody = world.createRigidBody(desc);
1644
- rigidBodyRefs.current.push(rigidBody);
1645
- const scale = ((_options$scales = options.scales) === null || _options$scales === void 0 ? void 0 : _options$scales[index]) || [1, 1, 1];
1646
- const instanceScale = worldScale.clone().multiply(vectorArrayToVector3(scale));
1647
- rigidBodyStates.set(rigidBody.handle, createRigidBodyState({
1648
- rigidBody,
1649
- object: mesh,
1650
- setMatrix: matrix => mesh.setMatrixAt(index, matrix),
1651
- getMatrix: matrix => {
1652
- mesh.getMatrixAt(index, matrix);
1653
- return matrix;
1654
- },
1655
- worldScale: instanceScale
1656
- }));
1657
- const [x, y, z] = (positions === null || positions === void 0 ? void 0 : positions[index]) || [0, 0, 0];
1658
- const [rx, ry, rz] = (rotations === null || rotations === void 0 ? void 0 : rotations[index]) || [0, 0, 0];
1608
+ const getInstancedMesh = () => {
1609
+ const firstChild = instancedWrapper.current.children[0];
1659
1610
 
1660
- _object3d.position.set(x, y, z);
1611
+ if (firstChild && "isInstancedMesh" in firstChild) {
1612
+ return firstChild;
1613
+ }
1661
1614
 
1662
- _object3d.rotation.set(rx, ry, rz);
1615
+ return undefined;
1616
+ };
1663
1617
 
1664
- _object3d.applyMatrix4(invertedWorld);
1618
+ useEffect(() => {
1619
+ const instancedMesh = getInstancedMesh();
1665
1620
 
1666
- mesh.setMatrixAt(index, _object3d.matrix);
1667
- rigidBody.setTranslation(_object3d.position, false);
1668
- rigidBody.setRotation(_object3d.quaternion, false);
1669
- const api = createRigidBodyApi({
1670
- current() {
1671
- return rigidBody;
1672
- }
1621
+ if (instancedMesh) {
1622
+ instancedMesh.instanceMatrix.setUsage(DynamicDrawUsage);
1623
+ } else {
1624
+ console.warn("InstancedRigidBodies expects exactly one child, which must be an InstancedMesh");
1625
+ }
1626
+ }, []); // Update the RigidBodyStates whenever the instances change
1673
1627
 
1674
- });
1675
- instances.push({
1676
- rigidBody,
1677
- api
1678
- });
1679
- }
1680
- }
1681
- });
1682
- return () => {
1683
- instances.forEach(rb => {
1684
- world.removeRigidBody(rb.rigidBody);
1685
- rigidBodyStates.delete(rb.rigidBody.handle);
1628
+ const applyInstancedState = (state, index) => {
1629
+ const instancedMesh = getInstancedMesh();
1630
+
1631
+ if (instancedMesh) {
1632
+ return _objectSpread2(_objectSpread2({}, state), {}, {
1633
+ getMatrix: matrix => {
1634
+ instancedMesh.getMatrixAt(index, matrix);
1635
+ return matrix;
1636
+ },
1637
+ setMatrix: matrix => {
1638
+ instancedMesh.setMatrixAt(index, matrix);
1639
+ instancedMesh.instanceMatrix.needsUpdate = true;
1640
+ },
1641
+ meshType: "instancedMesh"
1686
1642
  });
1687
- rigidBodyRefs.current = [];
1688
- instancesRef.current = [];
1689
- };
1690
- }, []);
1691
- const api = useMemo(() => createInstancedRigidBodiesApi(instancesRefGetter), []);
1692
- useImperativeHandle(ref, () => api);
1693
- useUpdateRigidBodyOptions(rigidBodyRefs, mergedOptions, rigidBodyStates, false);
1694
- useRigidBodyEvents(rigidBodyRefs, mergedOptions, rigidBodyEvents);
1695
- const contextValue = useMemo(() => {
1696
- return {
1697
- ref: object,
1698
- api,
1699
- options: mergedOptions
1700
- };
1701
- }, [api, mergedOptions]);
1702
- return /*#__PURE__*/React.createElement(RigidBodyContext.Provider, {
1703
- value: contextValue
1704
- }, /*#__PURE__*/React.createElement("object3D", {
1643
+ }
1644
+
1645
+ return state;
1646
+ };
1647
+
1648
+ return /*#__PURE__*/React.createElement("object3D", _extends({
1705
1649
  ref: object
1706
- }, props.children, childColliderProps.map((colliderProps, index) => /*#__PURE__*/React.createElement(AnyCollider, _extends({
1650
+ }, rigidBodyProps, {
1651
+ position: position,
1652
+ rotation: rotation,
1653
+ quaternion: quaternion,
1654
+ scale: scale
1655
+ }), /*#__PURE__*/React.createElement("object3D", {
1656
+ ref: instancedWrapper
1657
+ }, children), instances === null || instances === void 0 ? void 0 : instances.map((instance, index) => /*#__PURE__*/React.createElement(RigidBody, _extends({}, rigidBodyProps, instance, {
1658
+ ref: body => rigidBodyApis.current[index] = body,
1659
+ transformState: state => applyInstancedState(state, index)
1660
+ }), /*#__PURE__*/React.createElement(React.Fragment, null, colliderNodes.map((node, index) => /*#__PURE__*/React.createElement(Fragment, {
1707
1661
  key: index
1708
- }, colliderProps)))));
1709
- });
1662
+ }, node)), childColliderProps.map((colliderProps, colliderIndex) => /*#__PURE__*/React.createElement(AnyCollider, _extends({
1663
+ key: colliderIndex
1664
+ }, colliderProps)))))));
1665
+ }));
1710
1666
  InstancedRigidBodies.displayName = "InstancedRigidBodies";
1711
1667
 
1668
+ /**
1669
+ * @internal
1670
+ */
1671
+
1712
1672
  const useImpulseJoint = (body1, body2, params) => {
1713
1673
  const {
1714
1674
  world
1715
1675
  } = useRapier();
1716
1676
  const jointRef = useRef();
1717
- const getJointRef = useRef(() => {
1718
- if (!jointRef.current) {
1719
- let rb1;
1720
- let rb2;
1721
-
1722
- if ("current" in body1 && body1.current && "current" in body2 && body2.current) {
1723
- rb1 = world.getRigidBody(body1.current.handle);
1724
- rb2 = world.getRigidBody(body2.current.handle);
1725
- const newJoint = world.createImpulseJoint(params, rb1, rb2);
1726
- jointRef.current = newJoint;
1727
- }
1677
+ useImperativeInstance(() => {
1678
+ if (body1.current && body2.current) {
1679
+ const newJoint = world.createImpulseJoint(params, body1.current, body2.current);
1680
+ jointRef.current = newJoint;
1681
+ return newJoint;
1682
+ }
1683
+ }, joint => {
1684
+ if (joint) {
1685
+ jointRef.current = undefined;
1686
+ world.removeImpulseJoint(joint);
1728
1687
  }
1729
-
1730
- return jointRef.current;
1731
1688
  });
1732
- useEffect(() => {
1733
- const joint = getJointRef.current();
1734
- return () => {
1735
- if (joint) {
1736
- world.removeImpulseJoint(joint);
1737
- jointRef.current = undefined;
1738
- }
1739
- };
1740
- }, []);
1741
- const api = useMemo(() => createJointApi(getJointRef), []);
1742
- return api;
1689
+ return jointRef;
1743
1690
  };
1744
1691
  /**
1745
- *
1746
1692
  * A fixed joint ensures that two rigid-bodies don't move relative to each other.
1747
1693
  * Fixed joints are characterized by one local frame (represented by an isometry) on each rigid-body.
1748
1694
  * The fixed-joint makes these frames coincide in world-space.
1695
+ *
1696
+ * @category Hooks - Joints
1749
1697
  */
1750
1698
 
1751
1699
  const useFixedJoint = (body1, body2, [body1Anchor, body1LocalFrame, body2Anchor, body2LocalFrame]) => {
@@ -1759,6 +1707,8 @@ const useFixedJoint = (body1, body2, [body1Anchor, body1LocalFrame, body2Anchor,
1759
1707
  * translational motion at this points). This is typically used to simulate ragdolls arms, pendulums, etc.
1760
1708
  * They are characterized by one local anchor on each rigid-body. Each anchor represents the location of the
1761
1709
  * points that need to coincide on the local-space of each rigid-body.
1710
+ *
1711
+ * @category Hooks - Joints
1762
1712
  */
1763
1713
 
1764
1714
  const useSphericalJoint = (body1, body2, [body1Anchor, body2Anchor]) => {
@@ -1771,6 +1721,8 @@ const useSphericalJoint = (body1, body2, [body1Anchor, body2Anchor]) => {
1771
1721
  * The revolute joint prevents any relative movement between two rigid-bodies, except for relative
1772
1722
  * rotations along one axis. This is typically used to simulate wheels, fans, etc.
1773
1723
  * They are characterized by one local anchor as well as one local axis on each rigid-body.
1724
+ *
1725
+ * @category Hooks - Joints
1774
1726
  */
1775
1727
 
1776
1728
  const useRevoluteJoint = (body1, body2, [body1Anchor, body2Anchor, axis, limits]) => {
@@ -1790,6 +1742,8 @@ const useRevoluteJoint = (body1, body2, [body1Anchor, body2Anchor, axis, limits]
1790
1742
  * The prismatic joint prevents any relative movement between two rigid-bodies, except for relative translations along one axis.
1791
1743
  * It is characterized by one local anchor as well as one local axis on each rigid-body. In 3D, an optional
1792
1744
  * local tangent axis can be specified for each rigid-body.
1745
+ *
1746
+ * @category Hooks - Joints
1793
1747
  */
1794
1748
 
1795
1749
  const usePrismaticJoint = (body1, body2, [body1Anchor, body2Anchor, axis, limits]) => {
@@ -1842,4 +1796,4 @@ const interactionGroups = (memberships, filters) => (bitmask(memberships) << 16)
1842
1796
 
1843
1797
  const bitmask = groups => [groups].flat().reduce((acc, layer) => acc | 1 << layer, 0);
1844
1798
 
1845
- export { AnyCollider, Attractor, BallCollider, CapsuleCollider, ConeCollider, ConvexHullCollider, CuboidCollider, CylinderCollider, Debug, HeightfieldCollider, InstancedRigidBodies, MeshCollider, Physics, RigidBody, RoundCuboidCollider, TrimeshCollider, interactionGroups, useAfterPhysicsStep, useBeforePhysicsStep, useFixedJoint, useImpulseJoint, usePrismaticJoint, useRapier, useRevoluteJoint, useSphericalJoint };
1799
+ export { AnyCollider, Attractor, BallCollider, CapsuleCollider, ConeCollider, ConvexHullCollider, CuboidCollider, CylinderCollider, Debug, HeightfieldCollider, InstancedRigidBodies, MeshCollider, Physics, RigidBody, RoundCuboidCollider, TrimeshCollider, euler, interactionGroups, quat, useAfterPhysicsStep, useBeforePhysicsStep, useFixedJoint, useImpulseJoint, usePrismaticJoint, useRapier, useRevoluteJoint, useSphericalJoint, vec3 };