@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.
- package/dist/declarations/src/components/Physics.d.ts +40 -4
- package/dist/declarations/src/index.d.ts +0 -1
- package/dist/declarations/src/utils/singleton-proxy.d.ts +10 -0
- package/dist/declarations/src/utils/utils-collider.d.ts +2 -3
- package/dist/react-three-rapier.cjs.dev.js +127 -106
- package/dist/react-three-rapier.cjs.prod.js +127 -106
- package/dist/react-three-rapier.esm.js +128 -107
- package/package.json +2 -4
- package/dist/declarations/src/utils/api.d.ts +0 -17
@@ -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 = (
|
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:
|
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:
|
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
|
-
*
|
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
|
-
|
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
|
-
|
698
|
-
},
|
699
|
-
React.useEffect(() => {
|
700
|
-
// Save the destroy function and instance
|
701
|
-
const instance = getInstance();
|
669
|
+
};
|
670
|
+
const proxy = new Proxy({}, handler);
|
702
671
|
|
703
|
-
|
672
|
+
const reset = () => {
|
673
|
+
instance = undefined;
|
674
|
+
};
|
675
|
+
/**
|
676
|
+
* Return the proxy and a reset function
|
677
|
+
*/
|
704
678
|
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
}
|
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
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
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();
|
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());
|
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
|
-
|
760
|
+
return () => {
|
761
|
+
worldProxy.free();
|
762
|
+
resetWorldProxy();
|
763
|
+
};
|
764
|
+
}, []); // Update mutable props
|
779
765
|
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
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
|
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 ?
|
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 =
|
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 =
|
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(
|
818
|
+
callback.current(worldProxy);
|
831
819
|
});
|
832
820
|
world.step(eventQueue); // Trigger afterStep callbacks
|
833
821
|
|
834
822
|
afterStepCallbacks.forEach(callback => {
|
835
|
-
callback.current(
|
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 =
|
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 >=
|
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 (
|
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 -=
|
850
|
+
steppingState.accumulator -= timeStep;
|
863
851
|
}
|
864
852
|
}
|
865
853
|
|
866
|
-
const interpolationAlpha = timeStepVariable || !
|
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
|
-
}, [
|
1010
|
+
}, [paused, timeStep, interpolate]);
|
1023
1011
|
const context = React.useMemo(() => ({
|
1024
1012
|
rapier,
|
1025
|
-
world:
|
1013
|
+
world: worldProxy,
|
1026
1014
|
physicsOptions: {
|
1027
|
-
colliders
|
1028
|
-
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:
|
1037
|
-
isDebug:
|
1024
|
+
isPaused: paused,
|
1025
|
+
isDebug: debug,
|
1038
1026
|
step
|
1039
|
-
}), [
|
1027
|
+
}), [paused, step, debug, colliders, gravity]);
|
1040
1028
|
const stepCallback = React.useCallback(delta => {
|
1041
|
-
if (!
|
1029
|
+
if (!paused) {
|
1042
1030
|
step(delta);
|
1043
1031
|
}
|
1044
|
-
}, [
|
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:
|
1037
|
+
type: updateLoop,
|
1050
1038
|
updatePriority: updatePriority
|
1051
|
-
}),
|
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.
|
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.
|
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;
|
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
|
-
|
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
|
-
*
|
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
|
-
|
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
|
-
|
698
|
-
},
|
699
|
-
React.useEffect(() => {
|
700
|
-
// Save the destroy function and instance
|
701
|
-
const instance = getInstance();
|
669
|
+
};
|
670
|
+
const proxy = new Proxy({}, handler);
|
702
671
|
|
703
|
-
|
672
|
+
const reset = () => {
|
673
|
+
instance = undefined;
|
674
|
+
};
|
675
|
+
/**
|
676
|
+
* Return the proxy and a reset function
|
677
|
+
*/
|
704
678
|
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
}
|
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
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
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();
|
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());
|
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
|
-
|
760
|
+
return () => {
|
761
|
+
worldProxy.free();
|
762
|
+
resetWorldProxy();
|
763
|
+
};
|
764
|
+
}, []); // Update mutable props
|
779
765
|
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
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
|
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 ?
|
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 =
|
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 =
|
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(
|
818
|
+
callback.current(worldProxy);
|
831
819
|
});
|
832
820
|
world.step(eventQueue); // Trigger afterStep callbacks
|
833
821
|
|
834
822
|
afterStepCallbacks.forEach(callback => {
|
835
|
-
callback.current(
|
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 =
|
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 >=
|
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 (
|
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 -=
|
850
|
+
steppingState.accumulator -= timeStep;
|
863
851
|
}
|
864
852
|
}
|
865
853
|
|
866
|
-
const interpolationAlpha = timeStepVariable || !
|
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
|
-
}, [
|
1010
|
+
}, [paused, timeStep, interpolate]);
|
1023
1011
|
const context = React.useMemo(() => ({
|
1024
1012
|
rapier,
|
1025
|
-
world:
|
1013
|
+
world: worldProxy,
|
1026
1014
|
physicsOptions: {
|
1027
|
-
colliders
|
1028
|
-
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:
|
1037
|
-
isDebug:
|
1024
|
+
isPaused: paused,
|
1025
|
+
isDebug: debug,
|
1038
1026
|
step
|
1039
|
-
}), [
|
1027
|
+
}), [paused, step, debug, colliders, gravity]);
|
1040
1028
|
const stepCallback = React.useCallback(delta => {
|
1041
|
-
if (!
|
1029
|
+
if (!paused) {
|
1042
1030
|
step(delta);
|
1043
1031
|
}
|
1044
|
-
}, [
|
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:
|
1037
|
+
type: updateLoop,
|
1050
1038
|
updatePriority: updatePriority
|
1051
|
-
}),
|
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.
|
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.
|
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;
|
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
|
-
|
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,
|
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
|
-
*
|
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
|
-
|
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
|
-
|
673
|
-
},
|
674
|
-
useEffect(() => {
|
675
|
-
// Save the destroy function and instance
|
676
|
-
const instance = getInstance();
|
644
|
+
};
|
645
|
+
const proxy = new Proxy({}, handler);
|
677
646
|
|
678
|
-
|
647
|
+
const reset = () => {
|
648
|
+
instance = undefined;
|
649
|
+
};
|
650
|
+
/**
|
651
|
+
* Return the proxy and a reset function
|
652
|
+
*/
|
679
653
|
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
}
|
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
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
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();
|
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());
|
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
|
-
|
735
|
+
return () => {
|
736
|
+
worldProxy.free();
|
737
|
+
resetWorldProxy();
|
738
|
+
};
|
739
|
+
}, []); // Update mutable props
|
754
740
|
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
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
|
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 ?
|
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 =
|
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 =
|
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(
|
793
|
+
callback.current(worldProxy);
|
806
794
|
});
|
807
795
|
world.step(eventQueue); // Trigger afterStep callbacks
|
808
796
|
|
809
797
|
afterStepCallbacks.forEach(callback => {
|
810
|
-
callback.current(
|
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 =
|
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 >=
|
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 (
|
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 -=
|
825
|
+
steppingState.accumulator -= timeStep;
|
838
826
|
}
|
839
827
|
}
|
840
828
|
|
841
|
-
const interpolationAlpha = timeStepVariable || !
|
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
|
-
}, [
|
985
|
+
}, [paused, timeStep, interpolate]);
|
998
986
|
const context = useMemo(() => ({
|
999
987
|
rapier,
|
1000
|
-
world:
|
988
|
+
world: worldProxy,
|
1001
989
|
physicsOptions: {
|
1002
|
-
colliders
|
1003
|
-
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:
|
1012
|
-
isDebug:
|
999
|
+
isPaused: paused,
|
1000
|
+
isDebug: debug,
|
1013
1001
|
step
|
1014
|
-
}), [
|
1002
|
+
}), [paused, step, debug, colliders, gravity]);
|
1015
1003
|
const stepCallback = useCallback(delta => {
|
1016
|
-
if (!
|
1004
|
+
if (!paused) {
|
1017
1005
|
step(delta);
|
1018
1006
|
}
|
1019
|
-
}, [
|
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:
|
1012
|
+
type: updateLoop,
|
1025
1013
|
updatePriority: updatePriority
|
1026
|
-
}),
|
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.
|
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.
|
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;
|
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
|
-
|
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.
|
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;
|