@react-three/rapier 0.16.0-canary.0 → 1.0.0-canary.2

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,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, useImperativeHandle, 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);
@@ -734,38 +707,42 @@ const Physics = ({
734
707
  const rapier = useAsset(importRapier);
735
708
  const {
736
709
  invalidate
737
- } = useThree(); // Init World
738
-
739
- const getWorld = useImperativeInstance(() => {
740
- return new rapier.World(vectorArrayToVector3(_gravity));
741
- }, world => {
742
- world.free();
743
- }, []);
710
+ } = useThree();
744
711
  const rigidBodyStates = useConst(() => new Map());
745
712
  const colliderStates = useConst(() => new Map());
746
713
  const rigidBodyEvents = useConst(() => new Map());
747
714
  const colliderEvents = useConst(() => new Map());
748
715
  const eventQueue = useConst(() => new EventQueue(false));
749
716
  const beforeStepCallbacks = useConst(() => new Set());
750
- const afterStepCallbacks = useConst(() => new Set()); // Update gravity
717
+ const afterStepCallbacks = useConst(() => new Set());
718
+ /**
719
+ * Initiate the world
720
+ * This creates a singleton proxy, so that the world is only created when
721
+ * something within it is accessed.
722
+ */
751
723
 
724
+ const {
725
+ proxy: worldProxy,
726
+ reset: resetWorldProxy
727
+ } = useConst(() => createSingletonProxy(() => new rapier.World(vectorArrayToVector3(_gravity))));
752
728
  useEffect(() => {
753
- const world = getWorld();
729
+ return () => {
730
+ worldProxy.free();
731
+ resetWorldProxy();
732
+ };
733
+ }, []); // Update gravity
754
734
 
755
- if (world) {
756
- world.gravity = vectorArrayToVector3(_gravity);
757
- }
735
+ useEffect(() => {
736
+ worldProxy.gravity = vectorArrayToVector3(_gravity);
758
737
  }, [_gravity]);
759
- const api = useMemo(() => createWorldApi(getWorld), []);
760
738
  const getSourceFromColliderHandle = useCallback(handle => {
761
739
  var _collider$parent;
762
740
 
763
- const world = getWorld();
764
- const collider = world.getCollider(handle);
741
+ const collider = worldProxy.getCollider(handle);
765
742
  const colEvents = colliderEvents.get(handle);
766
743
  const colliderState = colliderStates.get(handle);
767
744
  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;
745
+ const rigidBody = rigidBodyHandle !== undefined ? worldProxy.getRigidBody(rigidBodyHandle) : undefined;
769
746
  const rbEvents = rigidBody && rigidBodyHandle !== undefined ? rigidBodyEvents.get(rigidBodyHandle) : undefined;
770
747
  const rigidBodyState = rigidBodyHandle !== undefined ? rigidBodyStates.get(rigidBodyHandle) : undefined;
771
748
  const source = {
@@ -787,7 +764,7 @@ const Physics = ({
787
764
  accumulator: 0
788
765
  });
789
766
  const step = useCallback(dt => {
790
- const world = getWorld();
767
+ const world = worldProxy;
791
768
  /* Check if the timestep is supposed to be variable. We'll do this here
792
769
  once so we don't have to string-check every frame. */
793
770
 
@@ -802,12 +779,12 @@ const Physics = ({
802
779
  const stepWorld = () => {
803
780
  // Trigger beforeStep callbacks
804
781
  beforeStepCallbacks.forEach(callback => {
805
- callback.current(api);
782
+ callback.current(worldProxy);
806
783
  });
807
784
  world.step(eventQueue); // Trigger afterStep callbacks
808
785
 
809
786
  afterStepCallbacks.forEach(callback => {
810
- callback.current(api);
787
+ callback.current(worldProxy);
811
788
  });
812
789
  };
813
790
 
@@ -997,7 +974,7 @@ const Physics = ({
997
974
  }, [_paused, _timeStep, _interpolate]);
998
975
  const context = useMemo(() => ({
999
976
  rapier,
1000
- world: api,
977
+ world: worldProxy,
1001
978
  physicsOptions: {
1002
979
  colliders: _colliders,
1003
980
  gravity: _gravity
@@ -1043,6 +1020,33 @@ function _extends() {
1043
1020
  return _extends.apply(this, arguments);
1044
1021
  }
1045
1022
 
1023
+ /**
1024
+ * Initiate an instance and return a safe getter
1025
+ */
1026
+
1027
+ const useImperativeInstance = (createFn, destroyFn, dependencyList) => {
1028
+ const ref = useRef();
1029
+ const getInstance = useCallback(() => {
1030
+ if (!ref.current) {
1031
+ ref.current = createFn();
1032
+ }
1033
+
1034
+ return ref.current;
1035
+ }, dependencyList);
1036
+ useEffect(() => {
1037
+ // Save the destroy function and instance
1038
+ const instance = getInstance();
1039
+
1040
+ const destroy = () => destroyFn(instance);
1041
+
1042
+ return () => {
1043
+ destroy();
1044
+ ref.current = undefined;
1045
+ };
1046
+ }, [getInstance]);
1047
+ return getInstance;
1048
+ };
1049
+
1046
1050
  /**
1047
1051
  * Takes an object resembling a Vector3 and returs a Three.Vector3
1048
1052
  * @category Math helpers
@@ -1094,6 +1098,20 @@ const euler = ({
1094
1098
  return new Euler(x, y, z);
1095
1099
  };
1096
1100
 
1101
+ const useForwardedRef = (forwardedRef, defaultValue = null) => {
1102
+ const innerRef = useRef(defaultValue); // Update the forwarded ref when the inner ref changes
1103
+
1104
+ if (forwardedRef && typeof forwardedRef !== "function") {
1105
+ if (!forwardedRef.current) {
1106
+ forwardedRef.current = innerRef.current;
1107
+ }
1108
+
1109
+ return forwardedRef;
1110
+ }
1111
+
1112
+ return innerRef;
1113
+ };
1114
+
1097
1115
  /**
1098
1116
  * A collider is a shape that can be attached to a rigid body to define its physical properties.
1099
1117
  * @internal
@@ -1113,24 +1131,30 @@ const AnyCollider = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props, forwarded
1113
1131
  colliderStates
1114
1132
  } = useRapier();
1115
1133
  const rigidBodyContext = useRigidBodyContext();
1116
- const ref = useRef(null); // We spread the props out here to make sure that the ref is updated when the props change.
1134
+ const colliderRef = useForwardedRef(forwardedRef);
1135
+ const objectRef = useRef(null); // We spread the props out here to make sure that the ref is updated when the props change.
1117
1136
 
1118
1137
  const immutablePropArray = immutableColliderOptions.flatMap(key => Array.isArray(props[key]) ? [...props[key]] : props[key]);
1119
1138
  const getInstance = useImperativeInstance(() => {
1120
- const worldScale = ref.current.getWorldScale(vec3());
1139
+ const worldScale = objectRef.current.getWorldScale(vec3());
1121
1140
  const collider = createColliderFromOptions(props, world, worldScale, rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.getRigidBody);
1141
+
1142
+ if (typeof forwardedRef == "function") {
1143
+ forwardedRef(collider);
1144
+ }
1145
+
1146
+ colliderRef.current = collider;
1122
1147
  return collider;
1123
1148
  }, collider => {
1124
- world.removeCollider(collider);
1149
+ world.removeCollider(collider, true);
1125
1150
  }, [...immutablePropArray, rigidBodyContext]);
1126
1151
  useEffect(() => {
1127
1152
  const collider = getInstance();
1128
- colliderStates.set(collider.handle, createColliderState(collider, ref.current, rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.ref.current));
1153
+ colliderStates.set(collider.handle, createColliderState(collider, objectRef.current, rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.ref.current));
1129
1154
  return () => {
1130
1155
  colliderStates.delete(collider.handle);
1131
1156
  };
1132
1157
  }, [getInstance]);
1133
- useImperativeHandle(forwardedRef, () => getInstance(), [getInstance]);
1134
1158
  const mergedProps = useMemo(() => {
1135
1159
  return _objectSpread2(_objectSpread2({}, cleanRigidBodyPropsForCollider(rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.options)), props);
1136
1160
  }, [props, rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.options]);
@@ -1141,7 +1165,7 @@ const AnyCollider = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props, forwarded
1141
1165
  rotation: rotation,
1142
1166
  quaternion: quaternion,
1143
1167
  scale: scale,
1144
- ref: ref,
1168
+ ref: objectRef,
1145
1169
  name: name
1146
1170
  }, children);
1147
1171
  }));
@@ -1429,7 +1453,8 @@ const RigidBody = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props, forwardedRe
1429
1453
  } = props,
1430
1454
  objectProps = _objectWithoutProperties(props, _excluded$1);
1431
1455
 
1432
- const ref = useRef(null);
1456
+ const objectRef = useRef(null);
1457
+ const rigidBodyRef = useForwardedRef(forwardedRef);
1433
1458
  const {
1434
1459
  world,
1435
1460
  rigidBodyStates,
@@ -1444,11 +1469,17 @@ const RigidBody = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props, forwardedRe
1444
1469
  const immutablePropArray = immutableRigidBodyOptions.flatMap(key => {
1445
1470
  return Array.isArray(mergedOptions[key]) ? [...mergedOptions[key]] : mergedOptions[key];
1446
1471
  });
1447
- const childColliderProps = useChildColliderProps(ref, mergedOptions); // Provide a way to eagerly create rigidbody
1472
+ const childColliderProps = useChildColliderProps(objectRef, mergedOptions); // Provide a way to eagerly create rigidbody
1448
1473
 
1449
1474
  const getRigidBody = useImperativeInstance(() => {
1450
1475
  const desc = rigidBodyDescFromOptions(mergedOptions);
1451
1476
  const rigidBody = world.createRigidBody(desc);
1477
+
1478
+ if (typeof forwardedRef === "function") {
1479
+ forwardedRef(rigidBody);
1480
+ }
1481
+
1482
+ rigidBodyRef.current = rigidBody;
1452
1483
  return rigidBody;
1453
1484
  }, rigidBody => {
1454
1485
  world.removeRigidBody(rigidBody);
@@ -1458,7 +1489,7 @@ const RigidBody = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props, forwardedRe
1458
1489
  const rigidBody = getRigidBody();
1459
1490
  const state = createRigidBodyState({
1460
1491
  rigidBody,
1461
- object: ref.current
1492
+ object: objectRef.current
1462
1493
  });
1463
1494
  rigidBodyStates.set(rigidBody.handle, props.transformState ? props.transformState(state) : state);
1464
1495
  return () => {
@@ -1467,10 +1498,9 @@ const RigidBody = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props, forwardedRe
1467
1498
  }, [getRigidBody]);
1468
1499
  useUpdateRigidBodyOptions(getRigidBody, mergedOptions, rigidBodyStates);
1469
1500
  useRigidBodyEvents(getRigidBody, mergedOptions, rigidBodyEvents);
1470
- useImperativeHandle(forwardedRef, () => getRigidBody(), [getRigidBody]);
1471
1501
  const contextValue = useMemo(() => {
1472
1502
  return {
1473
- ref,
1503
+ ref: objectRef,
1474
1504
  getRigidBody: getRigidBody,
1475
1505
  options: mergedOptions
1476
1506
  };
@@ -1478,7 +1508,7 @@ const RigidBody = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props, forwardedRe
1478
1508
  return /*#__PURE__*/React.createElement(RigidBodyContext.Provider, {
1479
1509
  value: contextValue
1480
1510
  }, /*#__PURE__*/React.createElement("object3D", _extends({
1481
- ref: ref
1511
+ ref: objectRef
1482
1512
  }, objectProps, {
1483
1513
  position: position,
1484
1514
  rotation: rotation,
@@ -1525,9 +1555,10 @@ const MeshCollider = /*#__PURE__*/memo(props => {
1525
1555
  MeshCollider.displayName = "MeshCollider";
1526
1556
 
1527
1557
  const _excluded = ["children", "instances", "colliderNodes", "position", "rotation", "quaternion", "scale"];
1528
- const InstancedRigidBodies = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props, ref) => {
1529
- const object = useRef(null);
1530
- const instancedWrapper = useRef(null);
1558
+ const InstancedRigidBodies = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props, forwardedRef) => {
1559
+ const rigidBodiesRef = useForwardedRef(forwardedRef, []);
1560
+ const objectRef = useRef(null);
1561
+ const instanceWrapperRef = useRef(null);
1531
1562
 
1532
1563
  const {
1533
1564
  // instanced props
@@ -1542,14 +1573,12 @@ const InstancedRigidBodies = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props,
1542
1573
  } = props,
1543
1574
  rigidBodyProps = _objectWithoutProperties(props, _excluded);
1544
1575
 
1545
- const rigidBodyApis = useRef([]);
1546
- useImperativeHandle(ref, () => rigidBodyApis.current, [instances]);
1547
- const childColliderProps = useChildColliderProps(object, _objectSpread2(_objectSpread2({}, props), {}, {
1576
+ const childColliderProps = useChildColliderProps(objectRef, _objectSpread2(_objectSpread2({}, props), {}, {
1548
1577
  children: undefined
1549
1578
  }));
1550
1579
 
1551
1580
  const getInstancedMesh = () => {
1552
- const firstChild = instancedWrapper.current.children[0];
1581
+ const firstChild = instanceWrapperRef.current.children[0];
1553
1582
 
1554
1583
  if (firstChild && "isInstancedMesh" in firstChild) {
1555
1584
  return firstChild;
@@ -1589,16 +1618,16 @@ const InstancedRigidBodies = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props,
1589
1618
  };
1590
1619
 
1591
1620
  return /*#__PURE__*/React.createElement("object3D", _extends({
1592
- ref: object
1621
+ ref: objectRef
1593
1622
  }, rigidBodyProps, {
1594
1623
  position: position,
1595
1624
  rotation: rotation,
1596
1625
  quaternion: quaternion,
1597
1626
  scale: scale
1598
1627
  }), /*#__PURE__*/React.createElement("object3D", {
1599
- ref: instancedWrapper
1628
+ ref: instanceWrapperRef
1600
1629
  }, children), instances === null || instances === void 0 ? void 0 : instances.map((instance, index) => /*#__PURE__*/React.createElement(RigidBody, _extends({}, rigidBodyProps, instance, {
1601
- ref: body => rigidBodyApis.current[index] = body,
1630
+ ref: body => rigidBodiesRef.current[index] = body,
1602
1631
  transformState: state => applyInstancedState(state, index)
1603
1632
  }), /*#__PURE__*/React.createElement(React.Fragment, null, colliderNodes.map((node, index) => /*#__PURE__*/React.createElement(Fragment, {
1604
1633
  key: index
@@ -1619,14 +1648,15 @@ const useImpulseJoint = (body1, body2, params) => {
1619
1648
  const jointRef = useRef();
1620
1649
  useImperativeInstance(() => {
1621
1650
  if (body1.current && body2.current) {
1622
- const newJoint = world.createImpulseJoint(params, body1.current, body2.current);
1623
- jointRef.current = newJoint;
1651
+ const newJoint = world.createImpulseJoint(params, body1.current, body2.current, true);
1652
+ jointRef.current = newJoint; // console.log(body1.current, body2.current, newJoint);
1653
+
1624
1654
  return newJoint;
1625
1655
  }
1626
1656
  }, joint => {
1627
1657
  if (joint) {
1628
1658
  jointRef.current = undefined;
1629
- world.removeImpulseJoint(joint);
1659
+ world.removeImpulseJoint(joint, true);
1630
1660
  }
1631
1661
  }, []);
1632
1662
  return jointRef;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-three/rapier",
3
- "version": "0.16.0-canary.0",
3
+ "version": "1.0.0-canary.2",
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;