@react-three/rapier 0.16.0-canary.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,8 @@
1
1
  import type Rapier from "@dimforge/rapier3d-compat";
2
- import { Collider, ColliderHandle, RigidBody, RigidBodyHandle } from "@dimforge/rapier3d-compat";
2
+ import { Collider, ColliderHandle, RigidBody, RigidBodyHandle, World } from "@dimforge/rapier3d-compat";
3
3
  import React, { FC, ReactNode } from "react";
4
4
  import { Matrix4, Object3D, Vector3 } from "three";
5
5
  import { CollisionEnterHandler, CollisionExitHandler, ContactForceHandler, IntersectionEnterHandler, IntersectionExitHandler, RigidBodyAutoCollider, Vector3Tuple } from "../types";
6
- import { WorldApi } from "../utils/api";
7
6
  export interface RigidBodyState {
8
7
  meshType: "instancedMesh" | "mesh";
9
8
  rigidBody: RigidBody;
@@ -18,7 +17,7 @@ export interface RigidBodyState {
18
17
  isSleeping: boolean;
19
18
  }
20
19
  export declare type RigidBodyStateMap = Map<RigidBody["handle"], RigidBodyState>;
21
- export declare type WorldStepCallback = (worldApi: WorldApi) => void;
20
+ export declare type WorldStepCallback = (world: World) => void;
22
21
  export declare type WorldStepCallbackSet = Set<{
23
22
  current: WorldStepCallback;
24
23
  }>;
@@ -77,7 +76,7 @@ export interface RapierContext {
77
76
  /**
78
77
  * The Rapier physics world
79
78
  */
80
- world: WorldApi;
79
+ world: World;
81
80
  /**
82
81
  * If the physics simulation is paused
83
82
  */
@@ -116,6 +115,43 @@ export interface PhysicsProps {
116
115
  * @defaultValue [0, -9.81, 0]
117
116
  */
118
117
  gravity?: Vector3Tuple;
118
+ /**
119
+ * The maximum velocity iterations the velocity-based constraint solver can make to attempt
120
+ * to remove the energy introduced by constraint stabilization.
121
+ *
122
+ * @defaultValue 1
123
+ */
124
+ maxStabilizationIterations?: number;
125
+ /**
126
+ * The maximum velocity iterations the velocity-based friction constraint solver can make.
127
+ *
128
+ * The greater this value is, the most realistic friction will be.
129
+ * However a greater number of iterations is more computationally intensive.
130
+ *
131
+ * @defaultValue 8
132
+ */
133
+ maxVelocityFrictionIterations?: number;
134
+ /**
135
+ * The maximum velocity iterations the velocity-based force constraint solver can make.
136
+ *
137
+ * The greater this value is, the most rigid and realistic the physics simulation will be.
138
+ * However a greater number of iterations is more computationally intensive.
139
+ *
140
+ * @defaultValue 4
141
+ */
142
+ maxVelocityIterations?: number;
143
+ /**
144
+ * The maximal distance separating two objects that will generate predictive contacts
145
+ *
146
+ * @defaultValue 0.002
147
+ *
148
+ */
149
+ predictionDistance?: number;
150
+ /**
151
+ * The Error Reduction Parameter in between 0 and 1, is the proportion of the positional error to be corrected at each time step
152
+ * @defaultValue 0.8
153
+ */
154
+ erp?: number;
119
155
  /**
120
156
  * Set the base automatic colliders for this physics world
121
157
  * All Meshes inside RigidBodies will generate a collider
@@ -4,7 +4,6 @@ export type { InstancedRigidBodiesProps, InstancedRigidBodyProps } from "./compo
4
4
  export type { CylinderColliderProps, BallColliderProps, CapsuleColliderProps, ConeColliderProps, ConvexHullColliderProps, CuboidColliderProps, HeightfieldColliderProps, RoundCuboidColliderProps, TrimeshColliderProps, ColliderOptionsRequiredArgs } from "./components/AnyCollider";
5
5
  export type { PhysicsProps, RapierContext, WorldStepCallback } from "./components/Physics";
6
6
  export type { MeshColliderProps } from "./components/MeshCollider";
7
- export type { WorldApi } from "./utils/api";
8
7
  export { Physics } from "./components/Physics";
9
8
  export { RigidBody } from "./components/RigidBody";
10
9
  export { MeshCollider } from "./components/MeshCollider";
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Creates a proxy that will create a singleton instance of the given class
3
+ * when a property is accessed, and not before.
4
+ *
5
+ * @returns A proxy and a reset function, so that the instance can created again
6
+ */
7
+ export declare const createSingletonProxy: <SingletonClass extends object, CreationFn extends () => SingletonClass = () => SingletonClass>(createInstance: CreationFn) => {
8
+ proxy: SingletonClass;
9
+ reset: () => void;
10
+ };
@@ -1,7 +1,6 @@
1
- import { Collider, RigidBody } from "@dimforge/rapier3d-compat";
1
+ import { Collider, RigidBody, World } from "@dimforge/rapier3d-compat";
2
2
  import { BufferGeometry, Object3D, Vector3 } from "three";
3
3
  import { ColliderProps, RigidBodyProps } from "..";
4
- import { WorldApi } from "./api";
5
4
  import { ColliderState, ColliderStateMap, EventMap } from "../components/Physics";
6
5
  import { ColliderShape, RigidBodyAutoCollider } from "../types";
7
6
  export declare const scaleColliderArgs: (shape: ColliderShape, args: (number | ArrayLike<number> | {
@@ -13,7 +12,7 @@ export declare const scaleColliderArgs: (shape: ColliderShape, args: (number | A
13
12
  y: number;
14
13
  z: number;
15
14
  })[];
16
- export declare const createColliderFromOptions: (options: ColliderProps, world: WorldApi, scale: Vector3, getRigidBody?: () => RigidBody) => Collider;
15
+ export declare const createColliderFromOptions: (options: ColliderProps, world: World, scale: Vector3, getRigidBody?: () => RigidBody) => Collider;
17
16
  declare type ImmutableColliderOptions = (keyof ColliderProps)[];
18
17
  export declare const immutableColliderOptions: ImmutableColliderOptions;
19
18
  export declare const setColliderOptions: (collider: Collider, options: ColliderProps, states: ColliderStateMap) => void;
@@ -72,40 +72,6 @@ function _objectSpread2(target) {
72
72
  return target;
73
73
  }
74
74
 
75
- const createWorldApi = getWorld => {
76
- return {
77
- raw: () => getWorld(),
78
- getCollider: handle => getWorld().getCollider(handle),
79
- getRigidBody: handle => getWorld().getRigidBody(handle),
80
- createRigidBody: desc => getWorld().createRigidBody(desc),
81
- createCollider: (desc, rigidBody) => getWorld().createCollider(desc, rigidBody),
82
- removeRigidBody: rigidBody => {
83
- if (!getWorld().bodies.contains(rigidBody.handle)) return;
84
- getWorld().removeRigidBody(rigidBody);
85
- },
86
- removeCollider: (collider, wakeUp = true) => {
87
- if (!getWorld().colliders.contains(collider.handle)) return;
88
- getWorld().removeCollider(collider, wakeUp);
89
- },
90
- createImpulseJoint: (params, rigidBodyA, rigidBodyB, wakeUp = true) => getWorld().createImpulseJoint(params, rigidBodyA, rigidBodyB, wakeUp),
91
- removeImpulseJoint: (joint, wakeUp = true) => {
92
- if (!getWorld().impulseJoints.contains(joint.handle)) return;
93
- getWorld().removeImpulseJoint(joint, wakeUp);
94
- },
95
- forEachCollider: callback => getWorld().forEachCollider(callback),
96
- setGravity: ({
97
- x,
98
- y,
99
- z
100
- }) => getWorld().gravity = {
101
- x,
102
- y,
103
- z
104
- },
105
- debugRender: () => getWorld().debugRender()
106
- };
107
- };
108
-
109
75
  const _quaternion = new three.Quaternion();
110
76
  new three.Euler();
111
77
  const _vector3 = new three.Vector3();
@@ -684,30 +650,37 @@ const Debug = /*#__PURE__*/React.memo(() => {
684
650
  });
685
651
 
686
652
  /**
687
- * Initiate an instance and return a safe getter
653
+ * Creates a proxy that will create a singleton instance of the given class
654
+ * when a property is accessed, and not before.
655
+ *
656
+ * @returns A proxy and a reset function, so that the instance can created again
688
657
  */
658
+ const createSingletonProxy = createInstance => {
659
+ let instance;
660
+ const handler = {
661
+ get(target, prop) {
662
+ if (!instance) {
663
+ instance = createInstance();
664
+ }
689
665
 
690
- const useImperativeInstance = (createFn, destroyFn, dependencyList) => {
691
- const ref = React.useRef();
692
- const getInstance = React.useCallback(() => {
693
- if (!ref.current) {
694
- ref.current = createFn();
666
+ return Reflect.get(instance, prop);
695
667
  }
696
668
 
697
- return ref.current;
698
- }, dependencyList);
699
- React.useEffect(() => {
700
- // Save the destroy function and instance
701
- const instance = getInstance();
669
+ };
670
+ const proxy = new Proxy({}, handler);
702
671
 
703
- const destroy = () => destroyFn(instance);
672
+ const reset = () => {
673
+ instance = undefined;
674
+ };
675
+ /**
676
+ * Return the proxy and a reset function
677
+ */
704
678
 
705
- return () => {
706
- destroy();
707
- ref.current = undefined;
708
- };
709
- }, [getInstance]);
710
- return getInstance;
679
+
680
+ return {
681
+ proxy,
682
+ reset
683
+ };
711
684
  };
712
685
 
713
686
  const rapierContext = /*#__PURE__*/React.createContext(undefined);
@@ -745,52 +718,67 @@ const importRapier = async () => {
745
718
  * The main physics component used to create a physics world.
746
719
  * @category Components
747
720
  */
748
- const Physics = ({
749
- colliders: _colliders = "cuboid",
750
- gravity: _gravity = [0, -9.81, 0],
751
- children,
752
- timeStep: _timeStep = 1 / 60,
753
- paused: _paused = false,
754
- interpolate: _interpolate = true,
755
- updatePriority,
756
- updateLoop: _updateLoop = "follow",
757
- debug: _debug = false
758
- }) => {
721
+ const Physics = props => {
722
+ const {
723
+ colliders = "cuboid",
724
+ children,
725
+ timeStep = 1 / 60,
726
+ paused = false,
727
+ interpolate = true,
728
+ updatePriority,
729
+ updateLoop = "follow",
730
+ debug = false,
731
+ gravity = [0, -9.81, 0],
732
+ maxStabilizationIterations = 1,
733
+ maxVelocityFrictionIterations = 8,
734
+ maxVelocityIterations = 4,
735
+ predictionDistance = 0.002,
736
+ erp = 0.8
737
+ } = props;
759
738
  const rapier = useAsset.useAsset(importRapier);
760
739
  const {
761
740
  invalidate
762
- } = fiber.useThree(); // Init World
763
-
764
- const getWorld = useImperativeInstance(() => {
765
- return new rapier.World(vectorArrayToVector3(_gravity));
766
- }, world => {
767
- world.free();
768
- }, []);
741
+ } = fiber.useThree();
769
742
  const rigidBodyStates = useConst(() => new Map());
770
743
  const colliderStates = useConst(() => new Map());
771
744
  const rigidBodyEvents = useConst(() => new Map());
772
745
  const colliderEvents = useConst(() => new Map());
773
746
  const eventQueue = useConst(() => new rapier3dCompat.EventQueue(false));
774
747
  const beforeStepCallbacks = useConst(() => new Set());
775
- const afterStepCallbacks = useConst(() => new Set()); // Update gravity
748
+ const afterStepCallbacks = useConst(() => new Set());
749
+ /**
750
+ * Initiate the world
751
+ * This creates a singleton proxy, so that the world is only created when
752
+ * something within it is accessed.
753
+ */
776
754
 
755
+ const {
756
+ proxy: worldProxy,
757
+ reset: resetWorldProxy
758
+ } = useConst(() => createSingletonProxy(() => new rapier.World(vectorArrayToVector3(gravity))));
777
759
  React.useEffect(() => {
778
- const world = getWorld();
760
+ return () => {
761
+ worldProxy.free();
762
+ resetWorldProxy();
763
+ };
764
+ }, []); // Update mutable props
779
765
 
780
- if (world) {
781
- world.gravity = vectorArrayToVector3(_gravity);
782
- }
783
- }, [_gravity]);
784
- const api = React.useMemo(() => createWorldApi(getWorld), []);
766
+ React.useEffect(() => {
767
+ worldProxy.gravity = vectorArrayToVector3(gravity);
768
+ worldProxy.integrationParameters.maxStabilizationIterations = maxStabilizationIterations;
769
+ worldProxy.integrationParameters.maxVelocityFrictionIterations = maxVelocityFrictionIterations;
770
+ worldProxy.integrationParameters.maxVelocityIterations = maxVelocityIterations;
771
+ worldProxy.integrationParameters.predictionDistance = predictionDistance;
772
+ worldProxy.integrationParameters.erp = erp;
773
+ }, [worldProxy, ...gravity, maxStabilizationIterations, maxVelocityIterations, maxVelocityFrictionIterations, predictionDistance, erp]);
785
774
  const getSourceFromColliderHandle = React.useCallback(handle => {
786
775
  var _collider$parent;
787
776
 
788
- const world = getWorld();
789
- const collider = world.getCollider(handle);
777
+ const collider = worldProxy.getCollider(handle);
790
778
  const colEvents = colliderEvents.get(handle);
791
779
  const colliderState = colliderStates.get(handle);
792
780
  const rigidBodyHandle = collider === null || collider === void 0 ? void 0 : (_collider$parent = collider.parent()) === null || _collider$parent === void 0 ? void 0 : _collider$parent.handle;
793
- const rigidBody = rigidBodyHandle !== undefined ? world.getRigidBody(rigidBodyHandle) : undefined;
781
+ const rigidBody = rigidBodyHandle !== undefined ? worldProxy.getRigidBody(rigidBodyHandle) : undefined;
794
782
  const rbEvents = rigidBody && rigidBodyHandle !== undefined ? rigidBodyEvents.get(rigidBodyHandle) : undefined;
795
783
  const rigidBodyState = rigidBodyHandle !== undefined ? rigidBodyStates.get(rigidBodyHandle) : undefined;
796
784
  const source = {
@@ -812,11 +800,11 @@ const Physics = ({
812
800
  accumulator: 0
813
801
  });
814
802
  const step = React.useCallback(dt => {
815
- const world = getWorld();
803
+ const world = worldProxy;
816
804
  /* Check if the timestep is supposed to be variable. We'll do this here
817
805
  once so we don't have to string-check every frame. */
818
806
 
819
- const timeStepVariable = _timeStep === "vary";
807
+ const timeStepVariable = timeStep === "vary";
820
808
  /**
821
809
  * Fixed timeStep simulation progression
822
810
  * @see https://gafferongames.com/post/fix_your_timestep/
@@ -827,12 +815,12 @@ const Physics = ({
827
815
  const stepWorld = () => {
828
816
  // Trigger beforeStep callbacks
829
817
  beforeStepCallbacks.forEach(callback => {
830
- callback.current(api);
818
+ callback.current(worldProxy);
831
819
  });
832
820
  world.step(eventQueue); // Trigger afterStep callbacks
833
821
 
834
822
  afterStepCallbacks.forEach(callback => {
835
- callback.current(api);
823
+ callback.current(worldProxy);
836
824
  });
837
825
  };
838
826
 
@@ -840,15 +828,15 @@ const Physics = ({
840
828
  world.timestep = clampedDelta;
841
829
  stepWorld();
842
830
  } else {
843
- world.timestep = _timeStep; // don't step time forwards if paused
831
+ world.timestep = timeStep; // don't step time forwards if paused
844
832
  // Increase accumulator
845
833
 
846
834
  steppingState.accumulator += clampedDelta;
847
835
 
848
- while (steppingState.accumulator >= _timeStep) {
836
+ while (steppingState.accumulator >= timeStep) {
849
837
  // Set up previous state
850
838
  // needed for accurate interpolations if the world steps more than once
851
- if (_interpolate) {
839
+ if (interpolate) {
852
840
  steppingState.previousState = {};
853
841
  world.forEachRigidBody(body => {
854
842
  steppingState.previousState[body.handle] = {
@@ -859,11 +847,11 @@ const Physics = ({
859
847
  }
860
848
 
861
849
  stepWorld();
862
- steppingState.accumulator -= _timeStep;
850
+ steppingState.accumulator -= timeStep;
863
851
  }
864
852
  }
865
853
 
866
- const interpolationAlpha = timeStepVariable || !_interpolate || _paused ? 1 : steppingState.accumulator / _timeStep; // Update meshes
854
+ const interpolationAlpha = timeStepVariable || !interpolate || paused ? 1 : steppingState.accumulator / timeStep; // Update meshes
867
855
 
868
856
  rigidBodyStates.forEach((state, handle) => {
869
857
  const rigidBody = world.getRigidBody(handle);
@@ -1019,13 +1007,13 @@ const Physics = ({
1019
1007
  world.forEachActiveRigidBody(() => {
1020
1008
  invalidate();
1021
1009
  });
1022
- }, [_paused, _timeStep, _interpolate]);
1010
+ }, [paused, timeStep, interpolate]);
1023
1011
  const context = React.useMemo(() => ({
1024
1012
  rapier,
1025
- world: api,
1013
+ world: worldProxy,
1026
1014
  physicsOptions: {
1027
- colliders: _colliders,
1028
- gravity: _gravity
1015
+ colliders,
1016
+ gravity
1029
1017
  },
1030
1018
  rigidBodyStates,
1031
1019
  colliderStates,
@@ -1033,22 +1021,22 @@ const Physics = ({
1033
1021
  colliderEvents,
1034
1022
  beforeStepCallbacks,
1035
1023
  afterStepCallbacks,
1036
- isPaused: _paused,
1037
- isDebug: _debug,
1024
+ isPaused: paused,
1025
+ isDebug: debug,
1038
1026
  step
1039
- }), [_paused, step, _debug, _colliders, _gravity]);
1027
+ }), [paused, step, debug, colliders, gravity]);
1040
1028
  const stepCallback = React.useCallback(delta => {
1041
- if (!_paused) {
1029
+ if (!paused) {
1042
1030
  step(delta);
1043
1031
  }
1044
- }, [_paused, step]);
1032
+ }, [paused, step]);
1045
1033
  return /*#__PURE__*/React__default["default"].createElement(rapierContext.Provider, {
1046
1034
  value: context
1047
1035
  }, /*#__PURE__*/React__default["default"].createElement(FrameStepper$1, {
1048
1036
  onStep: stepCallback,
1049
- type: _updateLoop,
1037
+ type: updateLoop,
1050
1038
  updatePriority: updatePriority
1051
- }), _debug && /*#__PURE__*/React__default["default"].createElement(Debug, null), children);
1039
+ }), debug && /*#__PURE__*/React__default["default"].createElement(Debug, null), children);
1052
1040
  };
1053
1041
 
1054
1042
  function _extends() {
@@ -1068,6 +1056,33 @@ function _extends() {
1068
1056
  return _extends.apply(this, arguments);
1069
1057
  }
1070
1058
 
1059
+ /**
1060
+ * Initiate an instance and return a safe getter
1061
+ */
1062
+
1063
+ const useImperativeInstance = (createFn, destroyFn, dependencyList) => {
1064
+ const ref = React.useRef();
1065
+ const getInstance = React.useCallback(() => {
1066
+ if (!ref.current) {
1067
+ ref.current = createFn();
1068
+ }
1069
+
1070
+ return ref.current;
1071
+ }, dependencyList);
1072
+ React.useEffect(() => {
1073
+ // Save the destroy function and instance
1074
+ const instance = getInstance();
1075
+
1076
+ const destroy = () => destroyFn(instance);
1077
+
1078
+ return () => {
1079
+ destroy();
1080
+ ref.current = undefined;
1081
+ };
1082
+ }, [getInstance]);
1083
+ return getInstance;
1084
+ };
1085
+
1071
1086
  /**
1072
1087
  * Takes an object resembling a Vector3 and returs a Three.Vector3
1073
1088
  * @category Math helpers
@@ -1167,7 +1182,9 @@ const AnyCollider = /*#__PURE__*/React.memo( /*#__PURE__*/React.forwardRef((prop
1167
1182
  colliderRef.current = collider;
1168
1183
  return collider;
1169
1184
  }, collider => {
1170
- world.removeCollider(collider);
1185
+ if (world.getCollider(collider.handle)) {
1186
+ world.removeCollider(collider, true);
1187
+ }
1171
1188
  }, [...immutablePropArray, rigidBodyContext]);
1172
1189
  React.useEffect(() => {
1173
1190
  const collider = getInstance();
@@ -1503,7 +1520,9 @@ const RigidBody = /*#__PURE__*/React.memo( /*#__PURE__*/React.forwardRef((props,
1503
1520
  rigidBodyRef.current = rigidBody;
1504
1521
  return rigidBody;
1505
1522
  }, rigidBody => {
1506
- world.removeRigidBody(rigidBody);
1523
+ if (world.getRigidBody(rigidBody.handle)) {
1524
+ world.removeRigidBody(rigidBody);
1525
+ }
1507
1526
  }, immutablePropArray); // Only provide a object state after the ref has been set
1508
1527
 
1509
1528
  React.useEffect(() => {
@@ -1669,15 +1688,17 @@ const useImpulseJoint = (body1, body2, params) => {
1669
1688
  const jointRef = React.useRef();
1670
1689
  useImperativeInstance(() => {
1671
1690
  if (body1.current && body2.current) {
1672
- const newJoint = world.createImpulseJoint(params, body1.current, body2.current);
1673
- jointRef.current = newJoint; // console.log(body1.current, body2.current, newJoint);
1674
-
1691
+ const newJoint = world.createImpulseJoint(params, body1.current, body2.current, true);
1692
+ jointRef.current = newJoint;
1675
1693
  return newJoint;
1676
1694
  }
1677
1695
  }, joint => {
1678
1696
  if (joint) {
1679
1697
  jointRef.current = undefined;
1680
- world.removeImpulseJoint(joint);
1698
+
1699
+ if (world.getImpulseJoint(joint.handle)) {
1700
+ world.removeImpulseJoint(joint, true);
1701
+ }
1681
1702
  }
1682
1703
  }, []);
1683
1704
  return jointRef;
@@ -72,40 +72,6 @@ function _objectSpread2(target) {
72
72
  return target;
73
73
  }
74
74
 
75
- const createWorldApi = getWorld => {
76
- return {
77
- raw: () => getWorld(),
78
- getCollider: handle => getWorld().getCollider(handle),
79
- getRigidBody: handle => getWorld().getRigidBody(handle),
80
- createRigidBody: desc => getWorld().createRigidBody(desc),
81
- createCollider: (desc, rigidBody) => getWorld().createCollider(desc, rigidBody),
82
- removeRigidBody: rigidBody => {
83
- if (!getWorld().bodies.contains(rigidBody.handle)) return;
84
- getWorld().removeRigidBody(rigidBody);
85
- },
86
- removeCollider: (collider, wakeUp = true) => {
87
- if (!getWorld().colliders.contains(collider.handle)) return;
88
- getWorld().removeCollider(collider, wakeUp);
89
- },
90
- createImpulseJoint: (params, rigidBodyA, rigidBodyB, wakeUp = true) => getWorld().createImpulseJoint(params, rigidBodyA, rigidBodyB, wakeUp),
91
- removeImpulseJoint: (joint, wakeUp = true) => {
92
- if (!getWorld().impulseJoints.contains(joint.handle)) return;
93
- getWorld().removeImpulseJoint(joint, wakeUp);
94
- },
95
- forEachCollider: callback => getWorld().forEachCollider(callback),
96
- setGravity: ({
97
- x,
98
- y,
99
- z
100
- }) => getWorld().gravity = {
101
- x,
102
- y,
103
- z
104
- },
105
- debugRender: () => getWorld().debugRender()
106
- };
107
- };
108
-
109
75
  const _quaternion = new three.Quaternion();
110
76
  new three.Euler();
111
77
  const _vector3 = new three.Vector3();
@@ -684,30 +650,37 @@ const Debug = /*#__PURE__*/React.memo(() => {
684
650
  });
685
651
 
686
652
  /**
687
- * Initiate an instance and return a safe getter
653
+ * Creates a proxy that will create a singleton instance of the given class
654
+ * when a property is accessed, and not before.
655
+ *
656
+ * @returns A proxy and a reset function, so that the instance can created again
688
657
  */
658
+ const createSingletonProxy = createInstance => {
659
+ let instance;
660
+ const handler = {
661
+ get(target, prop) {
662
+ if (!instance) {
663
+ instance = createInstance();
664
+ }
689
665
 
690
- const useImperativeInstance = (createFn, destroyFn, dependencyList) => {
691
- const ref = React.useRef();
692
- const getInstance = React.useCallback(() => {
693
- if (!ref.current) {
694
- ref.current = createFn();
666
+ return Reflect.get(instance, prop);
695
667
  }
696
668
 
697
- return ref.current;
698
- }, dependencyList);
699
- React.useEffect(() => {
700
- // Save the destroy function and instance
701
- const instance = getInstance();
669
+ };
670
+ const proxy = new Proxy({}, handler);
702
671
 
703
- const destroy = () => destroyFn(instance);
672
+ const reset = () => {
673
+ instance = undefined;
674
+ };
675
+ /**
676
+ * Return the proxy and a reset function
677
+ */
704
678
 
705
- return () => {
706
- destroy();
707
- ref.current = undefined;
708
- };
709
- }, [getInstance]);
710
- return getInstance;
679
+
680
+ return {
681
+ proxy,
682
+ reset
683
+ };
711
684
  };
712
685
 
713
686
  const rapierContext = /*#__PURE__*/React.createContext(undefined);
@@ -745,52 +718,67 @@ const importRapier = async () => {
745
718
  * The main physics component used to create a physics world.
746
719
  * @category Components
747
720
  */
748
- const Physics = ({
749
- colliders: _colliders = "cuboid",
750
- gravity: _gravity = [0, -9.81, 0],
751
- children,
752
- timeStep: _timeStep = 1 / 60,
753
- paused: _paused = false,
754
- interpolate: _interpolate = true,
755
- updatePriority,
756
- updateLoop: _updateLoop = "follow",
757
- debug: _debug = false
758
- }) => {
721
+ const Physics = props => {
722
+ const {
723
+ colliders = "cuboid",
724
+ children,
725
+ timeStep = 1 / 60,
726
+ paused = false,
727
+ interpolate = true,
728
+ updatePriority,
729
+ updateLoop = "follow",
730
+ debug = false,
731
+ gravity = [0, -9.81, 0],
732
+ maxStabilizationIterations = 1,
733
+ maxVelocityFrictionIterations = 8,
734
+ maxVelocityIterations = 4,
735
+ predictionDistance = 0.002,
736
+ erp = 0.8
737
+ } = props;
759
738
  const rapier = useAsset.useAsset(importRapier);
760
739
  const {
761
740
  invalidate
762
- } = fiber.useThree(); // Init World
763
-
764
- const getWorld = useImperativeInstance(() => {
765
- return new rapier.World(vectorArrayToVector3(_gravity));
766
- }, world => {
767
- world.free();
768
- }, []);
741
+ } = fiber.useThree();
769
742
  const rigidBodyStates = useConst(() => new Map());
770
743
  const colliderStates = useConst(() => new Map());
771
744
  const rigidBodyEvents = useConst(() => new Map());
772
745
  const colliderEvents = useConst(() => new Map());
773
746
  const eventQueue = useConst(() => new rapier3dCompat.EventQueue(false));
774
747
  const beforeStepCallbacks = useConst(() => new Set());
775
- const afterStepCallbacks = useConst(() => new Set()); // Update gravity
748
+ const afterStepCallbacks = useConst(() => new Set());
749
+ /**
750
+ * Initiate the world
751
+ * This creates a singleton proxy, so that the world is only created when
752
+ * something within it is accessed.
753
+ */
776
754
 
755
+ const {
756
+ proxy: worldProxy,
757
+ reset: resetWorldProxy
758
+ } = useConst(() => createSingletonProxy(() => new rapier.World(vectorArrayToVector3(gravity))));
777
759
  React.useEffect(() => {
778
- const world = getWorld();
760
+ return () => {
761
+ worldProxy.free();
762
+ resetWorldProxy();
763
+ };
764
+ }, []); // Update mutable props
779
765
 
780
- if (world) {
781
- world.gravity = vectorArrayToVector3(_gravity);
782
- }
783
- }, [_gravity]);
784
- const api = React.useMemo(() => createWorldApi(getWorld), []);
766
+ React.useEffect(() => {
767
+ worldProxy.gravity = vectorArrayToVector3(gravity);
768
+ worldProxy.integrationParameters.maxStabilizationIterations = maxStabilizationIterations;
769
+ worldProxy.integrationParameters.maxVelocityFrictionIterations = maxVelocityFrictionIterations;
770
+ worldProxy.integrationParameters.maxVelocityIterations = maxVelocityIterations;
771
+ worldProxy.integrationParameters.predictionDistance = predictionDistance;
772
+ worldProxy.integrationParameters.erp = erp;
773
+ }, [worldProxy, ...gravity, maxStabilizationIterations, maxVelocityIterations, maxVelocityFrictionIterations, predictionDistance, erp]);
785
774
  const getSourceFromColliderHandle = React.useCallback(handle => {
786
775
  var _collider$parent;
787
776
 
788
- const world = getWorld();
789
- const collider = world.getCollider(handle);
777
+ const collider = worldProxy.getCollider(handle);
790
778
  const colEvents = colliderEvents.get(handle);
791
779
  const colliderState = colliderStates.get(handle);
792
780
  const rigidBodyHandle = collider === null || collider === void 0 ? void 0 : (_collider$parent = collider.parent()) === null || _collider$parent === void 0 ? void 0 : _collider$parent.handle;
793
- const rigidBody = rigidBodyHandle !== undefined ? world.getRigidBody(rigidBodyHandle) : undefined;
781
+ const rigidBody = rigidBodyHandle !== undefined ? worldProxy.getRigidBody(rigidBodyHandle) : undefined;
794
782
  const rbEvents = rigidBody && rigidBodyHandle !== undefined ? rigidBodyEvents.get(rigidBodyHandle) : undefined;
795
783
  const rigidBodyState = rigidBodyHandle !== undefined ? rigidBodyStates.get(rigidBodyHandle) : undefined;
796
784
  const source = {
@@ -812,11 +800,11 @@ const Physics = ({
812
800
  accumulator: 0
813
801
  });
814
802
  const step = React.useCallback(dt => {
815
- const world = getWorld();
803
+ const world = worldProxy;
816
804
  /* Check if the timestep is supposed to be variable. We'll do this here
817
805
  once so we don't have to string-check every frame. */
818
806
 
819
- const timeStepVariable = _timeStep === "vary";
807
+ const timeStepVariable = timeStep === "vary";
820
808
  /**
821
809
  * Fixed timeStep simulation progression
822
810
  * @see https://gafferongames.com/post/fix_your_timestep/
@@ -827,12 +815,12 @@ const Physics = ({
827
815
  const stepWorld = () => {
828
816
  // Trigger beforeStep callbacks
829
817
  beforeStepCallbacks.forEach(callback => {
830
- callback.current(api);
818
+ callback.current(worldProxy);
831
819
  });
832
820
  world.step(eventQueue); // Trigger afterStep callbacks
833
821
 
834
822
  afterStepCallbacks.forEach(callback => {
835
- callback.current(api);
823
+ callback.current(worldProxy);
836
824
  });
837
825
  };
838
826
 
@@ -840,15 +828,15 @@ const Physics = ({
840
828
  world.timestep = clampedDelta;
841
829
  stepWorld();
842
830
  } else {
843
- world.timestep = _timeStep; // don't step time forwards if paused
831
+ world.timestep = timeStep; // don't step time forwards if paused
844
832
  // Increase accumulator
845
833
 
846
834
  steppingState.accumulator += clampedDelta;
847
835
 
848
- while (steppingState.accumulator >= _timeStep) {
836
+ while (steppingState.accumulator >= timeStep) {
849
837
  // Set up previous state
850
838
  // needed for accurate interpolations if the world steps more than once
851
- if (_interpolate) {
839
+ if (interpolate) {
852
840
  steppingState.previousState = {};
853
841
  world.forEachRigidBody(body => {
854
842
  steppingState.previousState[body.handle] = {
@@ -859,11 +847,11 @@ const Physics = ({
859
847
  }
860
848
 
861
849
  stepWorld();
862
- steppingState.accumulator -= _timeStep;
850
+ steppingState.accumulator -= timeStep;
863
851
  }
864
852
  }
865
853
 
866
- const interpolationAlpha = timeStepVariable || !_interpolate || _paused ? 1 : steppingState.accumulator / _timeStep; // Update meshes
854
+ const interpolationAlpha = timeStepVariable || !interpolate || paused ? 1 : steppingState.accumulator / timeStep; // Update meshes
867
855
 
868
856
  rigidBodyStates.forEach((state, handle) => {
869
857
  const rigidBody = world.getRigidBody(handle);
@@ -1019,13 +1007,13 @@ const Physics = ({
1019
1007
  world.forEachActiveRigidBody(() => {
1020
1008
  invalidate();
1021
1009
  });
1022
- }, [_paused, _timeStep, _interpolate]);
1010
+ }, [paused, timeStep, interpolate]);
1023
1011
  const context = React.useMemo(() => ({
1024
1012
  rapier,
1025
- world: api,
1013
+ world: worldProxy,
1026
1014
  physicsOptions: {
1027
- colliders: _colliders,
1028
- gravity: _gravity
1015
+ colliders,
1016
+ gravity
1029
1017
  },
1030
1018
  rigidBodyStates,
1031
1019
  colliderStates,
@@ -1033,22 +1021,22 @@ const Physics = ({
1033
1021
  colliderEvents,
1034
1022
  beforeStepCallbacks,
1035
1023
  afterStepCallbacks,
1036
- isPaused: _paused,
1037
- isDebug: _debug,
1024
+ isPaused: paused,
1025
+ isDebug: debug,
1038
1026
  step
1039
- }), [_paused, step, _debug, _colliders, _gravity]);
1027
+ }), [paused, step, debug, colliders, gravity]);
1040
1028
  const stepCallback = React.useCallback(delta => {
1041
- if (!_paused) {
1029
+ if (!paused) {
1042
1030
  step(delta);
1043
1031
  }
1044
- }, [_paused, step]);
1032
+ }, [paused, step]);
1045
1033
  return /*#__PURE__*/React__default["default"].createElement(rapierContext.Provider, {
1046
1034
  value: context
1047
1035
  }, /*#__PURE__*/React__default["default"].createElement(FrameStepper$1, {
1048
1036
  onStep: stepCallback,
1049
- type: _updateLoop,
1037
+ type: updateLoop,
1050
1038
  updatePriority: updatePriority
1051
- }), _debug && /*#__PURE__*/React__default["default"].createElement(Debug, null), children);
1039
+ }), debug && /*#__PURE__*/React__default["default"].createElement(Debug, null), children);
1052
1040
  };
1053
1041
 
1054
1042
  function _extends() {
@@ -1068,6 +1056,33 @@ function _extends() {
1068
1056
  return _extends.apply(this, arguments);
1069
1057
  }
1070
1058
 
1059
+ /**
1060
+ * Initiate an instance and return a safe getter
1061
+ */
1062
+
1063
+ const useImperativeInstance = (createFn, destroyFn, dependencyList) => {
1064
+ const ref = React.useRef();
1065
+ const getInstance = React.useCallback(() => {
1066
+ if (!ref.current) {
1067
+ ref.current = createFn();
1068
+ }
1069
+
1070
+ return ref.current;
1071
+ }, dependencyList);
1072
+ React.useEffect(() => {
1073
+ // Save the destroy function and instance
1074
+ const instance = getInstance();
1075
+
1076
+ const destroy = () => destroyFn(instance);
1077
+
1078
+ return () => {
1079
+ destroy();
1080
+ ref.current = undefined;
1081
+ };
1082
+ }, [getInstance]);
1083
+ return getInstance;
1084
+ };
1085
+
1071
1086
  /**
1072
1087
  * Takes an object resembling a Vector3 and returs a Three.Vector3
1073
1088
  * @category Math helpers
@@ -1167,7 +1182,9 @@ const AnyCollider = /*#__PURE__*/React.memo( /*#__PURE__*/React.forwardRef((prop
1167
1182
  colliderRef.current = collider;
1168
1183
  return collider;
1169
1184
  }, collider => {
1170
- world.removeCollider(collider);
1185
+ if (world.getCollider(collider.handle)) {
1186
+ world.removeCollider(collider, true);
1187
+ }
1171
1188
  }, [...immutablePropArray, rigidBodyContext]);
1172
1189
  React.useEffect(() => {
1173
1190
  const collider = getInstance();
@@ -1503,7 +1520,9 @@ const RigidBody = /*#__PURE__*/React.memo( /*#__PURE__*/React.forwardRef((props,
1503
1520
  rigidBodyRef.current = rigidBody;
1504
1521
  return rigidBody;
1505
1522
  }, rigidBody => {
1506
- world.removeRigidBody(rigidBody);
1523
+ if (world.getRigidBody(rigidBody.handle)) {
1524
+ world.removeRigidBody(rigidBody);
1525
+ }
1507
1526
  }, immutablePropArray); // Only provide a object state after the ref has been set
1508
1527
 
1509
1528
  React.useEffect(() => {
@@ -1669,15 +1688,17 @@ const useImpulseJoint = (body1, body2, params) => {
1669
1688
  const jointRef = React.useRef();
1670
1689
  useImperativeInstance(() => {
1671
1690
  if (body1.current && body2.current) {
1672
- const newJoint = world.createImpulseJoint(params, body1.current, body2.current);
1673
- jointRef.current = newJoint; // console.log(body1.current, body2.current, newJoint);
1674
-
1691
+ const newJoint = world.createImpulseJoint(params, body1.current, body2.current, true);
1692
+ jointRef.current = newJoint;
1675
1693
  return newJoint;
1676
1694
  }
1677
1695
  }, joint => {
1678
1696
  if (joint) {
1679
1697
  jointRef.current = undefined;
1680
- world.removeImpulseJoint(joint);
1698
+
1699
+ if (world.getImpulseJoint(joint.handle)) {
1700
+ world.removeImpulseJoint(joint, true);
1701
+ }
1681
1702
  }
1682
1703
  }, []);
1683
1704
  return jointRef;
@@ -1,7 +1,7 @@
1
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, useEffect, memo, useMemo, useContext, useState, useCallback, createContext, forwardRef, Fragment } from 'react';
4
+ import React, { useRef, useEffect, memo, useMemo, useContext, useState, createContext, useCallback, forwardRef, Fragment } from 'react';
5
5
  import { Quaternion, Euler, Vector3, Object3D, Matrix4, BufferAttribute, MathUtils, DynamicDrawUsage } from 'three';
6
6
  import { useAsset } from 'use-asset';
7
7
  import { mergeVertices } from 'three-stdlib';
@@ -47,40 +47,6 @@ function _objectSpread2(target) {
47
47
  return target;
48
48
  }
49
49
 
50
- const createWorldApi = getWorld => {
51
- return {
52
- raw: () => getWorld(),
53
- getCollider: handle => getWorld().getCollider(handle),
54
- getRigidBody: handle => getWorld().getRigidBody(handle),
55
- createRigidBody: desc => getWorld().createRigidBody(desc),
56
- createCollider: (desc, rigidBody) => getWorld().createCollider(desc, rigidBody),
57
- removeRigidBody: rigidBody => {
58
- if (!getWorld().bodies.contains(rigidBody.handle)) return;
59
- getWorld().removeRigidBody(rigidBody);
60
- },
61
- removeCollider: (collider, wakeUp = true) => {
62
- if (!getWorld().colliders.contains(collider.handle)) return;
63
- getWorld().removeCollider(collider, wakeUp);
64
- },
65
- createImpulseJoint: (params, rigidBodyA, rigidBodyB, wakeUp = true) => getWorld().createImpulseJoint(params, rigidBodyA, rigidBodyB, wakeUp),
66
- removeImpulseJoint: (joint, wakeUp = true) => {
67
- if (!getWorld().impulseJoints.contains(joint.handle)) return;
68
- getWorld().removeImpulseJoint(joint, wakeUp);
69
- },
70
- forEachCollider: callback => getWorld().forEachCollider(callback),
71
- setGravity: ({
72
- x,
73
- y,
74
- z
75
- }) => getWorld().gravity = {
76
- x,
77
- y,
78
- z
79
- },
80
- debugRender: () => getWorld().debugRender()
81
- };
82
- };
83
-
84
50
  const _quaternion = new Quaternion();
85
51
  new Euler();
86
52
  const _vector3 = new Vector3();
@@ -659,30 +625,37 @@ const Debug = /*#__PURE__*/memo(() => {
659
625
  });
660
626
 
661
627
  /**
662
- * Initiate an instance and return a safe getter
628
+ * Creates a proxy that will create a singleton instance of the given class
629
+ * when a property is accessed, and not before.
630
+ *
631
+ * @returns A proxy and a reset function, so that the instance can created again
663
632
  */
633
+ const createSingletonProxy = createInstance => {
634
+ let instance;
635
+ const handler = {
636
+ get(target, prop) {
637
+ if (!instance) {
638
+ instance = createInstance();
639
+ }
664
640
 
665
- const useImperativeInstance = (createFn, destroyFn, dependencyList) => {
666
- const ref = useRef();
667
- const getInstance = useCallback(() => {
668
- if (!ref.current) {
669
- ref.current = createFn();
641
+ return Reflect.get(instance, prop);
670
642
  }
671
643
 
672
- return ref.current;
673
- }, dependencyList);
674
- useEffect(() => {
675
- // Save the destroy function and instance
676
- const instance = getInstance();
644
+ };
645
+ const proxy = new Proxy({}, handler);
677
646
 
678
- const destroy = () => destroyFn(instance);
647
+ const reset = () => {
648
+ instance = undefined;
649
+ };
650
+ /**
651
+ * Return the proxy and a reset function
652
+ */
679
653
 
680
- return () => {
681
- destroy();
682
- ref.current = undefined;
683
- };
684
- }, [getInstance]);
685
- return getInstance;
654
+
655
+ return {
656
+ proxy,
657
+ reset
658
+ };
686
659
  };
687
660
 
688
661
  const rapierContext = /*#__PURE__*/createContext(undefined);
@@ -720,52 +693,67 @@ const importRapier = async () => {
720
693
  * The main physics component used to create a physics world.
721
694
  * @category Components
722
695
  */
723
- const Physics = ({
724
- colliders: _colliders = "cuboid",
725
- gravity: _gravity = [0, -9.81, 0],
726
- children,
727
- timeStep: _timeStep = 1 / 60,
728
- paused: _paused = false,
729
- interpolate: _interpolate = true,
730
- updatePriority,
731
- updateLoop: _updateLoop = "follow",
732
- debug: _debug = false
733
- }) => {
696
+ const Physics = props => {
697
+ const {
698
+ colliders = "cuboid",
699
+ children,
700
+ timeStep = 1 / 60,
701
+ paused = false,
702
+ interpolate = true,
703
+ updatePriority,
704
+ updateLoop = "follow",
705
+ debug = false,
706
+ gravity = [0, -9.81, 0],
707
+ maxStabilizationIterations = 1,
708
+ maxVelocityFrictionIterations = 8,
709
+ maxVelocityIterations = 4,
710
+ predictionDistance = 0.002,
711
+ erp = 0.8
712
+ } = props;
734
713
  const rapier = useAsset(importRapier);
735
714
  const {
736
715
  invalidate
737
- } = useThree(); // Init World
738
-
739
- const getWorld = useImperativeInstance(() => {
740
- return new rapier.World(vectorArrayToVector3(_gravity));
741
- }, world => {
742
- world.free();
743
- }, []);
716
+ } = useThree();
744
717
  const rigidBodyStates = useConst(() => new Map());
745
718
  const colliderStates = useConst(() => new Map());
746
719
  const rigidBodyEvents = useConst(() => new Map());
747
720
  const colliderEvents = useConst(() => new Map());
748
721
  const eventQueue = useConst(() => new EventQueue(false));
749
722
  const beforeStepCallbacks = useConst(() => new Set());
750
- const afterStepCallbacks = useConst(() => new Set()); // Update gravity
723
+ const afterStepCallbacks = useConst(() => new Set());
724
+ /**
725
+ * Initiate the world
726
+ * This creates a singleton proxy, so that the world is only created when
727
+ * something within it is accessed.
728
+ */
751
729
 
730
+ const {
731
+ proxy: worldProxy,
732
+ reset: resetWorldProxy
733
+ } = useConst(() => createSingletonProxy(() => new rapier.World(vectorArrayToVector3(gravity))));
752
734
  useEffect(() => {
753
- const world = getWorld();
735
+ return () => {
736
+ worldProxy.free();
737
+ resetWorldProxy();
738
+ };
739
+ }, []); // Update mutable props
754
740
 
755
- if (world) {
756
- world.gravity = vectorArrayToVector3(_gravity);
757
- }
758
- }, [_gravity]);
759
- const api = useMemo(() => createWorldApi(getWorld), []);
741
+ useEffect(() => {
742
+ worldProxy.gravity = vectorArrayToVector3(gravity);
743
+ worldProxy.integrationParameters.maxStabilizationIterations = maxStabilizationIterations;
744
+ worldProxy.integrationParameters.maxVelocityFrictionIterations = maxVelocityFrictionIterations;
745
+ worldProxy.integrationParameters.maxVelocityIterations = maxVelocityIterations;
746
+ worldProxy.integrationParameters.predictionDistance = predictionDistance;
747
+ worldProxy.integrationParameters.erp = erp;
748
+ }, [worldProxy, ...gravity, maxStabilizationIterations, maxVelocityIterations, maxVelocityFrictionIterations, predictionDistance, erp]);
760
749
  const getSourceFromColliderHandle = useCallback(handle => {
761
750
  var _collider$parent;
762
751
 
763
- const world = getWorld();
764
- const collider = world.getCollider(handle);
752
+ const collider = worldProxy.getCollider(handle);
765
753
  const colEvents = colliderEvents.get(handle);
766
754
  const colliderState = colliderStates.get(handle);
767
755
  const rigidBodyHandle = collider === null || collider === void 0 ? void 0 : (_collider$parent = collider.parent()) === null || _collider$parent === void 0 ? void 0 : _collider$parent.handle;
768
- const rigidBody = rigidBodyHandle !== undefined ? world.getRigidBody(rigidBodyHandle) : undefined;
756
+ const rigidBody = rigidBodyHandle !== undefined ? worldProxy.getRigidBody(rigidBodyHandle) : undefined;
769
757
  const rbEvents = rigidBody && rigidBodyHandle !== undefined ? rigidBodyEvents.get(rigidBodyHandle) : undefined;
770
758
  const rigidBodyState = rigidBodyHandle !== undefined ? rigidBodyStates.get(rigidBodyHandle) : undefined;
771
759
  const source = {
@@ -787,11 +775,11 @@ const Physics = ({
787
775
  accumulator: 0
788
776
  });
789
777
  const step = useCallback(dt => {
790
- const world = getWorld();
778
+ const world = worldProxy;
791
779
  /* Check if the timestep is supposed to be variable. We'll do this here
792
780
  once so we don't have to string-check every frame. */
793
781
 
794
- const timeStepVariable = _timeStep === "vary";
782
+ const timeStepVariable = timeStep === "vary";
795
783
  /**
796
784
  * Fixed timeStep simulation progression
797
785
  * @see https://gafferongames.com/post/fix_your_timestep/
@@ -802,12 +790,12 @@ const Physics = ({
802
790
  const stepWorld = () => {
803
791
  // Trigger beforeStep callbacks
804
792
  beforeStepCallbacks.forEach(callback => {
805
- callback.current(api);
793
+ callback.current(worldProxy);
806
794
  });
807
795
  world.step(eventQueue); // Trigger afterStep callbacks
808
796
 
809
797
  afterStepCallbacks.forEach(callback => {
810
- callback.current(api);
798
+ callback.current(worldProxy);
811
799
  });
812
800
  };
813
801
 
@@ -815,15 +803,15 @@ const Physics = ({
815
803
  world.timestep = clampedDelta;
816
804
  stepWorld();
817
805
  } else {
818
- world.timestep = _timeStep; // don't step time forwards if paused
806
+ world.timestep = timeStep; // don't step time forwards if paused
819
807
  // Increase accumulator
820
808
 
821
809
  steppingState.accumulator += clampedDelta;
822
810
 
823
- while (steppingState.accumulator >= _timeStep) {
811
+ while (steppingState.accumulator >= timeStep) {
824
812
  // Set up previous state
825
813
  // needed for accurate interpolations if the world steps more than once
826
- if (_interpolate) {
814
+ if (interpolate) {
827
815
  steppingState.previousState = {};
828
816
  world.forEachRigidBody(body => {
829
817
  steppingState.previousState[body.handle] = {
@@ -834,11 +822,11 @@ const Physics = ({
834
822
  }
835
823
 
836
824
  stepWorld();
837
- steppingState.accumulator -= _timeStep;
825
+ steppingState.accumulator -= timeStep;
838
826
  }
839
827
  }
840
828
 
841
- const interpolationAlpha = timeStepVariable || !_interpolate || _paused ? 1 : steppingState.accumulator / _timeStep; // Update meshes
829
+ const interpolationAlpha = timeStepVariable || !interpolate || paused ? 1 : steppingState.accumulator / timeStep; // Update meshes
842
830
 
843
831
  rigidBodyStates.forEach((state, handle) => {
844
832
  const rigidBody = world.getRigidBody(handle);
@@ -994,13 +982,13 @@ const Physics = ({
994
982
  world.forEachActiveRigidBody(() => {
995
983
  invalidate();
996
984
  });
997
- }, [_paused, _timeStep, _interpolate]);
985
+ }, [paused, timeStep, interpolate]);
998
986
  const context = useMemo(() => ({
999
987
  rapier,
1000
- world: api,
988
+ world: worldProxy,
1001
989
  physicsOptions: {
1002
- colliders: _colliders,
1003
- gravity: _gravity
990
+ colliders,
991
+ gravity
1004
992
  },
1005
993
  rigidBodyStates,
1006
994
  colliderStates,
@@ -1008,22 +996,22 @@ const Physics = ({
1008
996
  colliderEvents,
1009
997
  beforeStepCallbacks,
1010
998
  afterStepCallbacks,
1011
- isPaused: _paused,
1012
- isDebug: _debug,
999
+ isPaused: paused,
1000
+ isDebug: debug,
1013
1001
  step
1014
- }), [_paused, step, _debug, _colliders, _gravity]);
1002
+ }), [paused, step, debug, colliders, gravity]);
1015
1003
  const stepCallback = useCallback(delta => {
1016
- if (!_paused) {
1004
+ if (!paused) {
1017
1005
  step(delta);
1018
1006
  }
1019
- }, [_paused, step]);
1007
+ }, [paused, step]);
1020
1008
  return /*#__PURE__*/React.createElement(rapierContext.Provider, {
1021
1009
  value: context
1022
1010
  }, /*#__PURE__*/React.createElement(FrameStepper$1, {
1023
1011
  onStep: stepCallback,
1024
- type: _updateLoop,
1012
+ type: updateLoop,
1025
1013
  updatePriority: updatePriority
1026
- }), _debug && /*#__PURE__*/React.createElement(Debug, null), children);
1014
+ }), debug && /*#__PURE__*/React.createElement(Debug, null), children);
1027
1015
  };
1028
1016
 
1029
1017
  function _extends() {
@@ -1043,6 +1031,33 @@ function _extends() {
1043
1031
  return _extends.apply(this, arguments);
1044
1032
  }
1045
1033
 
1034
+ /**
1035
+ * Initiate an instance and return a safe getter
1036
+ */
1037
+
1038
+ const useImperativeInstance = (createFn, destroyFn, dependencyList) => {
1039
+ const ref = useRef();
1040
+ const getInstance = useCallback(() => {
1041
+ if (!ref.current) {
1042
+ ref.current = createFn();
1043
+ }
1044
+
1045
+ return ref.current;
1046
+ }, dependencyList);
1047
+ useEffect(() => {
1048
+ // Save the destroy function and instance
1049
+ const instance = getInstance();
1050
+
1051
+ const destroy = () => destroyFn(instance);
1052
+
1053
+ return () => {
1054
+ destroy();
1055
+ ref.current = undefined;
1056
+ };
1057
+ }, [getInstance]);
1058
+ return getInstance;
1059
+ };
1060
+
1046
1061
  /**
1047
1062
  * Takes an object resembling a Vector3 and returs a Three.Vector3
1048
1063
  * @category Math helpers
@@ -1142,7 +1157,9 @@ const AnyCollider = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props, forwarded
1142
1157
  colliderRef.current = collider;
1143
1158
  return collider;
1144
1159
  }, collider => {
1145
- world.removeCollider(collider);
1160
+ if (world.getCollider(collider.handle)) {
1161
+ world.removeCollider(collider, true);
1162
+ }
1146
1163
  }, [...immutablePropArray, rigidBodyContext]);
1147
1164
  useEffect(() => {
1148
1165
  const collider = getInstance();
@@ -1478,7 +1495,9 @@ const RigidBody = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props, forwardedRe
1478
1495
  rigidBodyRef.current = rigidBody;
1479
1496
  return rigidBody;
1480
1497
  }, rigidBody => {
1481
- world.removeRigidBody(rigidBody);
1498
+ if (world.getRigidBody(rigidBody.handle)) {
1499
+ world.removeRigidBody(rigidBody);
1500
+ }
1482
1501
  }, immutablePropArray); // Only provide a object state after the ref has been set
1483
1502
 
1484
1503
  useEffect(() => {
@@ -1644,15 +1663,17 @@ const useImpulseJoint = (body1, body2, params) => {
1644
1663
  const jointRef = useRef();
1645
1664
  useImperativeInstance(() => {
1646
1665
  if (body1.current && body2.current) {
1647
- const newJoint = world.createImpulseJoint(params, body1.current, body2.current);
1648
- jointRef.current = newJoint; // console.log(body1.current, body2.current, newJoint);
1649
-
1666
+ const newJoint = world.createImpulseJoint(params, body1.current, body2.current, true);
1667
+ jointRef.current = newJoint;
1650
1668
  return newJoint;
1651
1669
  }
1652
1670
  }, joint => {
1653
1671
  if (joint) {
1654
1672
  jointRef.current = undefined;
1655
- world.removeImpulseJoint(joint);
1673
+
1674
+ if (world.getImpulseJoint(joint.handle)) {
1675
+ world.removeImpulseJoint(joint, true);
1676
+ }
1656
1677
  }
1657
1678
  }, []);
1658
1679
  return jointRef;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-three/rapier",
3
- "version": "0.16.0-canary.1",
3
+ "version": "1.0.0",
4
4
  "source": "src/index.ts",
5
5
  "main": "dist/react-three-rapier.cjs.js",
6
6
  "module": "dist/react-three-rapier.esm.js",
@@ -8,9 +8,7 @@
8
8
  "files": [
9
9
  "dist"
10
10
  ],
11
- "scripts": {
12
- "ts": "tsc -w"
13
- },
11
+ "scripts": {},
14
12
  "devDependencies": {
15
13
  "@react-three/drei": "9.45.0",
16
14
  "@react-three/fiber": "8.9.1",
@@ -1,17 +0,0 @@
1
- import { Collider, ColliderDesc, DebugRenderBuffers, ImpulseJoint, JointData, RigidBody, RigidBodyDesc, World } from "@dimforge/rapier3d-compat";
2
- import { Vector3 } from "three";
3
- export interface WorldApi {
4
- raw(): World;
5
- getCollider(handle: number): Collider | undefined;
6
- getRigidBody(handle: number): RigidBody | undefined;
7
- createRigidBody(desc: RigidBodyDesc): RigidBody;
8
- createCollider(desc: ColliderDesc, parent?: RigidBody): Collider;
9
- removeRigidBody(rigidBody: RigidBody): void;
10
- removeCollider(collider: Collider, wakeUp?: boolean): void;
11
- createImpulseJoint(params: JointData, rigidBodyA: RigidBody, rigidBodyB: RigidBody, wakeUp?: boolean): ImpulseJoint;
12
- removeImpulseJoint(joint: ImpulseJoint, wakeUp?: boolean): void;
13
- forEachCollider(callback: (collider: Collider) => void): void;
14
- setGravity(gravity: Vector3): void;
15
- debugRender(): DebugRenderBuffers;
16
- }
17
- export declare const createWorldApi: (getWorld: () => World) => WorldApi;