@react-three/rapier 1.0.0-canary.2 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -115,6 +115,43 @@ export interface PhysicsProps {
|
|
115
115
|
* @defaultValue [0, -9.81, 0]
|
116
116
|
*/
|
117
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;
|
118
155
|
/**
|
119
156
|
* Set the base automatic colliders for this physics world
|
120
157
|
* All Meshes inside RigidBodies will generate a collider
|
@@ -664,6 +664,14 @@ const createSingletonProxy = createInstance => {
|
|
664
664
|
}
|
665
665
|
|
666
666
|
return Reflect.get(instance, prop);
|
667
|
+
},
|
668
|
+
|
669
|
+
set(target, prop, value) {
|
670
|
+
if (!instance) {
|
671
|
+
instance = createInstance();
|
672
|
+
}
|
673
|
+
|
674
|
+
return Reflect.set(instance, prop, value);
|
667
675
|
}
|
668
676
|
|
669
677
|
};
|
@@ -718,17 +726,23 @@ const importRapier = async () => {
|
|
718
726
|
* The main physics component used to create a physics world.
|
719
727
|
* @category Components
|
720
728
|
*/
|
721
|
-
const Physics =
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
729
|
+
const Physics = props => {
|
730
|
+
const {
|
731
|
+
colliders = "cuboid",
|
732
|
+
children,
|
733
|
+
timeStep = 1 / 60,
|
734
|
+
paused = false,
|
735
|
+
interpolate = true,
|
736
|
+
updatePriority,
|
737
|
+
updateLoop = "follow",
|
738
|
+
debug = false,
|
739
|
+
gravity = [0, -9.81, 0],
|
740
|
+
maxStabilizationIterations = 1,
|
741
|
+
maxVelocityFrictionIterations = 8,
|
742
|
+
maxVelocityIterations = 4,
|
743
|
+
predictionDistance = 0.002,
|
744
|
+
erp = 0.8
|
745
|
+
} = props;
|
732
746
|
const rapier = useAsset.useAsset(importRapier);
|
733
747
|
const {
|
734
748
|
invalidate
|
@@ -749,17 +763,22 @@ const Physics = ({
|
|
749
763
|
const {
|
750
764
|
proxy: worldProxy,
|
751
765
|
reset: resetWorldProxy
|
752
|
-
} = useConst(() => createSingletonProxy(() => new rapier.World(vectorArrayToVector3(
|
766
|
+
} = useConst(() => createSingletonProxy(() => new rapier.World(vectorArrayToVector3(gravity))));
|
753
767
|
React.useEffect(() => {
|
754
768
|
return () => {
|
755
769
|
worldProxy.free();
|
756
770
|
resetWorldProxy();
|
757
771
|
};
|
758
|
-
}, []); // Update
|
772
|
+
}, []); // Update mutable props
|
759
773
|
|
760
774
|
React.useEffect(() => {
|
761
|
-
worldProxy.gravity = vectorArrayToVector3(
|
762
|
-
|
775
|
+
worldProxy.gravity = vectorArrayToVector3(gravity);
|
776
|
+
worldProxy.integrationParameters.maxStabilizationIterations = maxStabilizationIterations;
|
777
|
+
worldProxy.integrationParameters.maxVelocityFrictionIterations = maxVelocityFrictionIterations;
|
778
|
+
worldProxy.integrationParameters.maxVelocityIterations = maxVelocityIterations;
|
779
|
+
worldProxy.integrationParameters.predictionDistance = predictionDistance;
|
780
|
+
worldProxy.integrationParameters.erp = erp;
|
781
|
+
}, [worldProxy, ...gravity, maxStabilizationIterations, maxVelocityIterations, maxVelocityFrictionIterations, predictionDistance, erp]);
|
763
782
|
const getSourceFromColliderHandle = React.useCallback(handle => {
|
764
783
|
var _collider$parent;
|
765
784
|
|
@@ -793,39 +812,38 @@ const Physics = ({
|
|
793
812
|
/* Check if the timestep is supposed to be variable. We'll do this here
|
794
813
|
once so we don't have to string-check every frame. */
|
795
814
|
|
796
|
-
const timeStepVariable =
|
815
|
+
const timeStepVariable = timeStep === "vary";
|
797
816
|
/**
|
798
817
|
* Fixed timeStep simulation progression
|
799
818
|
* @see https://gafferongames.com/post/fix_your_timestep/
|
800
819
|
*/
|
801
820
|
|
802
|
-
const clampedDelta = three.MathUtils.clamp(dt, 0, 0.
|
821
|
+
const clampedDelta = three.MathUtils.clamp(dt, 0, 0.5);
|
803
822
|
|
804
|
-
const stepWorld =
|
823
|
+
const stepWorld = delta => {
|
805
824
|
// Trigger beforeStep callbacks
|
806
825
|
beforeStepCallbacks.forEach(callback => {
|
807
|
-
callback.current(
|
826
|
+
callback.current(world);
|
808
827
|
});
|
828
|
+
world.timestep = delta;
|
809
829
|
world.step(eventQueue); // Trigger afterStep callbacks
|
810
830
|
|
811
831
|
afterStepCallbacks.forEach(callback => {
|
812
|
-
callback.current(
|
832
|
+
callback.current(world);
|
813
833
|
});
|
814
834
|
};
|
815
835
|
|
816
836
|
if (timeStepVariable) {
|
817
|
-
|
818
|
-
stepWorld();
|
837
|
+
stepWorld(clampedDelta);
|
819
838
|
} else {
|
820
|
-
|
839
|
+
// don't step time forwards if paused
|
821
840
|
// Increase accumulator
|
822
|
-
|
823
841
|
steppingState.accumulator += clampedDelta;
|
824
842
|
|
825
|
-
while (steppingState.accumulator >=
|
843
|
+
while (steppingState.accumulator >= timeStep) {
|
826
844
|
// Set up previous state
|
827
845
|
// needed for accurate interpolations if the world steps more than once
|
828
|
-
if (
|
846
|
+
if (interpolate) {
|
829
847
|
steppingState.previousState = {};
|
830
848
|
world.forEachRigidBody(body => {
|
831
849
|
steppingState.previousState[body.handle] = {
|
@@ -835,12 +853,12 @@ const Physics = ({
|
|
835
853
|
});
|
836
854
|
}
|
837
855
|
|
838
|
-
stepWorld();
|
839
|
-
steppingState.accumulator -=
|
856
|
+
stepWorld(timeStep);
|
857
|
+
steppingState.accumulator -= timeStep;
|
840
858
|
}
|
841
859
|
}
|
842
860
|
|
843
|
-
const interpolationAlpha = timeStepVariable || !
|
861
|
+
const interpolationAlpha = timeStepVariable || !interpolate || paused ? 1 : steppingState.accumulator / timeStep; // Update meshes
|
844
862
|
|
845
863
|
rigidBodyStates.forEach((state, handle) => {
|
846
864
|
const rigidBody = world.getRigidBody(handle);
|
@@ -996,13 +1014,13 @@ const Physics = ({
|
|
996
1014
|
world.forEachActiveRigidBody(() => {
|
997
1015
|
invalidate();
|
998
1016
|
});
|
999
|
-
}, [
|
1017
|
+
}, [paused, timeStep, interpolate, worldProxy]);
|
1000
1018
|
const context = React.useMemo(() => ({
|
1001
1019
|
rapier,
|
1002
1020
|
world: worldProxy,
|
1003
1021
|
physicsOptions: {
|
1004
|
-
colliders
|
1005
|
-
gravity
|
1022
|
+
colliders,
|
1023
|
+
gravity
|
1006
1024
|
},
|
1007
1025
|
rigidBodyStates,
|
1008
1026
|
colliderStates,
|
@@ -1010,22 +1028,22 @@ const Physics = ({
|
|
1010
1028
|
colliderEvents,
|
1011
1029
|
beforeStepCallbacks,
|
1012
1030
|
afterStepCallbacks,
|
1013
|
-
isPaused:
|
1014
|
-
isDebug:
|
1031
|
+
isPaused: paused,
|
1032
|
+
isDebug: debug,
|
1015
1033
|
step
|
1016
|
-
}), [
|
1034
|
+
}), [paused, step, debug, colliders, gravity]);
|
1017
1035
|
const stepCallback = React.useCallback(delta => {
|
1018
|
-
if (!
|
1036
|
+
if (!paused) {
|
1019
1037
|
step(delta);
|
1020
1038
|
}
|
1021
|
-
}, [
|
1039
|
+
}, [paused, step]);
|
1022
1040
|
return /*#__PURE__*/React__default["default"].createElement(rapierContext.Provider, {
|
1023
1041
|
value: context
|
1024
1042
|
}, /*#__PURE__*/React__default["default"].createElement(FrameStepper$1, {
|
1025
1043
|
onStep: stepCallback,
|
1026
|
-
type:
|
1044
|
+
type: updateLoop,
|
1027
1045
|
updatePriority: updatePriority
|
1028
|
-
}),
|
1046
|
+
}), debug && /*#__PURE__*/React__default["default"].createElement(Debug, null), children);
|
1029
1047
|
};
|
1030
1048
|
|
1031
1049
|
function _extends() {
|
@@ -1171,7 +1189,9 @@ const AnyCollider = /*#__PURE__*/React.memo( /*#__PURE__*/React.forwardRef((prop
|
|
1171
1189
|
colliderRef.current = collider;
|
1172
1190
|
return collider;
|
1173
1191
|
}, collider => {
|
1174
|
-
world.
|
1192
|
+
if (world.getCollider(collider.handle)) {
|
1193
|
+
world.removeCollider(collider, true);
|
1194
|
+
}
|
1175
1195
|
}, [...immutablePropArray, rigidBodyContext]);
|
1176
1196
|
React.useEffect(() => {
|
1177
1197
|
const collider = getInstance();
|
@@ -1507,7 +1527,9 @@ const RigidBody = /*#__PURE__*/React.memo( /*#__PURE__*/React.forwardRef((props,
|
|
1507
1527
|
rigidBodyRef.current = rigidBody;
|
1508
1528
|
return rigidBody;
|
1509
1529
|
}, rigidBody => {
|
1510
|
-
world.
|
1530
|
+
if (world.getRigidBody(rigidBody.handle)) {
|
1531
|
+
world.removeRigidBody(rigidBody);
|
1532
|
+
}
|
1511
1533
|
}, immutablePropArray); // Only provide a object state after the ref has been set
|
1512
1534
|
|
1513
1535
|
React.useEffect(() => {
|
@@ -1674,14 +1696,16 @@ const useImpulseJoint = (body1, body2, params) => {
|
|
1674
1696
|
useImperativeInstance(() => {
|
1675
1697
|
if (body1.current && body2.current) {
|
1676
1698
|
const newJoint = world.createImpulseJoint(params, body1.current, body2.current, true);
|
1677
|
-
jointRef.current = newJoint;
|
1678
|
-
|
1699
|
+
jointRef.current = newJoint;
|
1679
1700
|
return newJoint;
|
1680
1701
|
}
|
1681
1702
|
}, joint => {
|
1682
1703
|
if (joint) {
|
1683
1704
|
jointRef.current = undefined;
|
1684
|
-
|
1705
|
+
|
1706
|
+
if (world.getImpulseJoint(joint.handle)) {
|
1707
|
+
world.removeImpulseJoint(joint, true);
|
1708
|
+
}
|
1685
1709
|
}
|
1686
1710
|
}, []);
|
1687
1711
|
return jointRef;
|
@@ -664,6 +664,14 @@ const createSingletonProxy = createInstance => {
|
|
664
664
|
}
|
665
665
|
|
666
666
|
return Reflect.get(instance, prop);
|
667
|
+
},
|
668
|
+
|
669
|
+
set(target, prop, value) {
|
670
|
+
if (!instance) {
|
671
|
+
instance = createInstance();
|
672
|
+
}
|
673
|
+
|
674
|
+
return Reflect.set(instance, prop, value);
|
667
675
|
}
|
668
676
|
|
669
677
|
};
|
@@ -718,17 +726,23 @@ const importRapier = async () => {
|
|
718
726
|
* The main physics component used to create a physics world.
|
719
727
|
* @category Components
|
720
728
|
*/
|
721
|
-
const Physics =
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
729
|
+
const Physics = props => {
|
730
|
+
const {
|
731
|
+
colliders = "cuboid",
|
732
|
+
children,
|
733
|
+
timeStep = 1 / 60,
|
734
|
+
paused = false,
|
735
|
+
interpolate = true,
|
736
|
+
updatePriority,
|
737
|
+
updateLoop = "follow",
|
738
|
+
debug = false,
|
739
|
+
gravity = [0, -9.81, 0],
|
740
|
+
maxStabilizationIterations = 1,
|
741
|
+
maxVelocityFrictionIterations = 8,
|
742
|
+
maxVelocityIterations = 4,
|
743
|
+
predictionDistance = 0.002,
|
744
|
+
erp = 0.8
|
745
|
+
} = props;
|
732
746
|
const rapier = useAsset.useAsset(importRapier);
|
733
747
|
const {
|
734
748
|
invalidate
|
@@ -749,17 +763,22 @@ const Physics = ({
|
|
749
763
|
const {
|
750
764
|
proxy: worldProxy,
|
751
765
|
reset: resetWorldProxy
|
752
|
-
} = useConst(() => createSingletonProxy(() => new rapier.World(vectorArrayToVector3(
|
766
|
+
} = useConst(() => createSingletonProxy(() => new rapier.World(vectorArrayToVector3(gravity))));
|
753
767
|
React.useEffect(() => {
|
754
768
|
return () => {
|
755
769
|
worldProxy.free();
|
756
770
|
resetWorldProxy();
|
757
771
|
};
|
758
|
-
}, []); // Update
|
772
|
+
}, []); // Update mutable props
|
759
773
|
|
760
774
|
React.useEffect(() => {
|
761
|
-
worldProxy.gravity = vectorArrayToVector3(
|
762
|
-
|
775
|
+
worldProxy.gravity = vectorArrayToVector3(gravity);
|
776
|
+
worldProxy.integrationParameters.maxStabilizationIterations = maxStabilizationIterations;
|
777
|
+
worldProxy.integrationParameters.maxVelocityFrictionIterations = maxVelocityFrictionIterations;
|
778
|
+
worldProxy.integrationParameters.maxVelocityIterations = maxVelocityIterations;
|
779
|
+
worldProxy.integrationParameters.predictionDistance = predictionDistance;
|
780
|
+
worldProxy.integrationParameters.erp = erp;
|
781
|
+
}, [worldProxy, ...gravity, maxStabilizationIterations, maxVelocityIterations, maxVelocityFrictionIterations, predictionDistance, erp]);
|
763
782
|
const getSourceFromColliderHandle = React.useCallback(handle => {
|
764
783
|
var _collider$parent;
|
765
784
|
|
@@ -793,39 +812,38 @@ const Physics = ({
|
|
793
812
|
/* Check if the timestep is supposed to be variable. We'll do this here
|
794
813
|
once so we don't have to string-check every frame. */
|
795
814
|
|
796
|
-
const timeStepVariable =
|
815
|
+
const timeStepVariable = timeStep === "vary";
|
797
816
|
/**
|
798
817
|
* Fixed timeStep simulation progression
|
799
818
|
* @see https://gafferongames.com/post/fix_your_timestep/
|
800
819
|
*/
|
801
820
|
|
802
|
-
const clampedDelta = three.MathUtils.clamp(dt, 0, 0.
|
821
|
+
const clampedDelta = three.MathUtils.clamp(dt, 0, 0.5);
|
803
822
|
|
804
|
-
const stepWorld =
|
823
|
+
const stepWorld = delta => {
|
805
824
|
// Trigger beforeStep callbacks
|
806
825
|
beforeStepCallbacks.forEach(callback => {
|
807
|
-
callback.current(
|
826
|
+
callback.current(world);
|
808
827
|
});
|
828
|
+
world.timestep = delta;
|
809
829
|
world.step(eventQueue); // Trigger afterStep callbacks
|
810
830
|
|
811
831
|
afterStepCallbacks.forEach(callback => {
|
812
|
-
callback.current(
|
832
|
+
callback.current(world);
|
813
833
|
});
|
814
834
|
};
|
815
835
|
|
816
836
|
if (timeStepVariable) {
|
817
|
-
|
818
|
-
stepWorld();
|
837
|
+
stepWorld(clampedDelta);
|
819
838
|
} else {
|
820
|
-
|
839
|
+
// don't step time forwards if paused
|
821
840
|
// Increase accumulator
|
822
|
-
|
823
841
|
steppingState.accumulator += clampedDelta;
|
824
842
|
|
825
|
-
while (steppingState.accumulator >=
|
843
|
+
while (steppingState.accumulator >= timeStep) {
|
826
844
|
// Set up previous state
|
827
845
|
// needed for accurate interpolations if the world steps more than once
|
828
|
-
if (
|
846
|
+
if (interpolate) {
|
829
847
|
steppingState.previousState = {};
|
830
848
|
world.forEachRigidBody(body => {
|
831
849
|
steppingState.previousState[body.handle] = {
|
@@ -835,12 +853,12 @@ const Physics = ({
|
|
835
853
|
});
|
836
854
|
}
|
837
855
|
|
838
|
-
stepWorld();
|
839
|
-
steppingState.accumulator -=
|
856
|
+
stepWorld(timeStep);
|
857
|
+
steppingState.accumulator -= timeStep;
|
840
858
|
}
|
841
859
|
}
|
842
860
|
|
843
|
-
const interpolationAlpha = timeStepVariable || !
|
861
|
+
const interpolationAlpha = timeStepVariable || !interpolate || paused ? 1 : steppingState.accumulator / timeStep; // Update meshes
|
844
862
|
|
845
863
|
rigidBodyStates.forEach((state, handle) => {
|
846
864
|
const rigidBody = world.getRigidBody(handle);
|
@@ -996,13 +1014,13 @@ const Physics = ({
|
|
996
1014
|
world.forEachActiveRigidBody(() => {
|
997
1015
|
invalidate();
|
998
1016
|
});
|
999
|
-
}, [
|
1017
|
+
}, [paused, timeStep, interpolate, worldProxy]);
|
1000
1018
|
const context = React.useMemo(() => ({
|
1001
1019
|
rapier,
|
1002
1020
|
world: worldProxy,
|
1003
1021
|
physicsOptions: {
|
1004
|
-
colliders
|
1005
|
-
gravity
|
1022
|
+
colliders,
|
1023
|
+
gravity
|
1006
1024
|
},
|
1007
1025
|
rigidBodyStates,
|
1008
1026
|
colliderStates,
|
@@ -1010,22 +1028,22 @@ const Physics = ({
|
|
1010
1028
|
colliderEvents,
|
1011
1029
|
beforeStepCallbacks,
|
1012
1030
|
afterStepCallbacks,
|
1013
|
-
isPaused:
|
1014
|
-
isDebug:
|
1031
|
+
isPaused: paused,
|
1032
|
+
isDebug: debug,
|
1015
1033
|
step
|
1016
|
-
}), [
|
1034
|
+
}), [paused, step, debug, colliders, gravity]);
|
1017
1035
|
const stepCallback = React.useCallback(delta => {
|
1018
|
-
if (!
|
1036
|
+
if (!paused) {
|
1019
1037
|
step(delta);
|
1020
1038
|
}
|
1021
|
-
}, [
|
1039
|
+
}, [paused, step]);
|
1022
1040
|
return /*#__PURE__*/React__default["default"].createElement(rapierContext.Provider, {
|
1023
1041
|
value: context
|
1024
1042
|
}, /*#__PURE__*/React__default["default"].createElement(FrameStepper$1, {
|
1025
1043
|
onStep: stepCallback,
|
1026
|
-
type:
|
1044
|
+
type: updateLoop,
|
1027
1045
|
updatePriority: updatePriority
|
1028
|
-
}),
|
1046
|
+
}), debug && /*#__PURE__*/React__default["default"].createElement(Debug, null), children);
|
1029
1047
|
};
|
1030
1048
|
|
1031
1049
|
function _extends() {
|
@@ -1171,7 +1189,9 @@ const AnyCollider = /*#__PURE__*/React.memo( /*#__PURE__*/React.forwardRef((prop
|
|
1171
1189
|
colliderRef.current = collider;
|
1172
1190
|
return collider;
|
1173
1191
|
}, collider => {
|
1174
|
-
world.
|
1192
|
+
if (world.getCollider(collider.handle)) {
|
1193
|
+
world.removeCollider(collider, true);
|
1194
|
+
}
|
1175
1195
|
}, [...immutablePropArray, rigidBodyContext]);
|
1176
1196
|
React.useEffect(() => {
|
1177
1197
|
const collider = getInstance();
|
@@ -1507,7 +1527,9 @@ const RigidBody = /*#__PURE__*/React.memo( /*#__PURE__*/React.forwardRef((props,
|
|
1507
1527
|
rigidBodyRef.current = rigidBody;
|
1508
1528
|
return rigidBody;
|
1509
1529
|
}, rigidBody => {
|
1510
|
-
world.
|
1530
|
+
if (world.getRigidBody(rigidBody.handle)) {
|
1531
|
+
world.removeRigidBody(rigidBody);
|
1532
|
+
}
|
1511
1533
|
}, immutablePropArray); // Only provide a object state after the ref has been set
|
1512
1534
|
|
1513
1535
|
React.useEffect(() => {
|
@@ -1674,14 +1696,16 @@ const useImpulseJoint = (body1, body2, params) => {
|
|
1674
1696
|
useImperativeInstance(() => {
|
1675
1697
|
if (body1.current && body2.current) {
|
1676
1698
|
const newJoint = world.createImpulseJoint(params, body1.current, body2.current, true);
|
1677
|
-
jointRef.current = newJoint;
|
1678
|
-
|
1699
|
+
jointRef.current = newJoint;
|
1679
1700
|
return newJoint;
|
1680
1701
|
}
|
1681
1702
|
}, joint => {
|
1682
1703
|
if (joint) {
|
1683
1704
|
jointRef.current = undefined;
|
1684
|
-
|
1705
|
+
|
1706
|
+
if (world.getImpulseJoint(joint.handle)) {
|
1707
|
+
world.removeImpulseJoint(joint, true);
|
1708
|
+
}
|
1685
1709
|
}
|
1686
1710
|
}, []);
|
1687
1711
|
return jointRef;
|
@@ -639,6 +639,14 @@ const createSingletonProxy = createInstance => {
|
|
639
639
|
}
|
640
640
|
|
641
641
|
return Reflect.get(instance, prop);
|
642
|
+
},
|
643
|
+
|
644
|
+
set(target, prop, value) {
|
645
|
+
if (!instance) {
|
646
|
+
instance = createInstance();
|
647
|
+
}
|
648
|
+
|
649
|
+
return Reflect.set(instance, prop, value);
|
642
650
|
}
|
643
651
|
|
644
652
|
};
|
@@ -693,17 +701,23 @@ const importRapier = async () => {
|
|
693
701
|
* The main physics component used to create a physics world.
|
694
702
|
* @category Components
|
695
703
|
*/
|
696
|
-
const Physics =
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
704
|
+
const Physics = props => {
|
705
|
+
const {
|
706
|
+
colliders = "cuboid",
|
707
|
+
children,
|
708
|
+
timeStep = 1 / 60,
|
709
|
+
paused = false,
|
710
|
+
interpolate = true,
|
711
|
+
updatePriority,
|
712
|
+
updateLoop = "follow",
|
713
|
+
debug = false,
|
714
|
+
gravity = [0, -9.81, 0],
|
715
|
+
maxStabilizationIterations = 1,
|
716
|
+
maxVelocityFrictionIterations = 8,
|
717
|
+
maxVelocityIterations = 4,
|
718
|
+
predictionDistance = 0.002,
|
719
|
+
erp = 0.8
|
720
|
+
} = props;
|
707
721
|
const rapier = useAsset(importRapier);
|
708
722
|
const {
|
709
723
|
invalidate
|
@@ -724,17 +738,22 @@ const Physics = ({
|
|
724
738
|
const {
|
725
739
|
proxy: worldProxy,
|
726
740
|
reset: resetWorldProxy
|
727
|
-
} = useConst(() => createSingletonProxy(() => new rapier.World(vectorArrayToVector3(
|
741
|
+
} = useConst(() => createSingletonProxy(() => new rapier.World(vectorArrayToVector3(gravity))));
|
728
742
|
useEffect(() => {
|
729
743
|
return () => {
|
730
744
|
worldProxy.free();
|
731
745
|
resetWorldProxy();
|
732
746
|
};
|
733
|
-
}, []); // Update
|
747
|
+
}, []); // Update mutable props
|
734
748
|
|
735
749
|
useEffect(() => {
|
736
|
-
worldProxy.gravity = vectorArrayToVector3(
|
737
|
-
|
750
|
+
worldProxy.gravity = vectorArrayToVector3(gravity);
|
751
|
+
worldProxy.integrationParameters.maxStabilizationIterations = maxStabilizationIterations;
|
752
|
+
worldProxy.integrationParameters.maxVelocityFrictionIterations = maxVelocityFrictionIterations;
|
753
|
+
worldProxy.integrationParameters.maxVelocityIterations = maxVelocityIterations;
|
754
|
+
worldProxy.integrationParameters.predictionDistance = predictionDistance;
|
755
|
+
worldProxy.integrationParameters.erp = erp;
|
756
|
+
}, [worldProxy, ...gravity, maxStabilizationIterations, maxVelocityIterations, maxVelocityFrictionIterations, predictionDistance, erp]);
|
738
757
|
const getSourceFromColliderHandle = useCallback(handle => {
|
739
758
|
var _collider$parent;
|
740
759
|
|
@@ -768,39 +787,38 @@ const Physics = ({
|
|
768
787
|
/* Check if the timestep is supposed to be variable. We'll do this here
|
769
788
|
once so we don't have to string-check every frame. */
|
770
789
|
|
771
|
-
const timeStepVariable =
|
790
|
+
const timeStepVariable = timeStep === "vary";
|
772
791
|
/**
|
773
792
|
* Fixed timeStep simulation progression
|
774
793
|
* @see https://gafferongames.com/post/fix_your_timestep/
|
775
794
|
*/
|
776
795
|
|
777
|
-
const clampedDelta = MathUtils.clamp(dt, 0, 0.
|
796
|
+
const clampedDelta = MathUtils.clamp(dt, 0, 0.5);
|
778
797
|
|
779
|
-
const stepWorld =
|
798
|
+
const stepWorld = delta => {
|
780
799
|
// Trigger beforeStep callbacks
|
781
800
|
beforeStepCallbacks.forEach(callback => {
|
782
|
-
callback.current(
|
801
|
+
callback.current(world);
|
783
802
|
});
|
803
|
+
world.timestep = delta;
|
784
804
|
world.step(eventQueue); // Trigger afterStep callbacks
|
785
805
|
|
786
806
|
afterStepCallbacks.forEach(callback => {
|
787
|
-
callback.current(
|
807
|
+
callback.current(world);
|
788
808
|
});
|
789
809
|
};
|
790
810
|
|
791
811
|
if (timeStepVariable) {
|
792
|
-
|
793
|
-
stepWorld();
|
812
|
+
stepWorld(clampedDelta);
|
794
813
|
} else {
|
795
|
-
|
814
|
+
// don't step time forwards if paused
|
796
815
|
// Increase accumulator
|
797
|
-
|
798
816
|
steppingState.accumulator += clampedDelta;
|
799
817
|
|
800
|
-
while (steppingState.accumulator >=
|
818
|
+
while (steppingState.accumulator >= timeStep) {
|
801
819
|
// Set up previous state
|
802
820
|
// needed for accurate interpolations if the world steps more than once
|
803
|
-
if (
|
821
|
+
if (interpolate) {
|
804
822
|
steppingState.previousState = {};
|
805
823
|
world.forEachRigidBody(body => {
|
806
824
|
steppingState.previousState[body.handle] = {
|
@@ -810,12 +828,12 @@ const Physics = ({
|
|
810
828
|
});
|
811
829
|
}
|
812
830
|
|
813
|
-
stepWorld();
|
814
|
-
steppingState.accumulator -=
|
831
|
+
stepWorld(timeStep);
|
832
|
+
steppingState.accumulator -= timeStep;
|
815
833
|
}
|
816
834
|
}
|
817
835
|
|
818
|
-
const interpolationAlpha = timeStepVariable || !
|
836
|
+
const interpolationAlpha = timeStepVariable || !interpolate || paused ? 1 : steppingState.accumulator / timeStep; // Update meshes
|
819
837
|
|
820
838
|
rigidBodyStates.forEach((state, handle) => {
|
821
839
|
const rigidBody = world.getRigidBody(handle);
|
@@ -971,13 +989,13 @@ const Physics = ({
|
|
971
989
|
world.forEachActiveRigidBody(() => {
|
972
990
|
invalidate();
|
973
991
|
});
|
974
|
-
}, [
|
992
|
+
}, [paused, timeStep, interpolate, worldProxy]);
|
975
993
|
const context = useMemo(() => ({
|
976
994
|
rapier,
|
977
995
|
world: worldProxy,
|
978
996
|
physicsOptions: {
|
979
|
-
colliders
|
980
|
-
gravity
|
997
|
+
colliders,
|
998
|
+
gravity
|
981
999
|
},
|
982
1000
|
rigidBodyStates,
|
983
1001
|
colliderStates,
|
@@ -985,22 +1003,22 @@ const Physics = ({
|
|
985
1003
|
colliderEvents,
|
986
1004
|
beforeStepCallbacks,
|
987
1005
|
afterStepCallbacks,
|
988
|
-
isPaused:
|
989
|
-
isDebug:
|
1006
|
+
isPaused: paused,
|
1007
|
+
isDebug: debug,
|
990
1008
|
step
|
991
|
-
}), [
|
1009
|
+
}), [paused, step, debug, colliders, gravity]);
|
992
1010
|
const stepCallback = useCallback(delta => {
|
993
|
-
if (!
|
1011
|
+
if (!paused) {
|
994
1012
|
step(delta);
|
995
1013
|
}
|
996
|
-
}, [
|
1014
|
+
}, [paused, step]);
|
997
1015
|
return /*#__PURE__*/React.createElement(rapierContext.Provider, {
|
998
1016
|
value: context
|
999
1017
|
}, /*#__PURE__*/React.createElement(FrameStepper$1, {
|
1000
1018
|
onStep: stepCallback,
|
1001
|
-
type:
|
1019
|
+
type: updateLoop,
|
1002
1020
|
updatePriority: updatePriority
|
1003
|
-
}),
|
1021
|
+
}), debug && /*#__PURE__*/React.createElement(Debug, null), children);
|
1004
1022
|
};
|
1005
1023
|
|
1006
1024
|
function _extends() {
|
@@ -1146,7 +1164,9 @@ const AnyCollider = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props, forwarded
|
|
1146
1164
|
colliderRef.current = collider;
|
1147
1165
|
return collider;
|
1148
1166
|
}, collider => {
|
1149
|
-
world.
|
1167
|
+
if (world.getCollider(collider.handle)) {
|
1168
|
+
world.removeCollider(collider, true);
|
1169
|
+
}
|
1150
1170
|
}, [...immutablePropArray, rigidBodyContext]);
|
1151
1171
|
useEffect(() => {
|
1152
1172
|
const collider = getInstance();
|
@@ -1482,7 +1502,9 @@ const RigidBody = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props, forwardedRe
|
|
1482
1502
|
rigidBodyRef.current = rigidBody;
|
1483
1503
|
return rigidBody;
|
1484
1504
|
}, rigidBody => {
|
1485
|
-
world.
|
1505
|
+
if (world.getRigidBody(rigidBody.handle)) {
|
1506
|
+
world.removeRigidBody(rigidBody);
|
1507
|
+
}
|
1486
1508
|
}, immutablePropArray); // Only provide a object state after the ref has been set
|
1487
1509
|
|
1488
1510
|
useEffect(() => {
|
@@ -1649,14 +1671,16 @@ const useImpulseJoint = (body1, body2, params) => {
|
|
1649
1671
|
useImperativeInstance(() => {
|
1650
1672
|
if (body1.current && body2.current) {
|
1651
1673
|
const newJoint = world.createImpulseJoint(params, body1.current, body2.current, true);
|
1652
|
-
jointRef.current = newJoint;
|
1653
|
-
|
1674
|
+
jointRef.current = newJoint;
|
1654
1675
|
return newJoint;
|
1655
1676
|
}
|
1656
1677
|
}, joint => {
|
1657
1678
|
if (joint) {
|
1658
1679
|
jointRef.current = undefined;
|
1659
|
-
|
1680
|
+
|
1681
|
+
if (world.getImpulseJoint(joint.handle)) {
|
1682
|
+
world.removeImpulseJoint(joint, true);
|
1683
|
+
}
|
1660
1684
|
}
|
1661
1685
|
}, []);
|
1662
1686
|
return jointRef;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@react-three/rapier",
|
3
|
-
"version": "1.0.
|
3
|
+
"version": "1.0.1",
|
4
4
|
"source": "src/index.ts",
|
5
5
|
"main": "dist/react-three-rapier.cjs.js",
|
6
6
|
"module": "dist/react-three-rapier.esm.js",
|
@@ -17,11 +17,11 @@
|
|
17
17
|
"@types/three": "0.139.0",
|
18
18
|
"@vitejs/plugin-react": "^2.1.0",
|
19
19
|
"@vitest/ui": "0.29.7",
|
20
|
-
"happy-dom": "
|
20
|
+
"happy-dom": "9.19.2",
|
21
21
|
"react": "18.2.0",
|
22
22
|
"react-dom": "18.2.0",
|
23
23
|
"three": "0.146.0",
|
24
|
-
"vitest": "0.
|
24
|
+
"vitest": "0.31.1"
|
25
25
|
},
|
26
26
|
"peerDependencies": {
|
27
27
|
"@react-three/fiber": ">=8.9.0",
|
package/readme.md
CHANGED
@@ -27,7 +27,7 @@ The goal of this library to is to provide a fast physics engine with minimal fri
|
|
27
27
|
```tsx
|
28
28
|
import { Box, Torus } from "@react-three/drei";
|
29
29
|
import { Canvas } from "@react-three/fiber";
|
30
|
-
import { Physics, RigidBody,
|
30
|
+
import { Physics, RigidBody, CuboidCollider } from "@react-three/rapier";
|
31
31
|
|
32
32
|
const App = () => {
|
33
33
|
return (
|