@react-three/rapier 0.12.1 → 0.13.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.
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} +7 -2
  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 +548 -600
  21. package/dist/react-three-rapier.cjs.prod.js +548 -600
  22. package/dist/react-three-rapier.esm.js +549 -604
  23. package/package.json +12 -12
  24. package/readme.md +165 -30
  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,76 @@ 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;
574
-
575
- const type = rigidBodyTypeFromString((options === null || options === void 0 ? void 0 : options.type) || "dynamic");
576
- const desc = new RigidBodyDesc(type); // Apply immutable options
455
+ /**
456
+ * Exposes the Rapier context, and world
457
+ * @category Hooks
458
+ */
577
459
 
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
- };
460
+ const useRapier = () => {
461
+ return useContext(rapierContext);
601
462
  };
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
-
645
- type(rb, value) {
646
- rb.setBodyType(rigidBodyTypeFromString(value));
647
- },
463
+ /**
464
+ * Registers a callback to be called before the physics step
465
+ * @category Hooks
466
+ */
648
467
 
649
- position: () => {},
650
- rotation: () => {},
651
- quaternion: () => {},
652
- scale: () => {}
468
+ const useBeforePhysicsStep = callback => {
469
+ const {
470
+ beforeStepCallbacks
471
+ } = useRapier();
472
+ useEffect(() => {
473
+ beforeStepCallbacks.add(callback);
474
+ return () => {
475
+ beforeStepCallbacks.delete(callback);
476
+ };
477
+ }, []);
653
478
  };
654
- const mutableRigidBodyOptionKeys = Object.keys(mutableRigidBodyOptions);
655
- const setRigidBodyOptions = (rigidBody, options, states, updateTranslations = true) => {
656
- if (!rigidBody) {
657
- return;
658
- }
479
+ /**
480
+ * Registers a callback to be called after the physics step
481
+ * @category Hooks
482
+ */
659
483
 
660
- const state = states.get(rigidBody.handle);
484
+ const useAfterPhysicsStep = callback => {
485
+ const {
486
+ afterStepCallbacks
487
+ } = useRapier();
488
+ useEffect(() => {
489
+ afterStepCallbacks.add(callback);
490
+ return () => {
491
+ afterStepCallbacks.delete(callback);
492
+ };
493
+ }, []);
494
+ }; // Internal hooks
661
495
 
662
- if (state) {
663
- if (updateTranslations) {
664
- state.object.updateWorldMatrix(true, false);
496
+ /**
497
+ * @internal
498
+ */
665
499
 
666
- _matrix4.copy(state.object.matrixWorld).decompose(_position, _rotation, _scale);
500
+ const useChildColliderProps = (ref, options, ignoreMeshColliders = true) => {
501
+ const [colliderProps, setColliderProps] = useState([]);
502
+ useEffect(() => {
503
+ const object = ref.current;
667
504
 
668
- rigidBody.setTranslation(_position, false);
669
- rigidBody.setRotation(_rotation, false);
505
+ if (object && options.colliders !== false) {
506
+ setColliderProps(createColliderPropsFromChildren({
507
+ object: ref.current,
508
+ options,
509
+ ignoreMeshColliders
510
+ }));
670
511
  }
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
- };
512
+ }, [options.colliders]);
513
+ return colliderProps;
514
+ };
822
515
 
823
516
  const calcForceByType = {
824
517
  static: (s, m2, r, d, G) => s,
@@ -936,6 +629,10 @@ const importRapier = async () => {
936
629
  return r;
937
630
  };
938
631
 
632
+ /**
633
+ * The main physics component used to create a physics world.
634
+ * @category Components
635
+ */
939
636
  const Physics = ({
940
637
  colliders: _colliders = "cuboid",
941
638
  gravity: _gravity = [0, -9.81, 0],
@@ -1029,7 +726,13 @@ const Physics = ({
1029
726
  const clampedDelta = MathUtils.clamp(dt, 0, 0.2);
1030
727
 
1031
728
  const stepWorld = () => {
1032
- // Trigger beforeStep callbacks
729
+ // Apply attractors
730
+ world.forEachRigidBody(body => {
731
+ attractorStates.forEach(attractorState => {
732
+ applyAttractorForceOnRigidBody(body, attractorState);
733
+ });
734
+ }); // Trigger beforeStep callbacks
735
+
1033
736
  beforeStepCallbacks.forEach(callback => {
1034
737
  callback(api);
1035
738
  });
@@ -1050,22 +753,18 @@ const Physics = ({
1050
753
  steppingState.accumulator += clampedDelta;
1051
754
 
1052
755
  while (steppingState.accumulator >= _timeStep) {
1053
- world.forEachRigidBody(body => {
1054
- // Set up previous state
1055
- // needed for accurate interpolations if the world steps more than once
1056
- if (_interpolate) {
1057
- steppingState.previousState = {};
756
+ // Set up previous state
757
+ // needed for accurate interpolations if the world steps more than once
758
+ if (_interpolate) {
759
+ steppingState.previousState = {};
760
+ world.forEachRigidBody(body => {
1058
761
  steppingState.previousState[body.handle] = {
1059
762
  position: body.translation(),
1060
763
  rotation: body.rotation()
1061
764
  };
1062
- } // Apply attractors
1063
-
1064
-
1065
- attractorStates.forEach(attractorState => {
1066
- applyAttractorForceOnRigidBody(body, attractorState);
1067
765
  });
1068
- });
766
+ }
767
+
1069
768
  stepWorld();
1070
769
  steppingState.accumulator -= _timeStep;
1071
770
  }
@@ -1093,7 +792,7 @@ const Physics = ({
1093
792
  state.isSleeping = rigidBody.isSleeping();
1094
793
  }
1095
794
 
1096
- if (!rigidBody || rigidBody.isSleeping() || !state.setMatrix) {
795
+ if (!rigidBody || rigidBody.isSleeping() && !("isInstancedMesh" in state.object) || !state.setMatrix) {
1097
796
  return;
1098
797
  } // New states
1099
798
 
@@ -1107,7 +806,7 @@ const Physics = ({
1107
806
  _matrix4.compose(previousState.position, rapierQuaternionToQuaternion(previousState.rotation), state.scale).premultiply(state.invertedWorldMatrix).decompose(_position, _rotation, _scale); // Apply previous tick position
1108
807
 
1109
808
 
1110
- if (!(state.object instanceof InstancedMesh)) {
809
+ if (state.meshType == "mesh") {
1111
810
  state.object.position.copy(_position);
1112
811
  state.object.quaternion.copy(_rotation);
1113
812
  }
@@ -1116,9 +815,8 @@ const Physics = ({
1116
815
 
1117
816
  _matrix4.compose(t, rapierQuaternionToQuaternion(r), state.scale).premultiply(state.invertedWorldMatrix).decompose(_position, _rotation, _scale);
1118
817
 
1119
- if (state.object instanceof InstancedMesh) {
818
+ if (state.meshType == "instancedMesh") {
1120
819
  state.setMatrix(_matrix4);
1121
- state.object.instanceMatrix.needsUpdate = true;
1122
820
  } else {
1123
821
  // Interpolate to new position
1124
822
  state.object.position.lerp(_position, interpolationAlpha);
@@ -1302,8 +1000,87 @@ function _objectWithoutProperties(source, excluded) {
1302
1000
  return target;
1303
1001
  }
1304
1002
 
1003
+ /**
1004
+ * Initiate an instance and return a safe getter
1005
+ */
1006
+
1007
+ const useImperativeInstance = (createFn, destroyFn) => {
1008
+ const ref = useRef();
1009
+ const refGetter = useMemo(() => () => {
1010
+ if (!ref.current) {
1011
+ ref.current = createFn();
1012
+ }
1013
+
1014
+ return ref.current;
1015
+ }, []);
1016
+ useEffect(() => {
1017
+ const instance = refGetter();
1018
+ return () => {
1019
+ destroyFn(instance);
1020
+ ref.current = undefined;
1021
+ };
1022
+ }, []);
1023
+ return refGetter;
1024
+ };
1025
+
1026
+ /**
1027
+ * Takes an object resembling a Vector3 and returs a Three.Vector3
1028
+ * @category Math helpers
1029
+ */
1030
+
1031
+ const vec3 = ({
1032
+ x,
1033
+ y,
1034
+ z
1035
+ } = {
1036
+ x: 0,
1037
+ y: 0,
1038
+ z: 0
1039
+ }) => {
1040
+ return new Vector3(x, y, z);
1041
+ };
1042
+ /**
1043
+ * Takes an object resembling a Quaternion and returs a Three.Quaternion
1044
+ * @category Math helpers
1045
+ */
1046
+
1047
+ const quat = ({
1048
+ x,
1049
+ y,
1050
+ z,
1051
+ w
1052
+ } = {
1053
+ x: 0,
1054
+ y: 0,
1055
+ z: 0,
1056
+ w: 1
1057
+ }) => {
1058
+ return new Quaternion(x, y, z, w);
1059
+ };
1060
+ /**
1061
+ * Takes an object resembling an Euler and returs a Three.Euler
1062
+ * @category Math helpers
1063
+ */
1064
+
1065
+ const euler = ({
1066
+ x,
1067
+ y,
1068
+ z
1069
+ } = {
1070
+ x: 0,
1071
+ y: 0,
1072
+ z: 0
1073
+ }) => {
1074
+ return new Euler(x, y, z);
1075
+ };
1076
+
1305
1077
  // Colliders
1306
- const AnyCollider = /*#__PURE__*/memo( /*#__PURE__*/React.forwardRef((props, forwardedRef) => {
1078
+
1079
+ /**
1080
+ * A collider is a shape that can be attached to a rigid body to define its physical properties.
1081
+ * @internal
1082
+ */
1083
+ const AnyCollider = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props, forwardedRef) => {
1307
1084
  const {
1308
1085
  children,
1309
1086
  position,
@@ -1319,52 +1096,21 @@ const AnyCollider = /*#__PURE__*/memo( /*#__PURE__*/React.forwardRef((props, for
1319
1096
  } = useRapier();
1320
1097
  const rigidBodyContext = useRigidBodyContext();
1321
1098
  const ref = useRef(null);
1322
- const collidersRef = useMemo(() => {
1323
- if (forwardedRef !== null) {
1324
- return forwardedRef;
1325
- }
1326
-
1327
- const result = /*#__PURE__*/React.createRef();
1328
- result.current = [];
1329
- return result;
1330
- }, []);
1331
- useEffect(() => {
1332
- const object = ref.current;
1333
- const worldScale = object.getWorldScale(new Vector3());
1334
- const colliders = []; // If this is an InstancedRigidBody api
1335
-
1336
- if (rigidBodyContext && "at" in rigidBodyContext.api) {
1337
- rigidBodyContext.api.forEach((body, index) => {
1338
- var _rigidBodyContext$opt, _rigidBodyContext$opt2;
1339
-
1340
- let instanceScale = worldScale;
1341
-
1342
- 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]) {
1343
- instanceScale = instanceScale.clone().multiply(vectorArrayToVector3(rigidBodyContext.options.scales[index]));
1344
- }
1345
-
1346
- const collider = createColliderFromOptions(props, world, instanceScale, body.raw());
1347
- colliderStates.set(collider.handle, createColliderState(collider, object, rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.ref.current));
1348
- colliders.push(collider);
1349
- });
1350
- } else {
1351
- const collider = createColliderFromOptions(props, world, worldScale, rigidBodyContext && (rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.api).raw());
1352
- colliderStates.set(collider.handle, createColliderState(collider, object, rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.ref.current));
1353
- colliders.push(collider);
1354
- }
1355
-
1356
- collidersRef.current = colliders;
1357
- return () => {
1358
- colliders.forEach(collider => {
1359
- world.removeCollider(collider);
1360
- });
1361
- };
1362
- }, []);
1099
+ const getInstance = useImperativeInstance(() => {
1100
+ const worldScale = ref.current.getWorldScale(vec3());
1101
+ const collider = createColliderFromOptions(props, world, worldScale, rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.getRigidBody);
1102
+ colliderStates.set(collider.handle, createColliderState(collider, ref.current, rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.ref.current));
1103
+ return collider;
1104
+ }, collider => {
1105
+ colliderStates.delete(collider.handle);
1106
+ world.removeCollider(collider);
1107
+ });
1108
+ useImperativeHandle(forwardedRef, () => getInstance());
1363
1109
  const mergedProps = useMemo(() => {
1364
1110
  return _objectSpread2(_objectSpread2({}, rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.options), props);
1365
1111
  }, [props, rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.options]);
1366
- useUpdateColliderOptions(collidersRef, mergedProps, colliderStates);
1367
- useColliderEvents(collidersRef, mergedProps, colliderEvents);
1112
+ useUpdateColliderOptions(getInstance, mergedProps, colliderStates);
1113
+ useColliderEvents(getInstance, mergedProps, colliderEvents);
1368
1114
  return /*#__PURE__*/React.createElement("object3D", {
1369
1115
  position: position,
1370
1116
  rotation: rotation,
@@ -1374,54 +1120,99 @@ const AnyCollider = /*#__PURE__*/memo( /*#__PURE__*/React.forwardRef((props, for
1374
1120
  name: name
1375
1121
  }, children);
1376
1122
  }));
1123
+
1124
+ /**
1125
+ * A cuboid collider shape
1126
+ * @category Colliders
1127
+ */
1377
1128
  const CuboidCollider = /*#__PURE__*/React.forwardRef((props, ref) => {
1378
1129
  return /*#__PURE__*/React.createElement(AnyCollider, _extends({}, props, {
1379
1130
  shape: "cuboid",
1380
1131
  ref: ref
1381
1132
  }));
1382
1133
  });
1134
+ /**
1135
+ * A round cuboid collider shape
1136
+ * @category Colliders
1137
+ */
1138
+
1383
1139
  const RoundCuboidCollider = /*#__PURE__*/React.forwardRef((props, ref) => {
1384
1140
  return /*#__PURE__*/React.createElement(AnyCollider, _extends({}, props, {
1385
1141
  shape: "roundCuboid",
1386
1142
  ref: ref
1387
1143
  }));
1388
1144
  });
1145
+ /**
1146
+ * A ball collider shape
1147
+ * @category Colliders
1148
+ */
1149
+
1389
1150
  const BallCollider = /*#__PURE__*/React.forwardRef((props, ref) => {
1390
1151
  return /*#__PURE__*/React.createElement(AnyCollider, _extends({}, props, {
1391
1152
  shape: "ball",
1392
1153
  ref: ref
1393
1154
  }));
1394
1155
  });
1156
+ /**
1157
+ * A capsule collider shape
1158
+ * @category Colliders
1159
+ */
1160
+
1395
1161
  const CapsuleCollider = /*#__PURE__*/React.forwardRef((props, ref) => {
1396
1162
  return /*#__PURE__*/React.createElement(AnyCollider, _extends({}, props, {
1397
1163
  shape: "capsule",
1398
1164
  ref: ref
1399
1165
  }));
1400
1166
  });
1167
+ /**
1168
+ * A heightfield collider shape
1169
+ * @category Colliders
1170
+ */
1171
+
1401
1172
  const HeightfieldCollider = /*#__PURE__*/React.forwardRef((props, ref) => {
1402
1173
  return /*#__PURE__*/React.createElement(AnyCollider, _extends({}, props, {
1403
1174
  shape: "heightfield",
1404
1175
  ref: ref
1405
1176
  }));
1406
1177
  });
1178
+ /**
1179
+ * A trimesh collider shape
1180
+ * @category Colliders
1181
+ */
1182
+
1407
1183
  const TrimeshCollider = /*#__PURE__*/React.forwardRef((props, ref) => {
1408
1184
  return /*#__PURE__*/React.createElement(AnyCollider, _extends({}, props, {
1409
1185
  shape: "trimesh",
1410
1186
  ref: ref
1411
1187
  }));
1412
1188
  });
1189
+ /**
1190
+ * A cone collider shape
1191
+ * @category Colliders
1192
+ */
1193
+
1413
1194
  const ConeCollider = /*#__PURE__*/React.forwardRef((props, ref) => {
1414
1195
  return /*#__PURE__*/React.createElement(AnyCollider, _extends({}, props, {
1415
1196
  shape: "cone",
1416
1197
  ref: ref
1417
1198
  }));
1418
1199
  });
1200
+ /**
1201
+ * A cylinder collider shape
1202
+ * @category Colliders
1203
+ */
1204
+
1419
1205
  const CylinderCollider = /*#__PURE__*/React.forwardRef((props, ref) => {
1420
1206
  return /*#__PURE__*/React.createElement(AnyCollider, _extends({}, props, {
1421
1207
  shape: "cylinder",
1422
1208
  ref: ref
1423
1209
  }));
1424
1210
  });
1211
+ /**
1212
+ * A convex hull collider shape
1213
+ * @category Colliders
1214
+ */
1215
+
1425
1216
  const ConvexHullCollider = /*#__PURE__*/React.forwardRef((props, ref) => {
1426
1217
  return /*#__PURE__*/React.createElement(AnyCollider, _extends({}, props, {
1427
1218
  shape: "convexHull",
@@ -1438,31 +1229,212 @@ ConeCollider.displayName = "ConeCollider";
1438
1229
  CylinderCollider.displayName = "CylinderCollider";
1439
1230
  ConvexHullCollider.displayName = "ConvexHullCollider";
1440
1231
 
1441
- const _excluded$1 = ["children", "type", "position", "rotation", "scale", "quaternion"];
1232
+ const rigidBodyDescFromOptions = options => {
1233
+ var _options$canSleep;
1234
+
1235
+ const type = rigidBodyTypeFromString((options === null || options === void 0 ? void 0 : options.type) || "dynamic");
1236
+ const desc = new RigidBodyDesc(type); // Apply immutable options
1237
+
1238
+ desc.canSleep = (_options$canSleep = options === null || options === void 0 ? void 0 : options.canSleep) !== null && _options$canSleep !== void 0 ? _options$canSleep : true;
1239
+ return desc;
1240
+ };
1241
+ const createRigidBodyState = ({
1242
+ rigidBody,
1243
+ object,
1244
+ setMatrix,
1245
+ getMatrix,
1246
+ worldScale,
1247
+ meshType: _meshType = "mesh"
1248
+ }) => {
1249
+ object.updateWorldMatrix(true, false);
1250
+ const invertedWorldMatrix = object.parent.matrixWorld.clone().invert();
1251
+ return {
1252
+ object,
1253
+ rigidBody,
1254
+ invertedWorldMatrix,
1255
+ setMatrix: setMatrix ? setMatrix : matrix => {
1256
+ object.matrix.copy(matrix);
1257
+ },
1258
+ getMatrix: getMatrix ? getMatrix : matrix => matrix.copy(object.matrix),
1259
+ scale: worldScale || object.getWorldScale(_scale).clone(),
1260
+ isSleeping: false,
1261
+ meshType: _meshType
1262
+ };
1263
+ };
1264
+ const mutableRigidBodyOptions = {
1265
+ gravityScale: (rb, value) => {
1266
+ rb.setGravityScale(value, true);
1267
+ },
1268
+ linearDamping: (rb, value) => {
1269
+ rb.setLinearDamping(value);
1270
+ },
1271
+ angularDamping: (rb, value) => {
1272
+ rb.setAngularDamping(value);
1273
+ },
1274
+ enabledRotations: (rb, [x, y, z]) => {
1275
+ rb.setEnabledRotations(x, y, z, true);
1276
+ },
1277
+ enabledTranslations: (rb, [x, y, z]) => {
1278
+ rb.setEnabledTranslations(x, y, z, true);
1279
+ },
1280
+ lockRotations: (rb, value) => {
1281
+ rb.lockRotations(value, true);
1282
+ },
1283
+ lockTranslations: (rb, value) => {
1284
+ rb.lockTranslations(value, true);
1285
+ },
1286
+ angularVelocity: (rb, [x, y, z]) => {
1287
+ rb.setAngvel({
1288
+ x,
1289
+ y,
1290
+ z
1291
+ }, true);
1292
+ },
1293
+ linearVelocity: (rb, [x, y, z]) => {
1294
+ rb.setLinvel({
1295
+ x,
1296
+ y,
1297
+ z
1298
+ }, true);
1299
+ },
1300
+ ccd: (rb, value) => {
1301
+ rb.enableCcd(value);
1302
+ },
1303
+ userData: (rb, value) => {
1304
+ rb.userData = value;
1305
+ },
1306
+
1307
+ type(rb, value) {
1308
+ rb.setBodyType(rigidBodyTypeFromString(value), true);
1309
+ },
1310
+
1311
+ position: () => {},
1312
+ rotation: () => {},
1313
+ quaternion: () => {},
1314
+ scale: () => {}
1315
+ };
1316
+ const mutableRigidBodyOptionKeys = Object.keys(mutableRigidBodyOptions);
1317
+ const setRigidBodyOptions = (rigidBody, options, states, updateTranslations = true) => {
1318
+ if (!rigidBody) {
1319
+ return;
1320
+ }
1321
+
1322
+ const state = states.get(rigidBody.handle);
1323
+
1324
+ if (state) {
1325
+ if (updateTranslations) {
1326
+ state.object.updateWorldMatrix(true, false);
1327
+
1328
+ _matrix4.copy(state.object.matrixWorld).decompose(_position, _rotation, _scale);
1329
+
1330
+ rigidBody.setTranslation(_position, false);
1331
+ rigidBody.setRotation(_rotation, false);
1332
+ }
1333
+
1334
+ mutableRigidBodyOptionKeys.forEach(key => {
1335
+ if (key in options) {
1336
+ mutableRigidBodyOptions[key](rigidBody, options[key]);
1337
+ }
1338
+ });
1339
+ }
1340
+ };
1341
+ const useUpdateRigidBodyOptions = (getRigidBody, props, states, updateTranslations = true) => {
1342
+ // TODO: Improve this, split each prop into its own effect
1343
+ const mutablePropsAsFlatArray = useMemo(() => mutableRigidBodyOptionKeys.flatMap(key => {
1344
+ return vectorToTuple(props[key]);
1345
+ }), [props]);
1346
+ useEffect(() => {
1347
+ const rigidBody = getRigidBody();
1348
+ setRigidBodyOptions(rigidBody, props, states, updateTranslations);
1349
+ }, mutablePropsAsFlatArray);
1350
+ };
1351
+ const useRigidBodyEvents = (getRigidBody, props, events) => {
1352
+ const {
1353
+ onWake,
1354
+ onSleep,
1355
+ onCollisionEnter,
1356
+ onCollisionExit,
1357
+ onIntersectionEnter,
1358
+ onIntersectionExit
1359
+ } = props;
1360
+ const eventHandlers = {
1361
+ onWake,
1362
+ onSleep,
1363
+ onCollisionEnter,
1364
+ onCollisionExit,
1365
+ onIntersectionEnter,
1366
+ onIntersectionExit
1367
+ };
1368
+ useEffect(() => {
1369
+ const rigidBody = getRigidBody();
1370
+ events.set(rigidBody.handle, eventHandlers);
1371
+ return () => {
1372
+ events.delete(rigidBody.handle);
1373
+ };
1374
+ }, [onWake, onSleep, onCollisionEnter, onCollisionExit, onIntersectionEnter, onIntersectionExit]);
1375
+ };
1376
+
1377
+ const _excluded$1 = ["children", "type", "position", "rotation", "scale", "quaternion", "transformState"];
1442
1378
  const RigidBodyContext = /*#__PURE__*/createContext(undefined);
1443
1379
  const useRigidBodyContext = () => useContext(RigidBodyContext);
1444
- const RigidBody = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props, ref) => {
1380
+
1381
+ /**
1382
+ * A rigid body is a physical object that can be simulated by the physics engine.
1383
+ * @category Components
1384
+ */
1385
+ const RigidBody = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props, forwardedRef) => {
1445
1386
  const {
1446
1387
  children,
1447
1388
  type,
1448
1389
  position,
1449
1390
  rotation,
1450
1391
  scale,
1451
- quaternion
1392
+ quaternion,
1393
+ transformState
1452
1394
  } = props,
1453
1395
  objectProps = _objectWithoutProperties(props, _excluded$1);
1454
1396
 
1455
- const [object, api, childColliderProps] = useRigidBody(props);
1456
- useImperativeHandle(ref, () => api);
1457
- const contextValue = useMemo(() => ({
1458
- ref: object,
1459
- api,
1460
- options: props
1461
- }), [object, api, props]);
1397
+ const ref = useRef(null);
1398
+ const {
1399
+ world,
1400
+ rigidBodyStates,
1401
+ physicsOptions,
1402
+ rigidBodyEvents
1403
+ } = useRapier();
1404
+ const mergedOptions = useMemo(() => {
1405
+ return _objectSpread2(_objectSpread2(_objectSpread2({}, physicsOptions), props), {}, {
1406
+ children: undefined
1407
+ });
1408
+ }, [physicsOptions, props]);
1409
+ const childColliderProps = useChildColliderProps(ref, mergedOptions); // Create rigidbody
1410
+
1411
+ const getInstance = useImperativeInstance(() => {
1412
+ const desc = rigidBodyDescFromOptions(mergedOptions);
1413
+ const rigidBody = world.createRigidBody(desc);
1414
+ const state = createRigidBodyState({
1415
+ rigidBody,
1416
+ object: ref.current
1417
+ });
1418
+ rigidBodyStates.set(rigidBody.handle, props.transformState ? props.transformState(state) : state);
1419
+ return rigidBody;
1420
+ }, rigidBody => {
1421
+ world.removeRigidBody(rigidBody);
1422
+ rigidBodyStates.delete(rigidBody.handle);
1423
+ });
1424
+ useUpdateRigidBodyOptions(getInstance, mergedOptions, rigidBodyStates);
1425
+ useRigidBodyEvents(getInstance, mergedOptions, rigidBodyEvents);
1426
+ useImperativeHandle(forwardedRef, () => getInstance());
1427
+ const contextValue = useMemo(() => {
1428
+ return {
1429
+ ref,
1430
+ getRigidBody: getInstance,
1431
+ options: mergedOptions
1432
+ };
1433
+ }, [mergedOptions]);
1462
1434
  return /*#__PURE__*/React.createElement(RigidBodyContext.Provider, {
1463
1435
  value: contextValue
1464
1436
  }, /*#__PURE__*/React.createElement("object3D", _extends({
1465
- ref: object
1437
+ ref: ref
1466
1438
  }, objectProps, {
1467
1439
  position: position,
1468
1440
  rotation: rotation,
@@ -1474,14 +1446,17 @@ const RigidBody = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props, ref) => {
1474
1446
  }));
1475
1447
  RigidBody.displayName = "RigidBody";
1476
1448
 
1449
+ /**
1450
+ * A mesh collider is a collider that is automatically generated from the geometry of the children.
1451
+ * @category Colliders
1452
+ */
1477
1453
  const MeshCollider = /*#__PURE__*/memo(props => {
1478
1454
  const {
1479
1455
  children,
1480
1456
  type
1481
1457
  } = props;
1482
1458
  const {
1483
- physicsOptions,
1484
- world
1459
+ physicsOptions
1485
1460
  } = useRapier();
1486
1461
  const object = useRef(null);
1487
1462
  const {
@@ -1595,155 +1570,119 @@ const Debug = /*#__PURE__*/memo(() => {
1595
1570
  }, attractor))));
1596
1571
  });
1597
1572
 
1598
- const _excluded = ["positions", "rotations", "children"];
1599
- const InstancedRigidBodies = /*#__PURE__*/forwardRef((props, ref) => {
1600
- const {
1601
- world,
1602
- rigidBodyStates,
1603
- physicsOptions,
1604
- rigidBodyEvents
1605
- } = useRapier();
1573
+ const _excluded = ["children", "instances", "colliderNodes", "position", "rotation", "quaternion", "scale"];
1574
+ const InstancedRigidBodies = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props, ref) => {
1606
1575
  const object = useRef(null);
1576
+ const instancedWrapper = useRef(null);
1607
1577
 
1608
1578
  const {
1609
- positions,
1610
- rotations,
1611
- children
1579
+ // instanced props
1580
+ children,
1581
+ instances,
1582
+ colliderNodes = [],
1583
+ // wrapper object props
1584
+ position,
1585
+ rotation,
1586
+ quaternion,
1587
+ scale
1612
1588
  } = props,
1613
- options = _objectWithoutProperties(props, _excluded);
1589
+ rigidBodyProps = _objectWithoutProperties(props, _excluded);
1614
1590
 
1615
- const instancesRef = useRef([]);
1616
- const rigidBodyRefs = useRef([]);
1617
- const instancesRefGetter = useRef(() => {
1618
- if (!instancesRef.current) {
1619
- instancesRef.current = [];
1620
- }
1591
+ const rigidBodyApis = useRef([]);
1592
+ useImperativeHandle(ref, () => rigidBodyApis.current, [instances]);
1593
+ const childColliderProps = useChildColliderProps(object, _objectSpread2(_objectSpread2({}, props), {}, {
1594
+ children: undefined
1595
+ }));
1621
1596
 
1622
- return instancesRef.current;
1623
- });
1624
- const mergedOptions = useMemo(() => {
1625
- return _objectSpread2(_objectSpread2({}, physicsOptions), options);
1626
- }, [physicsOptions, options]);
1627
- const childColliderProps = useChildColliderProps(object, mergedOptions);
1628
- useLayoutEffect(() => {
1629
- object.current.updateWorldMatrix(true, false);
1630
- const instances = instancesRefGetter.current();
1631
- const invertedWorld = object.current.matrixWorld.clone().invert();
1632
- object.current.traverseVisible(mesh => {
1633
- if (mesh instanceof InstancedMesh) {
1634
- mesh.instanceMatrix.setUsage(DynamicDrawUsage);
1635
- const worldScale = mesh.getWorldScale(_scale);
1636
-
1637
- for (let index = 0; index < mesh.count; index++) {
1638
- var _options$scales;
1639
-
1640
- const desc = rigidBodyDescFromOptions(props);
1641
- const rigidBody = world.createRigidBody(desc);
1642
- rigidBodyRefs.current.push(rigidBody);
1643
- const scale = ((_options$scales = options.scales) === null || _options$scales === void 0 ? void 0 : _options$scales[index]) || [1, 1, 1];
1644
- const instanceScale = worldScale.clone().multiply(vectorArrayToVector3(scale));
1645
- rigidBodyStates.set(rigidBody.handle, createRigidBodyState({
1646
- rigidBody,
1647
- object: mesh,
1648
- setMatrix: matrix => mesh.setMatrixAt(index, matrix),
1649
- getMatrix: matrix => {
1650
- mesh.getMatrixAt(index, matrix);
1651
- return matrix;
1652
- },
1653
- worldScale: instanceScale
1654
- }));
1655
- const [x, y, z] = (positions === null || positions === void 0 ? void 0 : positions[index]) || [0, 0, 0];
1656
- const [rx, ry, rz] = (rotations === null || rotations === void 0 ? void 0 : rotations[index]) || [0, 0, 0];
1597
+ const getInstancedMesh = () => {
1598
+ const firstChild = instancedWrapper.current.children[0];
1657
1599
 
1658
- _object3d.position.set(x, y, z);
1600
+ if (firstChild && "isInstancedMesh" in firstChild) {
1601
+ return firstChild;
1602
+ }
1659
1603
 
1660
- _object3d.rotation.set(rx, ry, rz);
1604
+ return undefined;
1605
+ };
1661
1606
 
1662
- _object3d.applyMatrix4(invertedWorld);
1607
+ useEffect(() => {
1608
+ const instancedMesh = getInstancedMesh();
1663
1609
 
1664
- mesh.setMatrixAt(index, _object3d.matrix);
1665
- rigidBody.setTranslation(_object3d.position, false);
1666
- rigidBody.setRotation(_object3d.quaternion, false);
1667
- const api = createRigidBodyApi({
1668
- current() {
1669
- return rigidBody;
1670
- }
1610
+ if (instancedMesh) {
1611
+ instancedMesh.instanceMatrix.setUsage(DynamicDrawUsage);
1612
+ } else {
1613
+ console.warn("InstancedRigidBodies expects exactly one child, which must be an InstancedMesh");
1614
+ }
1615
+ }, []); // Update the RigidBodyStates whenever the instances change
1671
1616
 
1672
- });
1673
- instances.push({
1674
- rigidBody,
1675
- api
1676
- });
1677
- }
1678
- }
1679
- });
1680
- return () => {
1681
- instances.forEach(rb => {
1682
- world.removeRigidBody(rb.rigidBody);
1683
- rigidBodyStates.delete(rb.rigidBody.handle);
1617
+ const applyInstancedState = (state, index) => {
1618
+ const instancedMesh = getInstancedMesh();
1619
+
1620
+ if (instancedMesh) {
1621
+ return _objectSpread2(_objectSpread2({}, state), {}, {
1622
+ getMatrix: matrix => {
1623
+ instancedMesh.getMatrixAt(index, matrix);
1624
+ return matrix;
1625
+ },
1626
+ setMatrix: matrix => {
1627
+ instancedMesh.setMatrixAt(index, matrix);
1628
+ instancedMesh.instanceMatrix.needsUpdate = true;
1629
+ },
1630
+ meshType: "instancedMesh"
1684
1631
  });
1685
- rigidBodyRefs.current = [];
1686
- instancesRef.current = [];
1687
- };
1688
- }, []);
1689
- const api = useMemo(() => createInstancedRigidBodiesApi(instancesRefGetter), []);
1690
- useImperativeHandle(ref, () => api);
1691
- useUpdateRigidBodyOptions(rigidBodyRefs, mergedOptions, rigidBodyStates, false);
1692
- useRigidBodyEvents(rigidBodyRefs, mergedOptions, rigidBodyEvents);
1693
- const contextValue = useMemo(() => {
1694
- return {
1695
- ref: object,
1696
- api,
1697
- options: mergedOptions
1698
- };
1699
- }, [api, mergedOptions]);
1700
- return /*#__PURE__*/React.createElement(RigidBodyContext.Provider, {
1701
- value: contextValue
1702
- }, /*#__PURE__*/React.createElement("object3D", {
1632
+ }
1633
+
1634
+ return state;
1635
+ };
1636
+
1637
+ return /*#__PURE__*/React.createElement("object3D", _extends({
1703
1638
  ref: object
1704
- }, props.children, childColliderProps.map((colliderProps, index) => /*#__PURE__*/React.createElement(AnyCollider, _extends({
1639
+ }, rigidBodyProps, {
1640
+ position: position,
1641
+ rotation: rotation,
1642
+ quaternion: quaternion,
1643
+ scale: scale
1644
+ }), /*#__PURE__*/React.createElement("object3D", {
1645
+ ref: instancedWrapper
1646
+ }, children), instances === null || instances === void 0 ? void 0 : instances.map((instance, index) => /*#__PURE__*/React.createElement(RigidBody, _extends({}, rigidBodyProps, instance, {
1647
+ ref: body => rigidBodyApis.current[index] = body,
1648
+ transformState: state => applyInstancedState(state, index)
1649
+ }), /*#__PURE__*/React.createElement(React.Fragment, null, colliderNodes.map((node, index) => /*#__PURE__*/React.createElement(Fragment, {
1705
1650
  key: index
1706
- }, colliderProps)))));
1707
- });
1651
+ }, node)), childColliderProps.map((colliderProps, colliderIndex) => /*#__PURE__*/React.createElement(AnyCollider, _extends({
1652
+ key: colliderIndex
1653
+ }, colliderProps)))))));
1654
+ }));
1708
1655
  InstancedRigidBodies.displayName = "InstancedRigidBodies";
1709
1656
 
1657
+ /**
1658
+ * @internal
1659
+ */
1660
+
1710
1661
  const useImpulseJoint = (body1, body2, params) => {
1711
1662
  const {
1712
1663
  world
1713
1664
  } = useRapier();
1714
1665
  const jointRef = useRef();
1715
- const getJointRef = useRef(() => {
1716
- if (!jointRef.current) {
1717
- let rb1;
1718
- let rb2;
1719
-
1720
- if ("current" in body1 && body1.current && "current" in body2 && body2.current) {
1721
- rb1 = world.getRigidBody(body1.current.handle);
1722
- rb2 = world.getRigidBody(body2.current.handle);
1723
- const newJoint = world.createImpulseJoint(params, rb1, rb2);
1724
- jointRef.current = newJoint;
1725
- }
1666
+ useImperativeInstance(() => {
1667
+ if (body1.current && body2.current) {
1668
+ const newJoint = world.createImpulseJoint(params, body1.current, body2.current);
1669
+ jointRef.current = newJoint;
1670
+ return newJoint;
1671
+ }
1672
+ }, joint => {
1673
+ if (joint) {
1674
+ jointRef.current = undefined;
1675
+ world.removeImpulseJoint(joint);
1726
1676
  }
1727
-
1728
- return jointRef.current;
1729
1677
  });
1730
- useEffect(() => {
1731
- const joint = getJointRef.current();
1732
- return () => {
1733
- if (joint) {
1734
- world.removeImpulseJoint(joint);
1735
- jointRef.current = undefined;
1736
- }
1737
- };
1738
- }, []);
1739
- const api = useMemo(() => createJointApi(getJointRef), []);
1740
- return api;
1678
+ return jointRef;
1741
1679
  };
1742
1680
  /**
1743
- *
1744
1681
  * A fixed joint ensures that two rigid-bodies don't move relative to each other.
1745
1682
  * Fixed joints are characterized by one local frame (represented by an isometry) on each rigid-body.
1746
1683
  * The fixed-joint makes these frames coincide in world-space.
1684
+ *
1685
+ * @category Hooks - Joints
1747
1686
  */
1748
1687
 
1749
1688
  const useFixedJoint = (body1, body2, [body1Anchor, body1LocalFrame, body2Anchor, body2LocalFrame]) => {
@@ -1757,6 +1696,8 @@ const useFixedJoint = (body1, body2, [body1Anchor, body1LocalFrame, body2Anchor,
1757
1696
  * translational motion at this points). This is typically used to simulate ragdolls arms, pendulums, etc.
1758
1697
  * They are characterized by one local anchor on each rigid-body. Each anchor represents the location of the
1759
1698
  * points that need to coincide on the local-space of each rigid-body.
1699
+ *
1700
+ * @category Hooks - Joints
1760
1701
  */
1761
1702
 
1762
1703
  const useSphericalJoint = (body1, body2, [body1Anchor, body2Anchor]) => {
@@ -1769,6 +1710,8 @@ const useSphericalJoint = (body1, body2, [body1Anchor, body2Anchor]) => {
1769
1710
  * The revolute joint prevents any relative movement between two rigid-bodies, except for relative
1770
1711
  * rotations along one axis. This is typically used to simulate wheels, fans, etc.
1771
1712
  * They are characterized by one local anchor as well as one local axis on each rigid-body.
1713
+ *
1714
+ * @category Hooks - Joints
1772
1715
  */
1773
1716
 
1774
1717
  const useRevoluteJoint = (body1, body2, [body1Anchor, body2Anchor, axis, limits]) => {
@@ -1788,6 +1731,8 @@ const useRevoluteJoint = (body1, body2, [body1Anchor, body2Anchor, axis, limits]
1788
1731
  * The prismatic joint prevents any relative movement between two rigid-bodies, except for relative translations along one axis.
1789
1732
  * It is characterized by one local anchor as well as one local axis on each rigid-body. In 3D, an optional
1790
1733
  * local tangent axis can be specified for each rigid-body.
1734
+ *
1735
+ * @category Hooks - Joints
1791
1736
  */
1792
1737
 
1793
1738
  const usePrismaticJoint = (body1, body2, [body1Anchor, body2Anchor, axis, limits]) => {
@@ -1840,4 +1785,4 @@ const interactionGroups = (memberships, filters) => (bitmask(memberships) << 16)
1840
1785
 
1841
1786
  const bitmask = groups => [groups].flat().reduce((acc, layer) => acc | 1 << layer, 0);
1842
1787
 
1843
- 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 };
1788
+ 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 };