@react-three/rapier 0.7.0 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/declarations/src/Physics.d.ts +4 -2
- package/dist/declarations/src/types.d.ts +4 -4
- package/dist/declarations/src/utils-rigidbody.d.ts +3 -3
- package/dist/react-three-rapier.cjs.dev.js +87 -44
- package/dist/react-three-rapier.cjs.prod.js +87 -44
- package/dist/react-three-rapier.esm.js +87 -44
- package/package.json +1 -1
- package/readme.md +17 -1
@@ -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
|
*
|
@@ -118,11 +118,11 @@ 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
|
*/
|
@@ -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.
|
@@ -14,7 +14,7 @@ interface CreateRigidBodyStateOptions {
|
|
14
14
|
export declare const createRigidBodyState: ({ rigidBody, object, setMatrix, getMatrix, worldScale }: CreateRigidBodyStateOptions) => RigidBodyState;
|
15
15
|
declare type ImmutableRigidBodyOptions = (keyof RigidBodyProps)[];
|
16
16
|
export declare const immutableRigidBodyOptions: ImmutableRigidBodyOptions;
|
17
|
-
export declare const setRigidBodyOptions: (rigidBody: RigidBody, options: RigidBodyProps, states: RigidBodyStateMap) => void;
|
18
|
-
export declare const useUpdateRigidBodyOptions: (rigidBodyRef: MutableRefObject<RigidBody | undefined>, props: RigidBodyProps, states: RigidBodyStateMap) => void;
|
19
|
-
export declare const useRigidBodyEvents: (rigidBodyRef: MutableRefObject<RigidBody | undefined>, props: RigidBodyProps, events: EventMap) => void;
|
17
|
+
export declare const setRigidBodyOptions: (rigidBody: RigidBody, options: RigidBodyProps, states: RigidBodyStateMap, updateTranslations?: boolean) => void;
|
18
|
+
export declare const useUpdateRigidBodyOptions: (rigidBodyRef: MutableRefObject<RigidBody | RigidBody[] | undefined>, props: RigidBodyProps, states: RigidBodyStateMap, updateTranslations?: boolean) => void;
|
19
|
+
export declare const useRigidBodyEvents: (rigidBodyRef: MutableRefObject<RigidBody | RigidBody[] | undefined>, props: RigidBodyProps, events: EventMap) => void;
|
20
20
|
export {};
|
@@ -267,27 +267,38 @@ const Physics = ({
|
|
267
267
|
const [steppingState] = React.useState({
|
268
268
|
accumulator: 0
|
269
269
|
});
|
270
|
+
/* Check if the timestep is supposed to be variable. We'll do this here
|
271
|
+
once so we don't have to string-check every frame. */
|
272
|
+
|
273
|
+
const timeStepVariable = _timeStep === "vary";
|
270
274
|
fiber.useFrame((_, dt) => {
|
271
275
|
const world = worldRef.current;
|
272
276
|
if (!world) return;
|
273
|
-
world.timestep = _timeStep;
|
274
277
|
/**
|
275
278
|
* Fixed timeStep simulation progression
|
276
279
|
* @see https://gafferongames.com/post/fix_your_timestep/
|
277
280
|
*/
|
278
|
-
// don't step time forwards if paused
|
279
|
-
// Increase accumulator
|
280
281
|
|
281
|
-
|
282
|
+
const clampedDelta = MathUtils.clamp(dt, 0, 0.2);
|
283
|
+
|
284
|
+
if (timeStepVariable) {
|
285
|
+
world.timestep = clampedDelta;
|
286
|
+
if (!_paused) world.step(eventQueue);
|
287
|
+
} else {
|
288
|
+
world.timestep = _timeStep; // don't step time forwards if paused
|
289
|
+
// Increase accumulator
|
282
290
|
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
steppingState.accumulator
|
291
|
+
steppingState.accumulator += _paused ? 0 : clampedDelta;
|
292
|
+
|
293
|
+
if (!_paused) {
|
294
|
+
while (steppingState.accumulator >= _timeStep) {
|
295
|
+
world.step(eventQueue);
|
296
|
+
steppingState.accumulator -= _timeStep;
|
297
|
+
}
|
287
298
|
}
|
288
299
|
}
|
289
300
|
|
290
|
-
const interpolationAlpha = steppingState.accumulator % _timeStep / _timeStep; // Update meshes
|
301
|
+
const interpolationAlpha = timeStepVariable ? 1 : steppingState.accumulator % _timeStep / _timeStep; // Update meshes
|
291
302
|
|
292
303
|
rigidBodyStates.forEach((state, handle) => {
|
293
304
|
const rigidBody = world.getRigidBody(handle);
|
@@ -636,7 +647,7 @@ const mutableRigidBodyOptions = {
|
|
636
647
|
}
|
637
648
|
};
|
638
649
|
const mutableRigidBodyOptionKeys = Object.keys(mutableRigidBodyOptions);
|
639
|
-
const setRigidBodyOptions = (rigidBody, options, states) => {
|
650
|
+
const setRigidBodyOptions = (rigidBody, options, states, updateTranslations = true) => {
|
640
651
|
if (!rigidBody) {
|
641
652
|
return;
|
642
653
|
}
|
@@ -644,12 +655,15 @@ const setRigidBodyOptions = (rigidBody, options, states) => {
|
|
644
655
|
const state = states.get(rigidBody.handle);
|
645
656
|
|
646
657
|
if (state) {
|
647
|
-
|
658
|
+
if (updateTranslations) {
|
659
|
+
state.object.updateWorldMatrix(true, false);
|
660
|
+
|
661
|
+
_matrix4.copy(state.object.matrixWorld).decompose(_position, _rotation, _scale);
|
648
662
|
|
649
|
-
|
663
|
+
rigidBody.setTranslation(_position, false);
|
664
|
+
rigidBody.setRotation(_rotation, false);
|
665
|
+
}
|
650
666
|
|
651
|
-
rigidBody.setTranslation(_position, false);
|
652
|
-
rigidBody.setRotation(_rotation, false);
|
653
667
|
mutableRigidBodyOptionKeys.forEach(key => {
|
654
668
|
if (key in options) {
|
655
669
|
mutableRigidBodyOptions[key](rigidBody, options[key]);
|
@@ -657,9 +671,15 @@ const setRigidBodyOptions = (rigidBody, options, states) => {
|
|
657
671
|
});
|
658
672
|
}
|
659
673
|
};
|
660
|
-
const useUpdateRigidBodyOptions = (rigidBodyRef, props, states) => {
|
674
|
+
const useUpdateRigidBodyOptions = (rigidBodyRef, props, states, updateTranslations = true) => {
|
661
675
|
React.useEffect(() => {
|
662
|
-
|
676
|
+
if ("length" in rigidBodyRef.current) {
|
677
|
+
rigidBodyRef.current.forEach(rigidBody => {
|
678
|
+
setRigidBodyOptions(rigidBody, props, states, updateTranslations);
|
679
|
+
});
|
680
|
+
} else {
|
681
|
+
setRigidBodyOptions(rigidBodyRef.current, props, states, updateTranslations);
|
682
|
+
}
|
663
683
|
}, [props]);
|
664
684
|
};
|
665
685
|
const useRigidBodyEvents = (rigidBodyRef, props, events) => {
|
@@ -671,17 +691,31 @@ const useRigidBodyEvents = (rigidBodyRef, props, events) => {
|
|
671
691
|
onIntersectionEnter,
|
672
692
|
onIntersectionExit
|
673
693
|
} = props;
|
694
|
+
const eventHandlers = {
|
695
|
+
onWake,
|
696
|
+
onSleep,
|
697
|
+
onCollisionEnter,
|
698
|
+
onCollisionExit,
|
699
|
+
onIntersectionEnter,
|
700
|
+
onIntersectionExit
|
701
|
+
};
|
674
702
|
React.useEffect(() => {
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
703
|
+
if ("length" in rigidBodyRef.current) {
|
704
|
+
rigidBodyRef.current.forEach(rigidBody => {
|
705
|
+
events.set(rigidBody.handle, eventHandlers);
|
706
|
+
});
|
707
|
+
} else {
|
708
|
+
events.set(rigidBodyRef.current.handle, eventHandlers);
|
709
|
+
}
|
710
|
+
|
683
711
|
return () => {
|
684
|
-
|
712
|
+
if ("length" in rigidBodyRef.current) {
|
713
|
+
rigidBodyRef.current.forEach(rigidBody => {
|
714
|
+
events.delete(rigidBody.handle);
|
715
|
+
});
|
716
|
+
} else {
|
717
|
+
events.delete(rigidBodyRef.current.handle);
|
718
|
+
}
|
685
719
|
};
|
686
720
|
}, [onWake, onSleep, onCollisionEnter, onCollisionExit]);
|
687
721
|
};
|
@@ -924,7 +958,7 @@ const useChildColliderProps = (ref, options, ignoreMeshColliders = true) => {
|
|
924
958
|
ignoreMeshColliders
|
925
959
|
}));
|
926
960
|
}
|
927
|
-
}, [options]);
|
961
|
+
}, [options.colliders]);
|
928
962
|
return colliderProps;
|
929
963
|
};
|
930
964
|
const useRigidBody = (options = {}) => {
|
@@ -1186,12 +1220,13 @@ const RigidBody = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1186
1220
|
|
1187
1221
|
const [object, api, childColliderProps] = useRigidBody(props);
|
1188
1222
|
React.useImperativeHandle(ref, () => api);
|
1223
|
+
const contextValue = React.useMemo(() => ({
|
1224
|
+
ref: object,
|
1225
|
+
api,
|
1226
|
+
options: props
|
1227
|
+
}), [object, api, props]);
|
1189
1228
|
return /*#__PURE__*/React__default["default"].createElement(RigidBodyContext.Provider, {
|
1190
|
-
value:
|
1191
|
-
ref: object,
|
1192
|
-
api,
|
1193
|
-
options: props
|
1194
|
-
}
|
1229
|
+
value: contextValue
|
1195
1230
|
}, /*#__PURE__*/React__default["default"].createElement("object3D", _extends({
|
1196
1231
|
ref: object
|
1197
1232
|
}, objectProps, {
|
@@ -1413,7 +1448,8 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1413
1448
|
const {
|
1414
1449
|
world,
|
1415
1450
|
rigidBodyStates,
|
1416
|
-
physicsOptions
|
1451
|
+
physicsOptions,
|
1452
|
+
rigidBodyEvents
|
1417
1453
|
} = useRapier();
|
1418
1454
|
const object = React.useRef(null);
|
1419
1455
|
|
@@ -1424,7 +1460,8 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1424
1460
|
} = props,
|
1425
1461
|
options = _objectWithoutProperties(props, _excluded);
|
1426
1462
|
|
1427
|
-
const instancesRef = React.useRef();
|
1463
|
+
const instancesRef = React.useRef([]);
|
1464
|
+
const rigidBodyRefs = React.useRef([]);
|
1428
1465
|
const instancesRefGetter = React.useRef(() => {
|
1429
1466
|
if (!instancesRef.current) {
|
1430
1467
|
instancesRef.current = [];
|
@@ -1438,7 +1475,7 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1438
1475
|
const childColliderProps = useChildColliderProps(object, mergedOptions);
|
1439
1476
|
React.useLayoutEffect(() => {
|
1440
1477
|
object.current.updateWorldMatrix(true, false);
|
1441
|
-
const
|
1478
|
+
const instances = instancesRefGetter.current();
|
1442
1479
|
const invertedWorld = object.current.matrixWorld.clone().invert();
|
1443
1480
|
object.current.traverseVisible(mesh => {
|
1444
1481
|
if (mesh instanceof three.InstancedMesh) {
|
@@ -1450,6 +1487,7 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1450
1487
|
|
1451
1488
|
const desc = rigidBodyDescFromOptions(props);
|
1452
1489
|
const rigidBody = world.createRigidBody(desc);
|
1490
|
+
rigidBodyRefs.current.push(rigidBody);
|
1453
1491
|
const scale = ((_options$scales = options.scales) === null || _options$scales === void 0 ? void 0 : _options$scales[index]) || [1, 1, 1];
|
1454
1492
|
const instanceScale = worldScale.clone().multiply(vectorArrayToVector3(scale));
|
1455
1493
|
rigidBodyStates.set(rigidBody.handle, createRigidBodyState({
|
@@ -1469,10 +1507,9 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1469
1507
|
|
1470
1508
|
_object3d.rotation.set(rx, ry, rz);
|
1471
1509
|
|
1472
|
-
_object3d.applyMatrix4(invertedWorld);
|
1473
|
-
// will be replaced by the setRigidBodyOption below
|
1474
|
-
|
1510
|
+
_object3d.applyMatrix4(invertedWorld);
|
1475
1511
|
|
1512
|
+
mesh.setMatrixAt(index, _object3d.matrix);
|
1476
1513
|
rigidBody.setTranslation(_object3d.position, false);
|
1477
1514
|
rigidBody.setRotation(_object3d.quaternion, false);
|
1478
1515
|
const api = createRigidBodyApi({
|
@@ -1481,7 +1518,7 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1481
1518
|
}
|
1482
1519
|
|
1483
1520
|
});
|
1484
|
-
|
1521
|
+
instances.push({
|
1485
1522
|
rigidBody,
|
1486
1523
|
api
|
1487
1524
|
});
|
@@ -1489,21 +1526,27 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1489
1526
|
}
|
1490
1527
|
});
|
1491
1528
|
return () => {
|
1492
|
-
|
1529
|
+
instances.forEach(rb => {
|
1493
1530
|
world.removeRigidBody(rb.rigidBody);
|
1494
1531
|
rigidBodyStates.delete(rb.rigidBody.handle);
|
1495
1532
|
});
|
1496
|
-
|
1533
|
+
rigidBodyRefs.current = [];
|
1534
|
+
instancesRef.current = [];
|
1497
1535
|
};
|
1498
1536
|
}, []);
|
1499
1537
|
const api = React.useMemo(() => createInstancedRigidBodiesApi(instancesRefGetter), []);
|
1500
1538
|
React.useImperativeHandle(ref, () => api);
|
1501
|
-
|
1502
|
-
|
1539
|
+
useUpdateRigidBodyOptions(rigidBodyRefs, mergedOptions, rigidBodyStates, false);
|
1540
|
+
useRigidBodyEvents(rigidBodyRefs, mergedOptions, rigidBodyEvents);
|
1541
|
+
const contextValue = React.useMemo(() => {
|
1542
|
+
return {
|
1503
1543
|
ref: object,
|
1504
1544
|
api,
|
1505
|
-
options:
|
1506
|
-
}
|
1545
|
+
options: mergedOptions
|
1546
|
+
};
|
1547
|
+
}, [api, mergedOptions]);
|
1548
|
+
return /*#__PURE__*/React__default["default"].createElement(RigidBodyContext.Provider, {
|
1549
|
+
value: contextValue
|
1507
1550
|
}, /*#__PURE__*/React__default["default"].createElement("object3D", {
|
1508
1551
|
ref: object
|
1509
1552
|
}, props.children, childColliderProps.map((colliderProps, index) => /*#__PURE__*/React__default["default"].createElement(AnyCollider, _extends({
|
@@ -267,27 +267,38 @@ const Physics = ({
|
|
267
267
|
const [steppingState] = React.useState({
|
268
268
|
accumulator: 0
|
269
269
|
});
|
270
|
+
/* Check if the timestep is supposed to be variable. We'll do this here
|
271
|
+
once so we don't have to string-check every frame. */
|
272
|
+
|
273
|
+
const timeStepVariable = _timeStep === "vary";
|
270
274
|
fiber.useFrame((_, dt) => {
|
271
275
|
const world = worldRef.current;
|
272
276
|
if (!world) return;
|
273
|
-
world.timestep = _timeStep;
|
274
277
|
/**
|
275
278
|
* Fixed timeStep simulation progression
|
276
279
|
* @see https://gafferongames.com/post/fix_your_timestep/
|
277
280
|
*/
|
278
|
-
// don't step time forwards if paused
|
279
|
-
// Increase accumulator
|
280
281
|
|
281
|
-
|
282
|
+
const clampedDelta = MathUtils.clamp(dt, 0, 0.2);
|
283
|
+
|
284
|
+
if (timeStepVariable) {
|
285
|
+
world.timestep = clampedDelta;
|
286
|
+
if (!_paused) world.step(eventQueue);
|
287
|
+
} else {
|
288
|
+
world.timestep = _timeStep; // don't step time forwards if paused
|
289
|
+
// Increase accumulator
|
282
290
|
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
steppingState.accumulator
|
291
|
+
steppingState.accumulator += _paused ? 0 : clampedDelta;
|
292
|
+
|
293
|
+
if (!_paused) {
|
294
|
+
while (steppingState.accumulator >= _timeStep) {
|
295
|
+
world.step(eventQueue);
|
296
|
+
steppingState.accumulator -= _timeStep;
|
297
|
+
}
|
287
298
|
}
|
288
299
|
}
|
289
300
|
|
290
|
-
const interpolationAlpha = steppingState.accumulator % _timeStep / _timeStep; // Update meshes
|
301
|
+
const interpolationAlpha = timeStepVariable ? 1 : steppingState.accumulator % _timeStep / _timeStep; // Update meshes
|
291
302
|
|
292
303
|
rigidBodyStates.forEach((state, handle) => {
|
293
304
|
const rigidBody = world.getRigidBody(handle);
|
@@ -636,7 +647,7 @@ const mutableRigidBodyOptions = {
|
|
636
647
|
}
|
637
648
|
};
|
638
649
|
const mutableRigidBodyOptionKeys = Object.keys(mutableRigidBodyOptions);
|
639
|
-
const setRigidBodyOptions = (rigidBody, options, states) => {
|
650
|
+
const setRigidBodyOptions = (rigidBody, options, states, updateTranslations = true) => {
|
640
651
|
if (!rigidBody) {
|
641
652
|
return;
|
642
653
|
}
|
@@ -644,12 +655,15 @@ const setRigidBodyOptions = (rigidBody, options, states) => {
|
|
644
655
|
const state = states.get(rigidBody.handle);
|
645
656
|
|
646
657
|
if (state) {
|
647
|
-
|
658
|
+
if (updateTranslations) {
|
659
|
+
state.object.updateWorldMatrix(true, false);
|
660
|
+
|
661
|
+
_matrix4.copy(state.object.matrixWorld).decompose(_position, _rotation, _scale);
|
648
662
|
|
649
|
-
|
663
|
+
rigidBody.setTranslation(_position, false);
|
664
|
+
rigidBody.setRotation(_rotation, false);
|
665
|
+
}
|
650
666
|
|
651
|
-
rigidBody.setTranslation(_position, false);
|
652
|
-
rigidBody.setRotation(_rotation, false);
|
653
667
|
mutableRigidBodyOptionKeys.forEach(key => {
|
654
668
|
if (key in options) {
|
655
669
|
mutableRigidBodyOptions[key](rigidBody, options[key]);
|
@@ -657,9 +671,15 @@ const setRigidBodyOptions = (rigidBody, options, states) => {
|
|
657
671
|
});
|
658
672
|
}
|
659
673
|
};
|
660
|
-
const useUpdateRigidBodyOptions = (rigidBodyRef, props, states) => {
|
674
|
+
const useUpdateRigidBodyOptions = (rigidBodyRef, props, states, updateTranslations = true) => {
|
661
675
|
React.useEffect(() => {
|
662
|
-
|
676
|
+
if ("length" in rigidBodyRef.current) {
|
677
|
+
rigidBodyRef.current.forEach(rigidBody => {
|
678
|
+
setRigidBodyOptions(rigidBody, props, states, updateTranslations);
|
679
|
+
});
|
680
|
+
} else {
|
681
|
+
setRigidBodyOptions(rigidBodyRef.current, props, states, updateTranslations);
|
682
|
+
}
|
663
683
|
}, [props]);
|
664
684
|
};
|
665
685
|
const useRigidBodyEvents = (rigidBodyRef, props, events) => {
|
@@ -671,17 +691,31 @@ const useRigidBodyEvents = (rigidBodyRef, props, events) => {
|
|
671
691
|
onIntersectionEnter,
|
672
692
|
onIntersectionExit
|
673
693
|
} = props;
|
694
|
+
const eventHandlers = {
|
695
|
+
onWake,
|
696
|
+
onSleep,
|
697
|
+
onCollisionEnter,
|
698
|
+
onCollisionExit,
|
699
|
+
onIntersectionEnter,
|
700
|
+
onIntersectionExit
|
701
|
+
};
|
674
702
|
React.useEffect(() => {
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
703
|
+
if ("length" in rigidBodyRef.current) {
|
704
|
+
rigidBodyRef.current.forEach(rigidBody => {
|
705
|
+
events.set(rigidBody.handle, eventHandlers);
|
706
|
+
});
|
707
|
+
} else {
|
708
|
+
events.set(rigidBodyRef.current.handle, eventHandlers);
|
709
|
+
}
|
710
|
+
|
683
711
|
return () => {
|
684
|
-
|
712
|
+
if ("length" in rigidBodyRef.current) {
|
713
|
+
rigidBodyRef.current.forEach(rigidBody => {
|
714
|
+
events.delete(rigidBody.handle);
|
715
|
+
});
|
716
|
+
} else {
|
717
|
+
events.delete(rigidBodyRef.current.handle);
|
718
|
+
}
|
685
719
|
};
|
686
720
|
}, [onWake, onSleep, onCollisionEnter, onCollisionExit]);
|
687
721
|
};
|
@@ -924,7 +958,7 @@ const useChildColliderProps = (ref, options, ignoreMeshColliders = true) => {
|
|
924
958
|
ignoreMeshColliders
|
925
959
|
}));
|
926
960
|
}
|
927
|
-
}, [options]);
|
961
|
+
}, [options.colliders]);
|
928
962
|
return colliderProps;
|
929
963
|
};
|
930
964
|
const useRigidBody = (options = {}) => {
|
@@ -1186,12 +1220,13 @@ const RigidBody = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1186
1220
|
|
1187
1221
|
const [object, api, childColliderProps] = useRigidBody(props);
|
1188
1222
|
React.useImperativeHandle(ref, () => api);
|
1223
|
+
const contextValue = React.useMemo(() => ({
|
1224
|
+
ref: object,
|
1225
|
+
api,
|
1226
|
+
options: props
|
1227
|
+
}), [object, api, props]);
|
1189
1228
|
return /*#__PURE__*/React__default["default"].createElement(RigidBodyContext.Provider, {
|
1190
|
-
value:
|
1191
|
-
ref: object,
|
1192
|
-
api,
|
1193
|
-
options: props
|
1194
|
-
}
|
1229
|
+
value: contextValue
|
1195
1230
|
}, /*#__PURE__*/React__default["default"].createElement("object3D", _extends({
|
1196
1231
|
ref: object
|
1197
1232
|
}, objectProps, {
|
@@ -1413,7 +1448,8 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1413
1448
|
const {
|
1414
1449
|
world,
|
1415
1450
|
rigidBodyStates,
|
1416
|
-
physicsOptions
|
1451
|
+
physicsOptions,
|
1452
|
+
rigidBodyEvents
|
1417
1453
|
} = useRapier();
|
1418
1454
|
const object = React.useRef(null);
|
1419
1455
|
|
@@ -1424,7 +1460,8 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1424
1460
|
} = props,
|
1425
1461
|
options = _objectWithoutProperties(props, _excluded);
|
1426
1462
|
|
1427
|
-
const instancesRef = React.useRef();
|
1463
|
+
const instancesRef = React.useRef([]);
|
1464
|
+
const rigidBodyRefs = React.useRef([]);
|
1428
1465
|
const instancesRefGetter = React.useRef(() => {
|
1429
1466
|
if (!instancesRef.current) {
|
1430
1467
|
instancesRef.current = [];
|
@@ -1438,7 +1475,7 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1438
1475
|
const childColliderProps = useChildColliderProps(object, mergedOptions);
|
1439
1476
|
React.useLayoutEffect(() => {
|
1440
1477
|
object.current.updateWorldMatrix(true, false);
|
1441
|
-
const
|
1478
|
+
const instances = instancesRefGetter.current();
|
1442
1479
|
const invertedWorld = object.current.matrixWorld.clone().invert();
|
1443
1480
|
object.current.traverseVisible(mesh => {
|
1444
1481
|
if (mesh instanceof three.InstancedMesh) {
|
@@ -1450,6 +1487,7 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1450
1487
|
|
1451
1488
|
const desc = rigidBodyDescFromOptions(props);
|
1452
1489
|
const rigidBody = world.createRigidBody(desc);
|
1490
|
+
rigidBodyRefs.current.push(rigidBody);
|
1453
1491
|
const scale = ((_options$scales = options.scales) === null || _options$scales === void 0 ? void 0 : _options$scales[index]) || [1, 1, 1];
|
1454
1492
|
const instanceScale = worldScale.clone().multiply(vectorArrayToVector3(scale));
|
1455
1493
|
rigidBodyStates.set(rigidBody.handle, createRigidBodyState({
|
@@ -1469,10 +1507,9 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1469
1507
|
|
1470
1508
|
_object3d.rotation.set(rx, ry, rz);
|
1471
1509
|
|
1472
|
-
_object3d.applyMatrix4(invertedWorld);
|
1473
|
-
// will be replaced by the setRigidBodyOption below
|
1474
|
-
|
1510
|
+
_object3d.applyMatrix4(invertedWorld);
|
1475
1511
|
|
1512
|
+
mesh.setMatrixAt(index, _object3d.matrix);
|
1476
1513
|
rigidBody.setTranslation(_object3d.position, false);
|
1477
1514
|
rigidBody.setRotation(_object3d.quaternion, false);
|
1478
1515
|
const api = createRigidBodyApi({
|
@@ -1481,7 +1518,7 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1481
1518
|
}
|
1482
1519
|
|
1483
1520
|
});
|
1484
|
-
|
1521
|
+
instances.push({
|
1485
1522
|
rigidBody,
|
1486
1523
|
api
|
1487
1524
|
});
|
@@ -1489,21 +1526,27 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1489
1526
|
}
|
1490
1527
|
});
|
1491
1528
|
return () => {
|
1492
|
-
|
1529
|
+
instances.forEach(rb => {
|
1493
1530
|
world.removeRigidBody(rb.rigidBody);
|
1494
1531
|
rigidBodyStates.delete(rb.rigidBody.handle);
|
1495
1532
|
});
|
1496
|
-
|
1533
|
+
rigidBodyRefs.current = [];
|
1534
|
+
instancesRef.current = [];
|
1497
1535
|
};
|
1498
1536
|
}, []);
|
1499
1537
|
const api = React.useMemo(() => createInstancedRigidBodiesApi(instancesRefGetter), []);
|
1500
1538
|
React.useImperativeHandle(ref, () => api);
|
1501
|
-
|
1502
|
-
|
1539
|
+
useUpdateRigidBodyOptions(rigidBodyRefs, mergedOptions, rigidBodyStates, false);
|
1540
|
+
useRigidBodyEvents(rigidBodyRefs, mergedOptions, rigidBodyEvents);
|
1541
|
+
const contextValue = React.useMemo(() => {
|
1542
|
+
return {
|
1503
1543
|
ref: object,
|
1504
1544
|
api,
|
1505
|
-
options:
|
1506
|
-
}
|
1545
|
+
options: mergedOptions
|
1546
|
+
};
|
1547
|
+
}, [api, mergedOptions]);
|
1548
|
+
return /*#__PURE__*/React__default["default"].createElement(RigidBodyContext.Provider, {
|
1549
|
+
value: contextValue
|
1507
1550
|
}, /*#__PURE__*/React__default["default"].createElement("object3D", {
|
1508
1551
|
ref: object
|
1509
1552
|
}, props.children, childColliderProps.map((colliderProps, index) => /*#__PURE__*/React__default["default"].createElement(AnyCollider, _extends({
|
@@ -242,27 +242,38 @@ const Physics = ({
|
|
242
242
|
const [steppingState] = useState({
|
243
243
|
accumulator: 0
|
244
244
|
});
|
245
|
+
/* Check if the timestep is supposed to be variable. We'll do this here
|
246
|
+
once so we don't have to string-check every frame. */
|
247
|
+
|
248
|
+
const timeStepVariable = _timeStep === "vary";
|
245
249
|
useFrame((_, dt) => {
|
246
250
|
const world = worldRef.current;
|
247
251
|
if (!world) return;
|
248
|
-
world.timestep = _timeStep;
|
249
252
|
/**
|
250
253
|
* Fixed timeStep simulation progression
|
251
254
|
* @see https://gafferongames.com/post/fix_your_timestep/
|
252
255
|
*/
|
253
|
-
// don't step time forwards if paused
|
254
|
-
// Increase accumulator
|
255
256
|
|
256
|
-
|
257
|
+
const clampedDelta = clamp(dt, 0, 0.2);
|
258
|
+
|
259
|
+
if (timeStepVariable) {
|
260
|
+
world.timestep = clampedDelta;
|
261
|
+
if (!_paused) world.step(eventQueue);
|
262
|
+
} else {
|
263
|
+
world.timestep = _timeStep; // don't step time forwards if paused
|
264
|
+
// Increase accumulator
|
257
265
|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
steppingState.accumulator
|
266
|
+
steppingState.accumulator += _paused ? 0 : clampedDelta;
|
267
|
+
|
268
|
+
if (!_paused) {
|
269
|
+
while (steppingState.accumulator >= _timeStep) {
|
270
|
+
world.step(eventQueue);
|
271
|
+
steppingState.accumulator -= _timeStep;
|
272
|
+
}
|
262
273
|
}
|
263
274
|
}
|
264
275
|
|
265
|
-
const interpolationAlpha = steppingState.accumulator % _timeStep / _timeStep; // Update meshes
|
276
|
+
const interpolationAlpha = timeStepVariable ? 1 : steppingState.accumulator % _timeStep / _timeStep; // Update meshes
|
266
277
|
|
267
278
|
rigidBodyStates.forEach((state, handle) => {
|
268
279
|
const rigidBody = world.getRigidBody(handle);
|
@@ -611,7 +622,7 @@ const mutableRigidBodyOptions = {
|
|
611
622
|
}
|
612
623
|
};
|
613
624
|
const mutableRigidBodyOptionKeys = Object.keys(mutableRigidBodyOptions);
|
614
|
-
const setRigidBodyOptions = (rigidBody, options, states) => {
|
625
|
+
const setRigidBodyOptions = (rigidBody, options, states, updateTranslations = true) => {
|
615
626
|
if (!rigidBody) {
|
616
627
|
return;
|
617
628
|
}
|
@@ -619,12 +630,15 @@ const setRigidBodyOptions = (rigidBody, options, states) => {
|
|
619
630
|
const state = states.get(rigidBody.handle);
|
620
631
|
|
621
632
|
if (state) {
|
622
|
-
|
633
|
+
if (updateTranslations) {
|
634
|
+
state.object.updateWorldMatrix(true, false);
|
635
|
+
|
636
|
+
_matrix4.copy(state.object.matrixWorld).decompose(_position, _rotation, _scale);
|
623
637
|
|
624
|
-
|
638
|
+
rigidBody.setTranslation(_position, false);
|
639
|
+
rigidBody.setRotation(_rotation, false);
|
640
|
+
}
|
625
641
|
|
626
|
-
rigidBody.setTranslation(_position, false);
|
627
|
-
rigidBody.setRotation(_rotation, false);
|
628
642
|
mutableRigidBodyOptionKeys.forEach(key => {
|
629
643
|
if (key in options) {
|
630
644
|
mutableRigidBodyOptions[key](rigidBody, options[key]);
|
@@ -632,9 +646,15 @@ const setRigidBodyOptions = (rigidBody, options, states) => {
|
|
632
646
|
});
|
633
647
|
}
|
634
648
|
};
|
635
|
-
const useUpdateRigidBodyOptions = (rigidBodyRef, props, states) => {
|
649
|
+
const useUpdateRigidBodyOptions = (rigidBodyRef, props, states, updateTranslations = true) => {
|
636
650
|
useEffect(() => {
|
637
|
-
|
651
|
+
if ("length" in rigidBodyRef.current) {
|
652
|
+
rigidBodyRef.current.forEach(rigidBody => {
|
653
|
+
setRigidBodyOptions(rigidBody, props, states, updateTranslations);
|
654
|
+
});
|
655
|
+
} else {
|
656
|
+
setRigidBodyOptions(rigidBodyRef.current, props, states, updateTranslations);
|
657
|
+
}
|
638
658
|
}, [props]);
|
639
659
|
};
|
640
660
|
const useRigidBodyEvents = (rigidBodyRef, props, events) => {
|
@@ -646,17 +666,31 @@ const useRigidBodyEvents = (rigidBodyRef, props, events) => {
|
|
646
666
|
onIntersectionEnter,
|
647
667
|
onIntersectionExit
|
648
668
|
} = props;
|
669
|
+
const eventHandlers = {
|
670
|
+
onWake,
|
671
|
+
onSleep,
|
672
|
+
onCollisionEnter,
|
673
|
+
onCollisionExit,
|
674
|
+
onIntersectionEnter,
|
675
|
+
onIntersectionExit
|
676
|
+
};
|
649
677
|
useEffect(() => {
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
678
|
+
if ("length" in rigidBodyRef.current) {
|
679
|
+
rigidBodyRef.current.forEach(rigidBody => {
|
680
|
+
events.set(rigidBody.handle, eventHandlers);
|
681
|
+
});
|
682
|
+
} else {
|
683
|
+
events.set(rigidBodyRef.current.handle, eventHandlers);
|
684
|
+
}
|
685
|
+
|
658
686
|
return () => {
|
659
|
-
|
687
|
+
if ("length" in rigidBodyRef.current) {
|
688
|
+
rigidBodyRef.current.forEach(rigidBody => {
|
689
|
+
events.delete(rigidBody.handle);
|
690
|
+
});
|
691
|
+
} else {
|
692
|
+
events.delete(rigidBodyRef.current.handle);
|
693
|
+
}
|
660
694
|
};
|
661
695
|
}, [onWake, onSleep, onCollisionEnter, onCollisionExit]);
|
662
696
|
};
|
@@ -899,7 +933,7 @@ const useChildColliderProps = (ref, options, ignoreMeshColliders = true) => {
|
|
899
933
|
ignoreMeshColliders
|
900
934
|
}));
|
901
935
|
}
|
902
|
-
}, [options]);
|
936
|
+
}, [options.colliders]);
|
903
937
|
return colliderProps;
|
904
938
|
};
|
905
939
|
const useRigidBody = (options = {}) => {
|
@@ -1161,12 +1195,13 @@ const RigidBody = /*#__PURE__*/forwardRef((props, ref) => {
|
|
1161
1195
|
|
1162
1196
|
const [object, api, childColliderProps] = useRigidBody(props);
|
1163
1197
|
useImperativeHandle(ref, () => api);
|
1198
|
+
const contextValue = useMemo(() => ({
|
1199
|
+
ref: object,
|
1200
|
+
api,
|
1201
|
+
options: props
|
1202
|
+
}), [object, api, props]);
|
1164
1203
|
return /*#__PURE__*/React.createElement(RigidBodyContext.Provider, {
|
1165
|
-
value:
|
1166
|
-
ref: object,
|
1167
|
-
api,
|
1168
|
-
options: props
|
1169
|
-
}
|
1204
|
+
value: contextValue
|
1170
1205
|
}, /*#__PURE__*/React.createElement("object3D", _extends({
|
1171
1206
|
ref: object
|
1172
1207
|
}, objectProps, {
|
@@ -1388,7 +1423,8 @@ const InstancedRigidBodies = /*#__PURE__*/forwardRef((props, ref) => {
|
|
1388
1423
|
const {
|
1389
1424
|
world,
|
1390
1425
|
rigidBodyStates,
|
1391
|
-
physicsOptions
|
1426
|
+
physicsOptions,
|
1427
|
+
rigidBodyEvents
|
1392
1428
|
} = useRapier();
|
1393
1429
|
const object = useRef(null);
|
1394
1430
|
|
@@ -1399,7 +1435,8 @@ const InstancedRigidBodies = /*#__PURE__*/forwardRef((props, ref) => {
|
|
1399
1435
|
} = props,
|
1400
1436
|
options = _objectWithoutProperties(props, _excluded);
|
1401
1437
|
|
1402
|
-
const instancesRef = useRef();
|
1438
|
+
const instancesRef = useRef([]);
|
1439
|
+
const rigidBodyRefs = useRef([]);
|
1403
1440
|
const instancesRefGetter = useRef(() => {
|
1404
1441
|
if (!instancesRef.current) {
|
1405
1442
|
instancesRef.current = [];
|
@@ -1413,7 +1450,7 @@ const InstancedRigidBodies = /*#__PURE__*/forwardRef((props, ref) => {
|
|
1413
1450
|
const childColliderProps = useChildColliderProps(object, mergedOptions);
|
1414
1451
|
useLayoutEffect(() => {
|
1415
1452
|
object.current.updateWorldMatrix(true, false);
|
1416
|
-
const
|
1453
|
+
const instances = instancesRefGetter.current();
|
1417
1454
|
const invertedWorld = object.current.matrixWorld.clone().invert();
|
1418
1455
|
object.current.traverseVisible(mesh => {
|
1419
1456
|
if (mesh instanceof InstancedMesh) {
|
@@ -1425,6 +1462,7 @@ const InstancedRigidBodies = /*#__PURE__*/forwardRef((props, ref) => {
|
|
1425
1462
|
|
1426
1463
|
const desc = rigidBodyDescFromOptions(props);
|
1427
1464
|
const rigidBody = world.createRigidBody(desc);
|
1465
|
+
rigidBodyRefs.current.push(rigidBody);
|
1428
1466
|
const scale = ((_options$scales = options.scales) === null || _options$scales === void 0 ? void 0 : _options$scales[index]) || [1, 1, 1];
|
1429
1467
|
const instanceScale = worldScale.clone().multiply(vectorArrayToVector3(scale));
|
1430
1468
|
rigidBodyStates.set(rigidBody.handle, createRigidBodyState({
|
@@ -1444,10 +1482,9 @@ const InstancedRigidBodies = /*#__PURE__*/forwardRef((props, ref) => {
|
|
1444
1482
|
|
1445
1483
|
_object3d.rotation.set(rx, ry, rz);
|
1446
1484
|
|
1447
|
-
_object3d.applyMatrix4(invertedWorld);
|
1448
|
-
// will be replaced by the setRigidBodyOption below
|
1449
|
-
|
1485
|
+
_object3d.applyMatrix4(invertedWorld);
|
1450
1486
|
|
1487
|
+
mesh.setMatrixAt(index, _object3d.matrix);
|
1451
1488
|
rigidBody.setTranslation(_object3d.position, false);
|
1452
1489
|
rigidBody.setRotation(_object3d.quaternion, false);
|
1453
1490
|
const api = createRigidBodyApi({
|
@@ -1456,7 +1493,7 @@ const InstancedRigidBodies = /*#__PURE__*/forwardRef((props, ref) => {
|
|
1456
1493
|
}
|
1457
1494
|
|
1458
1495
|
});
|
1459
|
-
|
1496
|
+
instances.push({
|
1460
1497
|
rigidBody,
|
1461
1498
|
api
|
1462
1499
|
});
|
@@ -1464,21 +1501,27 @@ const InstancedRigidBodies = /*#__PURE__*/forwardRef((props, ref) => {
|
|
1464
1501
|
}
|
1465
1502
|
});
|
1466
1503
|
return () => {
|
1467
|
-
|
1504
|
+
instances.forEach(rb => {
|
1468
1505
|
world.removeRigidBody(rb.rigidBody);
|
1469
1506
|
rigidBodyStates.delete(rb.rigidBody.handle);
|
1470
1507
|
});
|
1471
|
-
|
1508
|
+
rigidBodyRefs.current = [];
|
1509
|
+
instancesRef.current = [];
|
1472
1510
|
};
|
1473
1511
|
}, []);
|
1474
1512
|
const api = useMemo(() => createInstancedRigidBodiesApi(instancesRefGetter), []);
|
1475
1513
|
useImperativeHandle(ref, () => api);
|
1476
|
-
|
1477
|
-
|
1514
|
+
useUpdateRigidBodyOptions(rigidBodyRefs, mergedOptions, rigidBodyStates, false);
|
1515
|
+
useRigidBodyEvents(rigidBodyRefs, mergedOptions, rigidBodyEvents);
|
1516
|
+
const contextValue = useMemo(() => {
|
1517
|
+
return {
|
1478
1518
|
ref: object,
|
1479
1519
|
api,
|
1480
|
-
options:
|
1481
|
-
}
|
1520
|
+
options: mergedOptions
|
1521
|
+
};
|
1522
|
+
}, [api, mergedOptions]);
|
1523
|
+
return /*#__PURE__*/React.createElement(RigidBodyContext.Provider, {
|
1524
|
+
value: contextValue
|
1482
1525
|
}, /*#__PURE__*/React.createElement("object3D", {
|
1483
1526
|
ref: object
|
1484
1527
|
}, props.children, childColliderProps.map((colliderProps, index) => /*#__PURE__*/React.createElement(AnyCollider, _extends({
|
package/package.json
CHANGED
package/readme.md
CHANGED
@@ -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
|