@react-three/rapier 0.7.1 → 0.7.3
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/Physics.d.ts +6 -4
- package/dist/declarations/src/index.d.ts +3 -3
- package/dist/declarations/src/types.d.ts +9 -9
- package/dist/react-three-rapier.cjs.dev.js +43 -34
- package/dist/react-three-rapier.cjs.prod.js +43 -34
- package/dist/react-three-rapier.esm.js +44 -35
- package/package.json +1 -1
- package/readme.md +20 -4
@@ -15,7 +15,7 @@ export interface RigidBodyState {
|
|
15
15
|
scale: Vector3;
|
16
16
|
isSleeping: boolean;
|
17
17
|
}
|
18
|
-
export declare type RigidBodyStateMap = Map<RigidBody[
|
18
|
+
export declare type RigidBodyStateMap = Map<RigidBody["handle"], RigidBodyState>;
|
19
19
|
export interface ColliderState {
|
20
20
|
collider: Collider;
|
21
21
|
object: Object3D;
|
@@ -25,7 +25,7 @@ export interface ColliderState {
|
|
25
25
|
*/
|
26
26
|
worldParent: Object3D;
|
27
27
|
}
|
28
|
-
export declare type ColliderStateMap = Map<Collider[
|
28
|
+
export declare type ColliderStateMap = Map<Collider["handle"], ColliderState>;
|
29
29
|
export interface RapierContext {
|
30
30
|
rapier: typeof Rapier;
|
31
31
|
world: WorldApi;
|
@@ -63,11 +63,13 @@ interface RapierWorldProps {
|
|
63
63
|
/**
|
64
64
|
* Set the timestep for the simulation.
|
65
65
|
* Setting this to a number (eg. 1/60) will run the
|
66
|
-
* simulation at that framerate.
|
66
|
+
* simulation at that framerate. Alternatively, you can set this to
|
67
|
+
* "vary", which will cause the simulation to always synchronize with
|
68
|
+
* the current frame delta times.
|
67
69
|
*
|
68
70
|
* @defaultValue 1/60
|
69
71
|
*/
|
70
|
-
timeStep?: number;
|
72
|
+
timeStep?: number | "vary";
|
71
73
|
/**
|
72
74
|
* Pause the physics simulation
|
73
75
|
*
|
@@ -1,8 +1,8 @@
|
|
1
1
|
export * from "./types";
|
2
2
|
export type { RigidBodyApi, WorldApi, InstancedRigidBodyApi } from "./api";
|
3
|
-
export type { RigidBodyProps } from
|
4
|
-
export type { InstancedRigidBodiesProps } from
|
5
|
-
export type { CylinderColliderProps, BallColliderProps, CapsuleColliderProps, ConeColliderProps, ConvexHullColliderProps, CuboidColliderProps, HeightfieldColliderProps, RoundCuboidColliderProps, TrimeshColliderProps } from
|
3
|
+
export type { RigidBodyProps } from "./RigidBody";
|
4
|
+
export type { InstancedRigidBodiesProps } from "./InstancedRigidBodies";
|
5
|
+
export type { CylinderColliderProps, BallColliderProps, CapsuleColliderProps, ConeColliderProps, ConvexHullColliderProps, CuboidColliderProps, HeightfieldColliderProps, RoundCuboidColliderProps, TrimeshColliderProps } from "./AnyCollider";
|
6
6
|
export { Physics } from "./Physics";
|
7
7
|
export { RigidBody } from "./RigidBody";
|
8
8
|
export { MeshCollider } from "./MeshCollider";
|
@@ -118,19 +118,19 @@ export interface UseColliderOptions<ColliderArgs extends Array<unknown>> {
|
|
118
118
|
/**
|
119
119
|
* The position of this collider relative to the rigid body
|
120
120
|
*/
|
121
|
-
position?:
|
121
|
+
position?: Object3DProps["position"];
|
122
122
|
/**
|
123
123
|
* The rotation of this collider relative to the rigid body
|
124
124
|
*/
|
125
|
-
rotation?:
|
125
|
+
rotation?: Object3DProps["rotation"];
|
126
126
|
/**
|
127
127
|
* The rotation, as a Quaternion, of this collider relative to the rigid body
|
128
128
|
*/
|
129
|
-
quaternion?: Object3DProps[
|
129
|
+
quaternion?: Object3DProps["quaternion"];
|
130
130
|
/**
|
131
131
|
* The scale of this collider relative to the rigid body
|
132
132
|
*/
|
133
|
-
scale?: Object3DProps[
|
133
|
+
scale?: Object3DProps["scale"];
|
134
134
|
/**
|
135
135
|
* Callback when this collider collides with another collider.
|
136
136
|
*/
|
@@ -219,11 +219,11 @@ export interface UseRigidBodyOptions extends ColliderProps {
|
|
219
219
|
/**
|
220
220
|
* Initial position of the RigidBody
|
221
221
|
*/
|
222
|
-
position?:
|
222
|
+
position?: Object3DProps["position"];
|
223
223
|
/**
|
224
224
|
* Initial rotation of the RigidBody
|
225
225
|
*/
|
226
|
-
rotation?:
|
226
|
+
rotation?: Object3DProps["rotation"];
|
227
227
|
/**
|
228
228
|
* Automatically generate colliders based on meshes inside this
|
229
229
|
* rigid body.
|
@@ -250,8 +250,8 @@ export interface UseRigidBodyOptions extends ColliderProps {
|
|
250
250
|
*/
|
251
251
|
collisionGroups?: InteractionGroups;
|
252
252
|
/**
|
253
|
-
|
254
|
-
|
253
|
+
* The default solver groups bitmask for all colliders in this rigid body.
|
254
|
+
* Can be customized per-collider.
|
255
255
|
*/
|
256
256
|
solverGroups?: InteractionGroups;
|
257
257
|
onSleep?(): void;
|
@@ -275,7 +275,7 @@ export interface UseRigidBodyOptions extends ColliderProps {
|
|
275
275
|
/**
|
276
276
|
* Passed down to the object3d representing this collider.
|
277
277
|
*/
|
278
|
-
userData?: Object3DProps[
|
278
|
+
userData?: Object3DProps["userData"];
|
279
279
|
}
|
280
280
|
export declare type SphericalJointParams = [
|
281
281
|
body1Anchor: Vector3Array,
|
@@ -7,7 +7,6 @@ var React = require('react');
|
|
7
7
|
var useAsset = require('use-asset');
|
8
8
|
var fiber = require('@react-three/fiber');
|
9
9
|
var three = require('three');
|
10
|
-
var MathUtils = require('three/src/math/MathUtils');
|
11
10
|
var threeStdlib = require('three-stdlib');
|
12
11
|
|
13
12
|
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
|
@@ -267,27 +266,38 @@ const Physics = ({
|
|
267
266
|
const [steppingState] = React.useState({
|
268
267
|
accumulator: 0
|
269
268
|
});
|
269
|
+
/* Check if the timestep is supposed to be variable. We'll do this here
|
270
|
+
once so we don't have to string-check every frame. */
|
271
|
+
|
272
|
+
const timeStepVariable = _timeStep === "vary";
|
270
273
|
fiber.useFrame((_, dt) => {
|
271
274
|
const world = worldRef.current;
|
272
275
|
if (!world) return;
|
273
|
-
world.timestep = _timeStep;
|
274
276
|
/**
|
275
277
|
* Fixed timeStep simulation progression
|
276
278
|
* @see https://gafferongames.com/post/fix_your_timestep/
|
277
|
-
|
278
|
-
|
279
|
-
|
279
|
+
*/
|
280
|
+
|
281
|
+
const clampedDelta = three.MathUtils.clamp(dt, 0, 0.2);
|
282
|
+
|
283
|
+
if (timeStepVariable) {
|
284
|
+
world.timestep = clampedDelta;
|
285
|
+
if (!_paused) world.step(eventQueue);
|
286
|
+
} else {
|
287
|
+
world.timestep = _timeStep; // don't step time forwards if paused
|
288
|
+
// Increase accumulator
|
280
289
|
|
281
|
-
|
290
|
+
steppingState.accumulator += _paused ? 0 : clampedDelta;
|
282
291
|
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
292
|
+
if (!_paused) {
|
293
|
+
while (steppingState.accumulator >= _timeStep) {
|
294
|
+
world.step(eventQueue);
|
295
|
+
steppingState.accumulator -= _timeStep;
|
296
|
+
}
|
287
297
|
}
|
288
298
|
}
|
289
299
|
|
290
|
-
const interpolationAlpha = steppingState.accumulator % _timeStep / _timeStep; // Update meshes
|
300
|
+
const interpolationAlpha = timeStepVariable ? 1 : steppingState.accumulator % _timeStep / _timeStep; // Update meshes
|
291
301
|
|
292
302
|
rigidBodyStates.forEach((state, handle) => {
|
293
303
|
const rigidBody = world.getRigidBody(handle);
|
@@ -947,7 +957,7 @@ const useChildColliderProps = (ref, options, ignoreMeshColliders = true) => {
|
|
947
957
|
ignoreMeshColliders
|
948
958
|
}));
|
949
959
|
}
|
950
|
-
}, [options]);
|
960
|
+
}, [options.colliders]);
|
951
961
|
return colliderProps;
|
952
962
|
};
|
953
963
|
const useRigidBody = (options = {}) => {
|
@@ -1209,12 +1219,13 @@ const RigidBody = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1209
1219
|
|
1210
1220
|
const [object, api, childColliderProps] = useRigidBody(props);
|
1211
1221
|
React.useImperativeHandle(ref, () => api);
|
1222
|
+
const contextValue = React.useMemo(() => ({
|
1223
|
+
ref: object,
|
1224
|
+
api,
|
1225
|
+
options: props
|
1226
|
+
}), [object, api, props]);
|
1212
1227
|
return /*#__PURE__*/React__default["default"].createElement(RigidBodyContext.Provider, {
|
1213
|
-
value:
|
1214
|
-
ref: object,
|
1215
|
-
api,
|
1216
|
-
options: props
|
1217
|
-
}
|
1228
|
+
value: contextValue
|
1218
1229
|
}, /*#__PURE__*/React__default["default"].createElement("object3D", _extends({
|
1219
1230
|
ref: object
|
1220
1231
|
}, objectProps, {
|
@@ -1449,6 +1460,7 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1449
1460
|
options = _objectWithoutProperties(props, _excluded);
|
1450
1461
|
|
1451
1462
|
const instancesRef = React.useRef([]);
|
1463
|
+
const rigidBodyRefs = React.useRef([]);
|
1452
1464
|
const instancesRefGetter = React.useRef(() => {
|
1453
1465
|
if (!instancesRef.current) {
|
1454
1466
|
instancesRef.current = [];
|
@@ -1462,7 +1474,7 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1462
1474
|
const childColliderProps = useChildColliderProps(object, mergedOptions);
|
1463
1475
|
React.useLayoutEffect(() => {
|
1464
1476
|
object.current.updateWorldMatrix(true, false);
|
1465
|
-
const
|
1477
|
+
const instances = instancesRefGetter.current();
|
1466
1478
|
const invertedWorld = object.current.matrixWorld.clone().invert();
|
1467
1479
|
object.current.traverseVisible(mesh => {
|
1468
1480
|
if (mesh instanceof three.InstancedMesh) {
|
@@ -1474,6 +1486,7 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1474
1486
|
|
1475
1487
|
const desc = rigidBodyDescFromOptions(props);
|
1476
1488
|
const rigidBody = world.createRigidBody(desc);
|
1489
|
+
rigidBodyRefs.current.push(rigidBody);
|
1477
1490
|
const scale = ((_options$scales = options.scales) === null || _options$scales === void 0 ? void 0 : _options$scales[index]) || [1, 1, 1];
|
1478
1491
|
const instanceScale = worldScale.clone().multiply(vectorArrayToVector3(scale));
|
1479
1492
|
rigidBodyStates.set(rigidBody.handle, createRigidBodyState({
|
@@ -1504,7 +1517,7 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1504
1517
|
}
|
1505
1518
|
|
1506
1519
|
});
|
1507
|
-
|
1520
|
+
instances.push({
|
1508
1521
|
rigidBody,
|
1509
1522
|
api
|
1510
1523
|
});
|
@@ -1512,31 +1525,27 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1512
1525
|
}
|
1513
1526
|
});
|
1514
1527
|
return () => {
|
1515
|
-
|
1528
|
+
instances.forEach(rb => {
|
1516
1529
|
world.removeRigidBody(rb.rigidBody);
|
1517
1530
|
rigidBodyStates.delete(rb.rigidBody.handle);
|
1518
1531
|
});
|
1532
|
+
rigidBodyRefs.current = [];
|
1519
1533
|
instancesRef.current = [];
|
1520
1534
|
};
|
1521
1535
|
}, []);
|
1522
1536
|
const api = React.useMemo(() => createInstancedRigidBodiesApi(instancesRefGetter), []);
|
1523
1537
|
React.useImperativeHandle(ref, () => api);
|
1524
|
-
useUpdateRigidBodyOptions(
|
1525
|
-
|
1526
|
-
|
1527
|
-
|
1528
|
-
}, mergedOptions, rigidBodyStates, false);
|
1529
|
-
useRigidBodyEvents({
|
1530
|
-
current: instancesRef.current.map(({
|
1531
|
-
rigidBody
|
1532
|
-
}) => rigidBody)
|
1533
|
-
}, mergedOptions, rigidBodyEvents);
|
1534
|
-
return /*#__PURE__*/React__default["default"].createElement(RigidBodyContext.Provider, {
|
1535
|
-
value: {
|
1538
|
+
useUpdateRigidBodyOptions(rigidBodyRefs, mergedOptions, rigidBodyStates, false);
|
1539
|
+
useRigidBodyEvents(rigidBodyRefs, mergedOptions, rigidBodyEvents);
|
1540
|
+
const contextValue = React.useMemo(() => {
|
1541
|
+
return {
|
1536
1542
|
ref: object,
|
1537
1543
|
api,
|
1538
|
-
options:
|
1539
|
-
}
|
1544
|
+
options: mergedOptions
|
1545
|
+
};
|
1546
|
+
}, [api, mergedOptions]);
|
1547
|
+
return /*#__PURE__*/React__default["default"].createElement(RigidBodyContext.Provider, {
|
1548
|
+
value: contextValue
|
1540
1549
|
}, /*#__PURE__*/React__default["default"].createElement("object3D", {
|
1541
1550
|
ref: object
|
1542
1551
|
}, props.children, childColliderProps.map((colliderProps, index) => /*#__PURE__*/React__default["default"].createElement(AnyCollider, _extends({
|
@@ -7,7 +7,6 @@ var React = require('react');
|
|
7
7
|
var useAsset = require('use-asset');
|
8
8
|
var fiber = require('@react-three/fiber');
|
9
9
|
var three = require('three');
|
10
|
-
var MathUtils = require('three/src/math/MathUtils');
|
11
10
|
var threeStdlib = require('three-stdlib');
|
12
11
|
|
13
12
|
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
|
@@ -267,27 +266,38 @@ const Physics = ({
|
|
267
266
|
const [steppingState] = React.useState({
|
268
267
|
accumulator: 0
|
269
268
|
});
|
269
|
+
/* Check if the timestep is supposed to be variable. We'll do this here
|
270
|
+
once so we don't have to string-check every frame. */
|
271
|
+
|
272
|
+
const timeStepVariable = _timeStep === "vary";
|
270
273
|
fiber.useFrame((_, dt) => {
|
271
274
|
const world = worldRef.current;
|
272
275
|
if (!world) return;
|
273
|
-
world.timestep = _timeStep;
|
274
276
|
/**
|
275
277
|
* Fixed timeStep simulation progression
|
276
278
|
* @see https://gafferongames.com/post/fix_your_timestep/
|
277
|
-
|
278
|
-
|
279
|
-
|
279
|
+
*/
|
280
|
+
|
281
|
+
const clampedDelta = three.MathUtils.clamp(dt, 0, 0.2);
|
282
|
+
|
283
|
+
if (timeStepVariable) {
|
284
|
+
world.timestep = clampedDelta;
|
285
|
+
if (!_paused) world.step(eventQueue);
|
286
|
+
} else {
|
287
|
+
world.timestep = _timeStep; // don't step time forwards if paused
|
288
|
+
// Increase accumulator
|
280
289
|
|
281
|
-
|
290
|
+
steppingState.accumulator += _paused ? 0 : clampedDelta;
|
282
291
|
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
292
|
+
if (!_paused) {
|
293
|
+
while (steppingState.accumulator >= _timeStep) {
|
294
|
+
world.step(eventQueue);
|
295
|
+
steppingState.accumulator -= _timeStep;
|
296
|
+
}
|
287
297
|
}
|
288
298
|
}
|
289
299
|
|
290
|
-
const interpolationAlpha = steppingState.accumulator % _timeStep / _timeStep; // Update meshes
|
300
|
+
const interpolationAlpha = timeStepVariable ? 1 : steppingState.accumulator % _timeStep / _timeStep; // Update meshes
|
291
301
|
|
292
302
|
rigidBodyStates.forEach((state, handle) => {
|
293
303
|
const rigidBody = world.getRigidBody(handle);
|
@@ -947,7 +957,7 @@ const useChildColliderProps = (ref, options, ignoreMeshColliders = true) => {
|
|
947
957
|
ignoreMeshColliders
|
948
958
|
}));
|
949
959
|
}
|
950
|
-
}, [options]);
|
960
|
+
}, [options.colliders]);
|
951
961
|
return colliderProps;
|
952
962
|
};
|
953
963
|
const useRigidBody = (options = {}) => {
|
@@ -1209,12 +1219,13 @@ const RigidBody = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1209
1219
|
|
1210
1220
|
const [object, api, childColliderProps] = useRigidBody(props);
|
1211
1221
|
React.useImperativeHandle(ref, () => api);
|
1222
|
+
const contextValue = React.useMemo(() => ({
|
1223
|
+
ref: object,
|
1224
|
+
api,
|
1225
|
+
options: props
|
1226
|
+
}), [object, api, props]);
|
1212
1227
|
return /*#__PURE__*/React__default["default"].createElement(RigidBodyContext.Provider, {
|
1213
|
-
value:
|
1214
|
-
ref: object,
|
1215
|
-
api,
|
1216
|
-
options: props
|
1217
|
-
}
|
1228
|
+
value: contextValue
|
1218
1229
|
}, /*#__PURE__*/React__default["default"].createElement("object3D", _extends({
|
1219
1230
|
ref: object
|
1220
1231
|
}, objectProps, {
|
@@ -1449,6 +1460,7 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1449
1460
|
options = _objectWithoutProperties(props, _excluded);
|
1450
1461
|
|
1451
1462
|
const instancesRef = React.useRef([]);
|
1463
|
+
const rigidBodyRefs = React.useRef([]);
|
1452
1464
|
const instancesRefGetter = React.useRef(() => {
|
1453
1465
|
if (!instancesRef.current) {
|
1454
1466
|
instancesRef.current = [];
|
@@ -1462,7 +1474,7 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1462
1474
|
const childColliderProps = useChildColliderProps(object, mergedOptions);
|
1463
1475
|
React.useLayoutEffect(() => {
|
1464
1476
|
object.current.updateWorldMatrix(true, false);
|
1465
|
-
const
|
1477
|
+
const instances = instancesRefGetter.current();
|
1466
1478
|
const invertedWorld = object.current.matrixWorld.clone().invert();
|
1467
1479
|
object.current.traverseVisible(mesh => {
|
1468
1480
|
if (mesh instanceof three.InstancedMesh) {
|
@@ -1474,6 +1486,7 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1474
1486
|
|
1475
1487
|
const desc = rigidBodyDescFromOptions(props);
|
1476
1488
|
const rigidBody = world.createRigidBody(desc);
|
1489
|
+
rigidBodyRefs.current.push(rigidBody);
|
1477
1490
|
const scale = ((_options$scales = options.scales) === null || _options$scales === void 0 ? void 0 : _options$scales[index]) || [1, 1, 1];
|
1478
1491
|
const instanceScale = worldScale.clone().multiply(vectorArrayToVector3(scale));
|
1479
1492
|
rigidBodyStates.set(rigidBody.handle, createRigidBodyState({
|
@@ -1504,7 +1517,7 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1504
1517
|
}
|
1505
1518
|
|
1506
1519
|
});
|
1507
|
-
|
1520
|
+
instances.push({
|
1508
1521
|
rigidBody,
|
1509
1522
|
api
|
1510
1523
|
});
|
@@ -1512,31 +1525,27 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1512
1525
|
}
|
1513
1526
|
});
|
1514
1527
|
return () => {
|
1515
|
-
|
1528
|
+
instances.forEach(rb => {
|
1516
1529
|
world.removeRigidBody(rb.rigidBody);
|
1517
1530
|
rigidBodyStates.delete(rb.rigidBody.handle);
|
1518
1531
|
});
|
1532
|
+
rigidBodyRefs.current = [];
|
1519
1533
|
instancesRef.current = [];
|
1520
1534
|
};
|
1521
1535
|
}, []);
|
1522
1536
|
const api = React.useMemo(() => createInstancedRigidBodiesApi(instancesRefGetter), []);
|
1523
1537
|
React.useImperativeHandle(ref, () => api);
|
1524
|
-
useUpdateRigidBodyOptions(
|
1525
|
-
|
1526
|
-
|
1527
|
-
|
1528
|
-
}, mergedOptions, rigidBodyStates, false);
|
1529
|
-
useRigidBodyEvents({
|
1530
|
-
current: instancesRef.current.map(({
|
1531
|
-
rigidBody
|
1532
|
-
}) => rigidBody)
|
1533
|
-
}, mergedOptions, rigidBodyEvents);
|
1534
|
-
return /*#__PURE__*/React__default["default"].createElement(RigidBodyContext.Provider, {
|
1535
|
-
value: {
|
1538
|
+
useUpdateRigidBodyOptions(rigidBodyRefs, mergedOptions, rigidBodyStates, false);
|
1539
|
+
useRigidBodyEvents(rigidBodyRefs, mergedOptions, rigidBodyEvents);
|
1540
|
+
const contextValue = React.useMemo(() => {
|
1541
|
+
return {
|
1536
1542
|
ref: object,
|
1537
1543
|
api,
|
1538
|
-
options:
|
1539
|
-
}
|
1544
|
+
options: mergedOptions
|
1545
|
+
};
|
1546
|
+
}, [api, mergedOptions]);
|
1547
|
+
return /*#__PURE__*/React__default["default"].createElement(RigidBodyContext.Provider, {
|
1548
|
+
value: contextValue
|
1540
1549
|
}, /*#__PURE__*/React__default["default"].createElement("object3D", {
|
1541
1550
|
ref: object
|
1542
1551
|
}, props.children, childColliderProps.map((colliderProps, index) => /*#__PURE__*/React__default["default"].createElement(AnyCollider, _extends({
|
@@ -3,8 +3,7 @@ export { CoefficientCombineRule, Collider as RapierCollider, RigidBody as Rapier
|
|
3
3
|
import React, { useState, useEffect, useRef, useMemo, createContext, useContext, memo, forwardRef, useImperativeHandle, useLayoutEffect } from 'react';
|
4
4
|
import { useAsset } from 'use-asset';
|
5
5
|
import { useFrame } from '@react-three/fiber';
|
6
|
-
import { Quaternion, Euler, Vector3, Object3D, Matrix4, InstancedMesh, MeshBasicMaterial, Color, PlaneGeometry, ConeBufferGeometry, CapsuleBufferGeometry, CylinderBufferGeometry, BufferGeometry, BufferAttribute, SphereBufferGeometry, BoxBufferGeometry, DynamicDrawUsage } from 'three';
|
7
|
-
import { clamp } from 'three/src/math/MathUtils';
|
6
|
+
import { Quaternion, Euler, Vector3, Object3D, Matrix4, MathUtils, InstancedMesh, MeshBasicMaterial, Color, PlaneGeometry, ConeBufferGeometry, CapsuleBufferGeometry, CylinderBufferGeometry, BufferGeometry, BufferAttribute, SphereBufferGeometry, BoxBufferGeometry, DynamicDrawUsage } from 'three';
|
8
7
|
import { mergeVertices, RoundedBoxGeometry } from 'three-stdlib';
|
9
8
|
|
10
9
|
const _quaternion = new Quaternion();
|
@@ -242,27 +241,38 @@ const Physics = ({
|
|
242
241
|
const [steppingState] = useState({
|
243
242
|
accumulator: 0
|
244
243
|
});
|
244
|
+
/* Check if the timestep is supposed to be variable. We'll do this here
|
245
|
+
once so we don't have to string-check every frame. */
|
246
|
+
|
247
|
+
const timeStepVariable = _timeStep === "vary";
|
245
248
|
useFrame((_, dt) => {
|
246
249
|
const world = worldRef.current;
|
247
250
|
if (!world) return;
|
248
|
-
world.timestep = _timeStep;
|
249
251
|
/**
|
250
252
|
* Fixed timeStep simulation progression
|
251
253
|
* @see https://gafferongames.com/post/fix_your_timestep/
|
252
|
-
|
253
|
-
|
254
|
-
|
254
|
+
*/
|
255
|
+
|
256
|
+
const clampedDelta = MathUtils.clamp(dt, 0, 0.2);
|
257
|
+
|
258
|
+
if (timeStepVariable) {
|
259
|
+
world.timestep = clampedDelta;
|
260
|
+
if (!_paused) world.step(eventQueue);
|
261
|
+
} else {
|
262
|
+
world.timestep = _timeStep; // don't step time forwards if paused
|
263
|
+
// Increase accumulator
|
255
264
|
|
256
|
-
|
265
|
+
steppingState.accumulator += _paused ? 0 : clampedDelta;
|
257
266
|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
267
|
+
if (!_paused) {
|
268
|
+
while (steppingState.accumulator >= _timeStep) {
|
269
|
+
world.step(eventQueue);
|
270
|
+
steppingState.accumulator -= _timeStep;
|
271
|
+
}
|
262
272
|
}
|
263
273
|
}
|
264
274
|
|
265
|
-
const interpolationAlpha = steppingState.accumulator % _timeStep / _timeStep; // Update meshes
|
275
|
+
const interpolationAlpha = timeStepVariable ? 1 : steppingState.accumulator % _timeStep / _timeStep; // Update meshes
|
266
276
|
|
267
277
|
rigidBodyStates.forEach((state, handle) => {
|
268
278
|
const rigidBody = world.getRigidBody(handle);
|
@@ -922,7 +932,7 @@ const useChildColliderProps = (ref, options, ignoreMeshColliders = true) => {
|
|
922
932
|
ignoreMeshColliders
|
923
933
|
}));
|
924
934
|
}
|
925
|
-
}, [options]);
|
935
|
+
}, [options.colliders]);
|
926
936
|
return colliderProps;
|
927
937
|
};
|
928
938
|
const useRigidBody = (options = {}) => {
|
@@ -1184,12 +1194,13 @@ const RigidBody = /*#__PURE__*/forwardRef((props, ref) => {
|
|
1184
1194
|
|
1185
1195
|
const [object, api, childColliderProps] = useRigidBody(props);
|
1186
1196
|
useImperativeHandle(ref, () => api);
|
1197
|
+
const contextValue = useMemo(() => ({
|
1198
|
+
ref: object,
|
1199
|
+
api,
|
1200
|
+
options: props
|
1201
|
+
}), [object, api, props]);
|
1187
1202
|
return /*#__PURE__*/React.createElement(RigidBodyContext.Provider, {
|
1188
|
-
value:
|
1189
|
-
ref: object,
|
1190
|
-
api,
|
1191
|
-
options: props
|
1192
|
-
}
|
1203
|
+
value: contextValue
|
1193
1204
|
}, /*#__PURE__*/React.createElement("object3D", _extends({
|
1194
1205
|
ref: object
|
1195
1206
|
}, objectProps, {
|
@@ -1424,6 +1435,7 @@ const InstancedRigidBodies = /*#__PURE__*/forwardRef((props, ref) => {
|
|
1424
1435
|
options = _objectWithoutProperties(props, _excluded);
|
1425
1436
|
|
1426
1437
|
const instancesRef = useRef([]);
|
1438
|
+
const rigidBodyRefs = useRef([]);
|
1427
1439
|
const instancesRefGetter = useRef(() => {
|
1428
1440
|
if (!instancesRef.current) {
|
1429
1441
|
instancesRef.current = [];
|
@@ -1437,7 +1449,7 @@ const InstancedRigidBodies = /*#__PURE__*/forwardRef((props, ref) => {
|
|
1437
1449
|
const childColliderProps = useChildColliderProps(object, mergedOptions);
|
1438
1450
|
useLayoutEffect(() => {
|
1439
1451
|
object.current.updateWorldMatrix(true, false);
|
1440
|
-
const
|
1452
|
+
const instances = instancesRefGetter.current();
|
1441
1453
|
const invertedWorld = object.current.matrixWorld.clone().invert();
|
1442
1454
|
object.current.traverseVisible(mesh => {
|
1443
1455
|
if (mesh instanceof InstancedMesh) {
|
@@ -1449,6 +1461,7 @@ const InstancedRigidBodies = /*#__PURE__*/forwardRef((props, ref) => {
|
|
1449
1461
|
|
1450
1462
|
const desc = rigidBodyDescFromOptions(props);
|
1451
1463
|
const rigidBody = world.createRigidBody(desc);
|
1464
|
+
rigidBodyRefs.current.push(rigidBody);
|
1452
1465
|
const scale = ((_options$scales = options.scales) === null || _options$scales === void 0 ? void 0 : _options$scales[index]) || [1, 1, 1];
|
1453
1466
|
const instanceScale = worldScale.clone().multiply(vectorArrayToVector3(scale));
|
1454
1467
|
rigidBodyStates.set(rigidBody.handle, createRigidBodyState({
|
@@ -1479,7 +1492,7 @@ const InstancedRigidBodies = /*#__PURE__*/forwardRef((props, ref) => {
|
|
1479
1492
|
}
|
1480
1493
|
|
1481
1494
|
});
|
1482
|
-
|
1495
|
+
instances.push({
|
1483
1496
|
rigidBody,
|
1484
1497
|
api
|
1485
1498
|
});
|
@@ -1487,31 +1500,27 @@ const InstancedRigidBodies = /*#__PURE__*/forwardRef((props, ref) => {
|
|
1487
1500
|
}
|
1488
1501
|
});
|
1489
1502
|
return () => {
|
1490
|
-
|
1503
|
+
instances.forEach(rb => {
|
1491
1504
|
world.removeRigidBody(rb.rigidBody);
|
1492
1505
|
rigidBodyStates.delete(rb.rigidBody.handle);
|
1493
1506
|
});
|
1507
|
+
rigidBodyRefs.current = [];
|
1494
1508
|
instancesRef.current = [];
|
1495
1509
|
};
|
1496
1510
|
}, []);
|
1497
1511
|
const api = useMemo(() => createInstancedRigidBodiesApi(instancesRefGetter), []);
|
1498
1512
|
useImperativeHandle(ref, () => api);
|
1499
|
-
useUpdateRigidBodyOptions(
|
1500
|
-
|
1501
|
-
|
1502
|
-
|
1503
|
-
}, mergedOptions, rigidBodyStates, false);
|
1504
|
-
useRigidBodyEvents({
|
1505
|
-
current: instancesRef.current.map(({
|
1506
|
-
rigidBody
|
1507
|
-
}) => rigidBody)
|
1508
|
-
}, mergedOptions, rigidBodyEvents);
|
1509
|
-
return /*#__PURE__*/React.createElement(RigidBodyContext.Provider, {
|
1510
|
-
value: {
|
1513
|
+
useUpdateRigidBodyOptions(rigidBodyRefs, mergedOptions, rigidBodyStates, false);
|
1514
|
+
useRigidBodyEvents(rigidBodyRefs, mergedOptions, rigidBodyEvents);
|
1515
|
+
const contextValue = useMemo(() => {
|
1516
|
+
return {
|
1511
1517
|
ref: object,
|
1512
1518
|
api,
|
1513
|
-
options:
|
1514
|
-
}
|
1519
|
+
options: mergedOptions
|
1520
|
+
};
|
1521
|
+
}, [api, mergedOptions]);
|
1522
|
+
return /*#__PURE__*/React.createElement(RigidBodyContext.Provider, {
|
1523
|
+
value: contextValue
|
1515
1524
|
}, /*#__PURE__*/React.createElement("object3D", {
|
1516
1525
|
ref: object
|
1517
1526
|
}, props.children, childColliderProps.map((colliderProps, index) => /*#__PURE__*/React.createElement(AnyCollider, _extends({
|
package/package.json
CHANGED
package/readme.md
CHANGED
@@ -141,7 +141,7 @@ const Scene = () => {
|
|
141
141
|
instancedApi.at(40).applyImpulse({ x: 0, y: 10, z: 0 });
|
142
142
|
|
143
143
|
// Or update all instances as if they were in an array
|
144
|
-
instancedApi.forEach(api => {
|
144
|
+
instancedApi.forEach((api) => {
|
145
145
|
api.applyImpulse({ x: 0, y: 10, z: 0 });
|
146
146
|
});
|
147
147
|
}, []);
|
@@ -239,10 +239,10 @@ You may also subscribe to collision events on individual Colliders:
|
|
239
239
|
|
240
240
|
```tsx
|
241
241
|
<CuboidCollider
|
242
|
-
onCollisionEnter={payload => {
|
242
|
+
onCollisionEnter={(payload) => {
|
243
243
|
/* ... */
|
244
244
|
}}
|
245
|
-
onCollisionExit={payload => {
|
245
|
+
onCollisionExit={(payload) => {
|
246
246
|
/* ... */
|
247
247
|
}}
|
248
248
|
/>
|
@@ -309,6 +309,22 @@ A Collider can be set to be a sensor, which means that it will not generate any
|
|
309
309
|
|
310
310
|
WIP
|
311
311
|
|
312
|
+
## Configuring Time Step Size
|
313
|
+
|
314
|
+
By default, `<Physics>` will simulate the physics world at a fixed rate of 60 frames per second. This can be changed by setting the `timeStep` prop on `<Physics>`:
|
315
|
+
|
316
|
+
```tsx
|
317
|
+
<Physics timeStep={1 / 30}>{/* ... */}</Physics>
|
318
|
+
```
|
319
|
+
|
320
|
+
The `timeStep` prop may also be set to `"vary"`, which will cause the simulation's time step to adjust to every frame's frame delta:
|
321
|
+
|
322
|
+
```tsx
|
323
|
+
<Physics timeStep="vary">{/* ... */}</Physics>
|
324
|
+
```
|
325
|
+
|
326
|
+
> **Note** This is useful for games that run at variable frame rates, but may cause instability in the simulation. It also prevents the physics simulation from being fully deterministic. Please use with care!
|
327
|
+
|
312
328
|
---
|
313
329
|
|
314
330
|
## Roadmap
|
@@ -326,6 +342,6 @@ In order, but also not necessarily:
|
|
326
342
|
- [x] InstancedMesh support
|
327
343
|
- [x] Timestep improvements for determinism
|
328
344
|
- [x] Normalize and improve collision events (add events to single Colliders)
|
329
|
-
- [
|
345
|
+
- [x] Add collision events to InstancedRigidBodies
|
330
346
|
- [ ] Docs
|
331
347
|
- [ ] CodeSandbox examples
|