@react-three/rapier 0.6.3 → 0.6.6
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/InstancedRigidBodies.d.ts +1 -1
- package/dist/declarations/src/Physics.d.ts +9 -7
- package/dist/declarations/src/RigidBody.d.ts +3 -2
- package/dist/react-three-rapier.cjs.dev.js +81 -41
- package/dist/react-three-rapier.cjs.prod.js +81 -41
- package/dist/react-three-rapier.esm.js +82 -42
- package/package.json +1 -1
- package/readme.md +1 -0
@@ -7,4 +7,4 @@ export interface InstancedRigidBodiesProps extends Omit<RigidBodyProps, "positio
|
|
7
7
|
rotations?: Vector3Array[];
|
8
8
|
scales?: Vector3Array[];
|
9
9
|
}
|
10
|
-
export declare const InstancedRigidBodies: React.ForwardRefExoticComponent<InstancedRigidBodiesProps & React.RefAttributes<InstancedRigidBodyApi>>;
|
10
|
+
export declare const InstancedRigidBodies: React.ForwardRefExoticComponent<Pick<InstancedRigidBodiesProps, "type" | "canSleep" | "linearDamping" | "angularDamping" | "linearVelocity" | "angularVelocity" | "gravityScale" | "ccd" | "colliders" | "friction" | "restitution" | "onSleep" | "onWake" | "lockRotations" | "lockTranslations" | "enabledRotations" | "enabledTranslations" | "children" | "quaternion" | "attach" | "args" | "key" | "onUpdate" | "up" | "scale" | "matrix" | "layers" | "dispose" | "id" | "uuid" | "name" | "parent" | "modelViewMatrix" | "normalMatrix" | "matrixWorld" | "matrixAutoUpdate" | "matrixWorldNeedsUpdate" | "visible" | "castShadow" | "receiveShadow" | "frustumCulled" | "renderOrder" | "animations" | "userData" | "customDepthMaterial" | "customDistanceMaterial" | "isObject3D" | "onBeforeRender" | "onAfterRender" | "applyMatrix4" | "applyQuaternion" | "setRotationFromAxisAngle" | "setRotationFromEuler" | "setRotationFromMatrix" | "setRotationFromQuaternion" | "rotateOnAxis" | "rotateOnWorldAxis" | "rotateX" | "rotateY" | "rotateZ" | "translateOnAxis" | "translateX" | "translateY" | "translateZ" | "localToWorld" | "worldToLocal" | "lookAt" | "add" | "remove" | "removeFromParent" | "clear" | "getObjectById" | "getObjectByName" | "getObjectByProperty" | "getWorldPosition" | "getWorldQuaternion" | "getWorldScale" | "getWorldDirection" | "raycast" | "traverse" | "traverseVisible" | "traverseAncestors" | "updateMatrix" | "updateMatrixWorld" | "updateWorldMatrix" | "toJSON" | "clone" | "copy" | "addEventListener" | "hasEventListener" | "removeEventListener" | "dispatchEvent" | "onClick" | "onContextMenu" | "onDoubleClick" | "onPointerUp" | "onPointerDown" | "onPointerOver" | "onPointerOut" | "onPointerEnter" | "onPointerLeave" | "onPointerMove" | "onPointerMissed" | "onPointerCancel" | "onWheel" | "positions" | "rotations" | "scales"> & React.RefAttributes<InstancedRigidBodyApi>>;
|
@@ -19,6 +19,7 @@ export interface RapierContext {
|
|
19
19
|
colliders: RigidBodyAutoCollider;
|
20
20
|
};
|
21
21
|
rigidBodyEvents: EventMap;
|
22
|
+
isPaused: boolean;
|
22
23
|
}
|
23
24
|
export declare const RapierContext: React.Context<RapierContext | undefined>;
|
24
25
|
declare type EventMap = Map<ColliderHandle | RigidBodyHandle, {
|
@@ -51,20 +52,21 @@ interface RapierWorldProps {
|
|
51
52
|
* Setting this to a number (eg. 1/60) will run the
|
52
53
|
* simulation at that framerate.
|
53
54
|
*
|
54
|
-
*
|
55
|
-
|
56
|
-
|
57
|
-
|
55
|
+
* @defaultValue 1/60
|
56
|
+
*/
|
57
|
+
timeStep?: number;
|
58
|
+
/**
|
59
|
+
* Maximum number of fixed steps to take per function call.
|
58
60
|
*
|
59
|
-
* @defaultValue
|
61
|
+
* @defaultValue 10
|
60
62
|
*/
|
61
|
-
|
63
|
+
maxSubSteps?: number;
|
62
64
|
/**
|
63
65
|
* Pause the physics simulation
|
64
66
|
*
|
65
67
|
* @defaultValue false
|
66
68
|
*/
|
67
|
-
paused
|
69
|
+
paused?: boolean;
|
68
70
|
}
|
69
71
|
export declare const Physics: FC<RapierWorldProps>;
|
70
72
|
export {};
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import React, { MutableRefObject, RefObject } from "react";
|
2
2
|
import { ReactNode } from "react";
|
3
3
|
import { Object3D } from "three";
|
4
|
+
import { Object3DProps } from "@react-three/fiber";
|
4
5
|
import { InstancedRigidBodyApi } from "./api";
|
5
6
|
import { InstancedRigidBodiesProps } from "./InstancedRigidBodies";
|
6
7
|
import { RigidBodyApi, UseRigidBodyOptions } from "./types";
|
@@ -16,7 +17,7 @@ export declare const useRigidBodyContext: () => {
|
|
16
17
|
hasCollisionEvents: boolean;
|
17
18
|
options: UseRigidBodyOptions | InstancedRigidBodiesProps;
|
18
19
|
};
|
19
|
-
export interface RigidBodyProps extends UseRigidBodyOptions {
|
20
|
+
export interface RigidBodyProps extends UseRigidBodyOptions, Omit<Object3DProps, 'type' | 'position' | 'rotation'> {
|
20
21
|
children?: ReactNode;
|
21
22
|
}
|
22
|
-
export declare const RigidBody: React.ForwardRefExoticComponent<RigidBodyProps & React.RefAttributes<import("./api").RigidBodyApi>>;
|
23
|
+
export declare const RigidBody: React.ForwardRefExoticComponent<Pick<RigidBodyProps, "type" | "canSleep" | "linearDamping" | "angularDamping" | "linearVelocity" | "angularVelocity" | "gravityScale" | "ccd" | "position" | "rotation" | "colliders" | "friction" | "restitution" | "onCollisionEnter" | "onCollisionExit" | "onSleep" | "onWake" | "lockRotations" | "lockTranslations" | "enabledRotations" | "enabledTranslations" | "children" | "quaternion" | "attach" | "args" | "key" | "onUpdate" | "up" | "scale" | "matrix" | "layers" | "dispose" | "id" | "uuid" | "name" | "parent" | "modelViewMatrix" | "normalMatrix" | "matrixWorld" | "matrixAutoUpdate" | "matrixWorldNeedsUpdate" | "visible" | "castShadow" | "receiveShadow" | "frustumCulled" | "renderOrder" | "animations" | "userData" | "customDepthMaterial" | "customDistanceMaterial" | "isObject3D" | "onBeforeRender" | "onAfterRender" | "applyMatrix4" | "applyQuaternion" | "setRotationFromAxisAngle" | "setRotationFromEuler" | "setRotationFromMatrix" | "setRotationFromQuaternion" | "rotateOnAxis" | "rotateOnWorldAxis" | "rotateX" | "rotateY" | "rotateZ" | "translateOnAxis" | "translateX" | "translateY" | "translateZ" | "localToWorld" | "worldToLocal" | "lookAt" | "add" | "remove" | "removeFromParent" | "clear" | "getObjectById" | "getObjectByName" | "getObjectByProperty" | "getWorldPosition" | "getWorldQuaternion" | "getWorldScale" | "getWorldDirection" | "raycast" | "traverse" | "traverseVisible" | "traverseAncestors" | "updateMatrix" | "updateMatrixWorld" | "updateWorldMatrix" | "toJSON" | "clone" | "copy" | "addEventListener" | "hasEventListener" | "removeEventListener" | "dispatchEvent" | "onClick" | "onContextMenu" | "onDoubleClick" | "onPointerUp" | "onPointerDown" | "onPointerOver" | "onPointerOut" | "onPointerEnter" | "onPointerLeave" | "onPointerMove" | "onPointerMissed" | "onPointerCancel" | "onWheel"> & React.RefAttributes<import("./api").RigidBodyApi>>;
|
@@ -7,6 +7,7 @@ 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 BufferGeometryUtils = require('three/examples/jsm/utils/BufferGeometryUtils');
|
10
11
|
|
11
12
|
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
|
12
13
|
|
@@ -244,7 +245,9 @@ const colliderDescFromGeometry = (geometry, colliders, scale, hasCollisionEvents
|
|
244
245
|
{
|
245
246
|
var _g$index;
|
246
247
|
|
247
|
-
const
|
248
|
+
const clonedGeometry = geometry.index ? geometry.clone() : BufferGeometryUtils.mergeVertices(geometry);
|
249
|
+
|
250
|
+
const _g = clonedGeometry.scale(scale.x, scale.y, scale.z);
|
248
251
|
|
249
252
|
desc = rapier3dCompat.ColliderDesc.trimesh(_g.attributes.position.array, (_g$index = _g.index) === null || _g$index === void 0 ? void 0 : _g$index.array);
|
250
253
|
}
|
@@ -469,10 +472,15 @@ const Physics = ({
|
|
469
472
|
colliders: _colliders = "cuboid",
|
470
473
|
gravity: _gravity = [0, -9.81, 0],
|
471
474
|
children,
|
472
|
-
timeStep: _timeStep =
|
475
|
+
timeStep: _timeStep = 1 / 60,
|
476
|
+
maxSubSteps: _maxSubSteps = 10,
|
473
477
|
paused: _paused = false
|
474
478
|
}) => {
|
475
479
|
const rapier = useAsset.useAsset(importRapier);
|
480
|
+
const [isPaused, setIsPaused] = React.useState(_paused);
|
481
|
+
React.useEffect(() => {
|
482
|
+
setIsPaused(_paused);
|
483
|
+
}, [_paused]);
|
476
484
|
const worldRef = React.useRef();
|
477
485
|
const getWorldRef = React.useRef(() => {
|
478
486
|
if (!worldRef.current) {
|
@@ -492,7 +500,6 @@ const Physics = ({
|
|
492
500
|
return () => {
|
493
501
|
if (world) {
|
494
502
|
world.free();
|
495
|
-
worldRef.current = undefined;
|
496
503
|
}
|
497
504
|
};
|
498
505
|
}, []); // Update gravity
|
@@ -504,22 +511,46 @@ const Physics = ({
|
|
504
511
|
world.gravity = vectorArrayToVector3(_gravity);
|
505
512
|
}
|
506
513
|
}, [_gravity]);
|
507
|
-
const
|
508
|
-
|
514
|
+
const [steppingState] = React.useState({
|
515
|
+
time: 0,
|
516
|
+
lastTime: 0,
|
517
|
+
accumulator: 0
|
518
|
+
});
|
519
|
+
fiber.useFrame((_, delta) => {
|
509
520
|
const world = worldRef.current;
|
510
|
-
if (!world) return;
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
521
|
+
if (!world) return;
|
522
|
+
world.timestep = _timeStep;
|
523
|
+
/**
|
524
|
+
* Fixed timeStep simulation progression
|
525
|
+
* @see https://gafferongames.com/post/fix_your_timestep/
|
526
|
+
*/
|
527
|
+
|
528
|
+
let previousTranslations = {}; // don't step time forwards if paused
|
529
|
+
|
530
|
+
const nowTime = steppingState.time += _paused ? 0 : delta * 1000;
|
531
|
+
const timeStepMs = _timeStep * 1000;
|
532
|
+
const timeSinceLast = nowTime - steppingState.lastTime;
|
533
|
+
steppingState.lastTime = nowTime;
|
534
|
+
steppingState.accumulator += timeSinceLast;
|
535
|
+
|
536
|
+
if (!_paused) {
|
537
|
+
let subSteps = 0;
|
538
|
+
|
539
|
+
while (steppingState.accumulator >= timeStepMs && subSteps < _maxSubSteps) {
|
540
|
+
// Collect previous state
|
541
|
+
world.bodies.forEach(b => {
|
542
|
+
previousTranslations[b.handle] = {
|
543
|
+
rotation: rapierQuaternionToQuaternion(b.rotation()).normalize(),
|
544
|
+
translation: rapierVector3ToVector3(b.translation())
|
545
|
+
};
|
546
|
+
});
|
547
|
+
world.step(eventQueue);
|
548
|
+
subSteps++;
|
549
|
+
steppingState.accumulator -= timeStepMs;
|
550
|
+
}
|
520
551
|
}
|
521
552
|
|
522
|
-
|
553
|
+
const interpolationAlpha = steppingState.accumulator % timeStepMs / timeStepMs; // Update meshes
|
523
554
|
|
524
555
|
rigidBodyStates.forEach((state, handle) => {
|
525
556
|
const rigidBody = world.getRigidBody(handle);
|
@@ -545,7 +576,12 @@ const Physics = ({
|
|
545
576
|
return;
|
546
577
|
}
|
547
578
|
|
548
|
-
|
579
|
+
let oldState = previousTranslations[rigidBody.handle];
|
580
|
+
let newTranslation = rapierVector3ToVector3(rigidBody.translation());
|
581
|
+
let newRotation = rapierQuaternionToQuaternion(rigidBody.rotation());
|
582
|
+
let interpolatedTranslation = oldState ? oldState.translation.lerp(newTranslation, 1) : newTranslation;
|
583
|
+
let interpolatedRotation = oldState ? oldState.rotation.slerp(newRotation, interpolationAlpha) : newRotation;
|
584
|
+
state.setMatrix(_matrix4.compose(interpolatedTranslation, interpolatedRotation, state.worldScale).premultiply(state.invertedMatrixWorld));
|
549
585
|
|
550
586
|
if (state.mesh instanceof three.InstancedMesh) {
|
551
587
|
state.mesh.instanceMatrix.needsUpdate = true;
|
@@ -595,7 +631,6 @@ const Physics = ({
|
|
595
631
|
});
|
596
632
|
}
|
597
633
|
});
|
598
|
-
time.current = now;
|
599
634
|
});
|
600
635
|
const api = React.useMemo(() => createWorldApi(getWorldRef), []);
|
601
636
|
const context = React.useMemo(() => ({
|
@@ -607,13 +642,32 @@ const Physics = ({
|
|
607
642
|
},
|
608
643
|
colliderMeshes,
|
609
644
|
rigidBodyStates,
|
610
|
-
rigidBodyEvents
|
611
|
-
|
645
|
+
rigidBodyEvents,
|
646
|
+
isPaused
|
647
|
+
}), [isPaused]);
|
612
648
|
return /*#__PURE__*/React__default["default"].createElement(RapierContext.Provider, {
|
613
649
|
value: context
|
614
650
|
}, children);
|
615
651
|
};
|
616
652
|
|
653
|
+
function _extends() {
|
654
|
+
_extends = Object.assign || function (target) {
|
655
|
+
for (var i = 1; i < arguments.length; i++) {
|
656
|
+
var source = arguments[i];
|
657
|
+
|
658
|
+
for (var key in source) {
|
659
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
660
|
+
target[key] = source[key];
|
661
|
+
}
|
662
|
+
}
|
663
|
+
}
|
664
|
+
|
665
|
+
return target;
|
666
|
+
};
|
667
|
+
|
668
|
+
return _extends.apply(this, arguments);
|
669
|
+
}
|
670
|
+
|
617
671
|
function _objectWithoutPropertiesLoose(source, excluded) {
|
618
672
|
if (source == null) return {};
|
619
673
|
var target = {};
|
@@ -880,7 +934,8 @@ const usePrismaticJoint = (body1, body2, [body1Anchor, body2Anchor, axis]) => {
|
|
880
934
|
return useImpulseJoint(body1, body2, rapier.JointData.prismatic(vectorArrayToVector3(body1Anchor), vectorArrayToVector3(body2Anchor), vectorArrayToVector3(axis)));
|
881
935
|
};
|
882
936
|
|
883
|
-
const _excluded$1 = ["children"]
|
937
|
+
const _excluded$1 = ["children"],
|
938
|
+
_excluded2 = ["type", "position", "rotation"];
|
884
939
|
const RigidBodyContext = /*#__PURE__*/React.createContext(undefined);
|
885
940
|
const useRigidBodyContext = () => React.useContext(RigidBodyContext); // RigidBody
|
886
941
|
|
@@ -891,6 +946,9 @@ const RigidBody = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
891
946
|
props = _objectWithoutProperties(_ref, _excluded$1);
|
892
947
|
|
893
948
|
const [object, api] = useRigidBody(props);
|
949
|
+
|
950
|
+
const objectProps = _objectWithoutProperties(props, _excluded2);
|
951
|
+
|
894
952
|
React.useImperativeHandle(ref, () => api);
|
895
953
|
return /*#__PURE__*/React__default["default"].createElement(RigidBodyContext.Provider, {
|
896
954
|
value: {
|
@@ -899,9 +957,9 @@ const RigidBody = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
899
957
|
hasCollisionEvents: !!(props.onCollisionEnter || props.onCollisionExit),
|
900
958
|
options: props
|
901
959
|
}
|
902
|
-
}, /*#__PURE__*/React__default["default"].createElement("object3D", {
|
960
|
+
}, /*#__PURE__*/React__default["default"].createElement("object3D", _extends({
|
903
961
|
ref: object
|
904
|
-
}, children));
|
962
|
+
}, objectProps), children));
|
905
963
|
});
|
906
964
|
|
907
965
|
const MeshCollider = ({
|
@@ -1191,24 +1249,6 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1191
1249
|
}, props.children));
|
1192
1250
|
});
|
1193
1251
|
|
1194
|
-
function _extends() {
|
1195
|
-
_extends = Object.assign || function (target) {
|
1196
|
-
for (var i = 1; i < arguments.length; i++) {
|
1197
|
-
var source = arguments[i];
|
1198
|
-
|
1199
|
-
for (var key in source) {
|
1200
|
-
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
1201
|
-
target[key] = source[key];
|
1202
|
-
}
|
1203
|
-
}
|
1204
|
-
}
|
1205
|
-
|
1206
|
-
return target;
|
1207
|
-
};
|
1208
|
-
|
1209
|
-
return _extends.apply(this, arguments);
|
1210
|
-
}
|
1211
|
-
|
1212
1252
|
const _excluded = ["children"];
|
1213
1253
|
|
1214
1254
|
const AnyCollider = _ref => {
|
@@ -7,6 +7,7 @@ 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 BufferGeometryUtils = require('three/examples/jsm/utils/BufferGeometryUtils');
|
10
11
|
|
11
12
|
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
|
12
13
|
|
@@ -244,7 +245,9 @@ const colliderDescFromGeometry = (geometry, colliders, scale, hasCollisionEvents
|
|
244
245
|
{
|
245
246
|
var _g$index;
|
246
247
|
|
247
|
-
const
|
248
|
+
const clonedGeometry = geometry.index ? geometry.clone() : BufferGeometryUtils.mergeVertices(geometry);
|
249
|
+
|
250
|
+
const _g = clonedGeometry.scale(scale.x, scale.y, scale.z);
|
248
251
|
|
249
252
|
desc = rapier3dCompat.ColliderDesc.trimesh(_g.attributes.position.array, (_g$index = _g.index) === null || _g$index === void 0 ? void 0 : _g$index.array);
|
250
253
|
}
|
@@ -469,10 +472,15 @@ const Physics = ({
|
|
469
472
|
colliders: _colliders = "cuboid",
|
470
473
|
gravity: _gravity = [0, -9.81, 0],
|
471
474
|
children,
|
472
|
-
timeStep: _timeStep =
|
475
|
+
timeStep: _timeStep = 1 / 60,
|
476
|
+
maxSubSteps: _maxSubSteps = 10,
|
473
477
|
paused: _paused = false
|
474
478
|
}) => {
|
475
479
|
const rapier = useAsset.useAsset(importRapier);
|
480
|
+
const [isPaused, setIsPaused] = React.useState(_paused);
|
481
|
+
React.useEffect(() => {
|
482
|
+
setIsPaused(_paused);
|
483
|
+
}, [_paused]);
|
476
484
|
const worldRef = React.useRef();
|
477
485
|
const getWorldRef = React.useRef(() => {
|
478
486
|
if (!worldRef.current) {
|
@@ -492,7 +500,6 @@ const Physics = ({
|
|
492
500
|
return () => {
|
493
501
|
if (world) {
|
494
502
|
world.free();
|
495
|
-
worldRef.current = undefined;
|
496
503
|
}
|
497
504
|
};
|
498
505
|
}, []); // Update gravity
|
@@ -504,22 +511,46 @@ const Physics = ({
|
|
504
511
|
world.gravity = vectorArrayToVector3(_gravity);
|
505
512
|
}
|
506
513
|
}, [_gravity]);
|
507
|
-
const
|
508
|
-
|
514
|
+
const [steppingState] = React.useState({
|
515
|
+
time: 0,
|
516
|
+
lastTime: 0,
|
517
|
+
accumulator: 0
|
518
|
+
});
|
519
|
+
fiber.useFrame((_, delta) => {
|
509
520
|
const world = worldRef.current;
|
510
|
-
if (!world) return;
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
521
|
+
if (!world) return;
|
522
|
+
world.timestep = _timeStep;
|
523
|
+
/**
|
524
|
+
* Fixed timeStep simulation progression
|
525
|
+
* @see https://gafferongames.com/post/fix_your_timestep/
|
526
|
+
*/
|
527
|
+
|
528
|
+
let previousTranslations = {}; // don't step time forwards if paused
|
529
|
+
|
530
|
+
const nowTime = steppingState.time += _paused ? 0 : delta * 1000;
|
531
|
+
const timeStepMs = _timeStep * 1000;
|
532
|
+
const timeSinceLast = nowTime - steppingState.lastTime;
|
533
|
+
steppingState.lastTime = nowTime;
|
534
|
+
steppingState.accumulator += timeSinceLast;
|
535
|
+
|
536
|
+
if (!_paused) {
|
537
|
+
let subSteps = 0;
|
538
|
+
|
539
|
+
while (steppingState.accumulator >= timeStepMs && subSteps < _maxSubSteps) {
|
540
|
+
// Collect previous state
|
541
|
+
world.bodies.forEach(b => {
|
542
|
+
previousTranslations[b.handle] = {
|
543
|
+
rotation: rapierQuaternionToQuaternion(b.rotation()).normalize(),
|
544
|
+
translation: rapierVector3ToVector3(b.translation())
|
545
|
+
};
|
546
|
+
});
|
547
|
+
world.step(eventQueue);
|
548
|
+
subSteps++;
|
549
|
+
steppingState.accumulator -= timeStepMs;
|
550
|
+
}
|
520
551
|
}
|
521
552
|
|
522
|
-
|
553
|
+
const interpolationAlpha = steppingState.accumulator % timeStepMs / timeStepMs; // Update meshes
|
523
554
|
|
524
555
|
rigidBodyStates.forEach((state, handle) => {
|
525
556
|
const rigidBody = world.getRigidBody(handle);
|
@@ -545,7 +576,12 @@ const Physics = ({
|
|
545
576
|
return;
|
546
577
|
}
|
547
578
|
|
548
|
-
|
579
|
+
let oldState = previousTranslations[rigidBody.handle];
|
580
|
+
let newTranslation = rapierVector3ToVector3(rigidBody.translation());
|
581
|
+
let newRotation = rapierQuaternionToQuaternion(rigidBody.rotation());
|
582
|
+
let interpolatedTranslation = oldState ? oldState.translation.lerp(newTranslation, 1) : newTranslation;
|
583
|
+
let interpolatedRotation = oldState ? oldState.rotation.slerp(newRotation, interpolationAlpha) : newRotation;
|
584
|
+
state.setMatrix(_matrix4.compose(interpolatedTranslation, interpolatedRotation, state.worldScale).premultiply(state.invertedMatrixWorld));
|
549
585
|
|
550
586
|
if (state.mesh instanceof three.InstancedMesh) {
|
551
587
|
state.mesh.instanceMatrix.needsUpdate = true;
|
@@ -595,7 +631,6 @@ const Physics = ({
|
|
595
631
|
});
|
596
632
|
}
|
597
633
|
});
|
598
|
-
time.current = now;
|
599
634
|
});
|
600
635
|
const api = React.useMemo(() => createWorldApi(getWorldRef), []);
|
601
636
|
const context = React.useMemo(() => ({
|
@@ -607,13 +642,32 @@ const Physics = ({
|
|
607
642
|
},
|
608
643
|
colliderMeshes,
|
609
644
|
rigidBodyStates,
|
610
|
-
rigidBodyEvents
|
611
|
-
|
645
|
+
rigidBodyEvents,
|
646
|
+
isPaused
|
647
|
+
}), [isPaused]);
|
612
648
|
return /*#__PURE__*/React__default["default"].createElement(RapierContext.Provider, {
|
613
649
|
value: context
|
614
650
|
}, children);
|
615
651
|
};
|
616
652
|
|
653
|
+
function _extends() {
|
654
|
+
_extends = Object.assign || function (target) {
|
655
|
+
for (var i = 1; i < arguments.length; i++) {
|
656
|
+
var source = arguments[i];
|
657
|
+
|
658
|
+
for (var key in source) {
|
659
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
660
|
+
target[key] = source[key];
|
661
|
+
}
|
662
|
+
}
|
663
|
+
}
|
664
|
+
|
665
|
+
return target;
|
666
|
+
};
|
667
|
+
|
668
|
+
return _extends.apply(this, arguments);
|
669
|
+
}
|
670
|
+
|
617
671
|
function _objectWithoutPropertiesLoose(source, excluded) {
|
618
672
|
if (source == null) return {};
|
619
673
|
var target = {};
|
@@ -880,7 +934,8 @@ const usePrismaticJoint = (body1, body2, [body1Anchor, body2Anchor, axis]) => {
|
|
880
934
|
return useImpulseJoint(body1, body2, rapier.JointData.prismatic(vectorArrayToVector3(body1Anchor), vectorArrayToVector3(body2Anchor), vectorArrayToVector3(axis)));
|
881
935
|
};
|
882
936
|
|
883
|
-
const _excluded$1 = ["children"]
|
937
|
+
const _excluded$1 = ["children"],
|
938
|
+
_excluded2 = ["type", "position", "rotation"];
|
884
939
|
const RigidBodyContext = /*#__PURE__*/React.createContext(undefined);
|
885
940
|
const useRigidBodyContext = () => React.useContext(RigidBodyContext); // RigidBody
|
886
941
|
|
@@ -891,6 +946,9 @@ const RigidBody = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
891
946
|
props = _objectWithoutProperties(_ref, _excluded$1);
|
892
947
|
|
893
948
|
const [object, api] = useRigidBody(props);
|
949
|
+
|
950
|
+
const objectProps = _objectWithoutProperties(props, _excluded2);
|
951
|
+
|
894
952
|
React.useImperativeHandle(ref, () => api);
|
895
953
|
return /*#__PURE__*/React__default["default"].createElement(RigidBodyContext.Provider, {
|
896
954
|
value: {
|
@@ -899,9 +957,9 @@ const RigidBody = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
899
957
|
hasCollisionEvents: !!(props.onCollisionEnter || props.onCollisionExit),
|
900
958
|
options: props
|
901
959
|
}
|
902
|
-
}, /*#__PURE__*/React__default["default"].createElement("object3D", {
|
960
|
+
}, /*#__PURE__*/React__default["default"].createElement("object3D", _extends({
|
903
961
|
ref: object
|
904
|
-
}, children));
|
962
|
+
}, objectProps), children));
|
905
963
|
});
|
906
964
|
|
907
965
|
const MeshCollider = ({
|
@@ -1191,24 +1249,6 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1191
1249
|
}, props.children));
|
1192
1250
|
});
|
1193
1251
|
|
1194
|
-
function _extends() {
|
1195
|
-
_extends = Object.assign || function (target) {
|
1196
|
-
for (var i = 1; i < arguments.length; i++) {
|
1197
|
-
var source = arguments[i];
|
1198
|
-
|
1199
|
-
for (var key in source) {
|
1200
|
-
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
1201
|
-
target[key] = source[key];
|
1202
|
-
}
|
1203
|
-
}
|
1204
|
-
}
|
1205
|
-
|
1206
|
-
return target;
|
1207
|
-
};
|
1208
|
-
|
1209
|
-
return _extends.apply(this, arguments);
|
1210
|
-
}
|
1211
|
-
|
1212
1252
|
const _excluded = ["children"];
|
1213
1253
|
|
1214
1254
|
const AnyCollider = _ref => {
|
@@ -1,9 +1,10 @@
|
|
1
1
|
import { ColliderDesc, ActiveEvents, RigidBodyDesc, CoefficientCombineRule, EventQueue, ShapeType } from '@dimforge/rapier3d-compat';
|
2
2
|
export { CoefficientCombineRule, Collider as RapierCollider, RigidBody as RapierRigidBody } from '@dimforge/rapier3d-compat';
|
3
|
-
import React, {
|
3
|
+
import React, { useState, useEffect, useRef, useMemo, createContext, useContext, forwardRef, useImperativeHandle, memo, useLayoutEffect } from 'react';
|
4
4
|
import { useAsset } from 'use-asset';
|
5
5
|
import { useFrame } from '@react-three/fiber';
|
6
6
|
import { Quaternion, Euler, Vector3, Object3D, Matrix4, InstancedMesh, CylinderBufferGeometry, BufferGeometry, BufferAttribute, SphereBufferGeometry, BoxBufferGeometry, DynamicDrawUsage } from 'three';
|
7
|
+
import { mergeVertices } from 'three/examples/jsm/utils/BufferGeometryUtils';
|
7
8
|
|
8
9
|
const _quaternion = new Quaternion();
|
9
10
|
const _euler = new Euler();
|
@@ -219,7 +220,9 @@ const colliderDescFromGeometry = (geometry, colliders, scale, hasCollisionEvents
|
|
219
220
|
{
|
220
221
|
var _g$index;
|
221
222
|
|
222
|
-
const
|
223
|
+
const clonedGeometry = geometry.index ? geometry.clone() : mergeVertices(geometry);
|
224
|
+
|
225
|
+
const _g = clonedGeometry.scale(scale.x, scale.y, scale.z);
|
223
226
|
|
224
227
|
desc = ColliderDesc.trimesh(_g.attributes.position.array, (_g$index = _g.index) === null || _g$index === void 0 ? void 0 : _g$index.array);
|
225
228
|
}
|
@@ -444,10 +447,15 @@ const Physics = ({
|
|
444
447
|
colliders: _colliders = "cuboid",
|
445
448
|
gravity: _gravity = [0, -9.81, 0],
|
446
449
|
children,
|
447
|
-
timeStep: _timeStep =
|
450
|
+
timeStep: _timeStep = 1 / 60,
|
451
|
+
maxSubSteps: _maxSubSteps = 10,
|
448
452
|
paused: _paused = false
|
449
453
|
}) => {
|
450
454
|
const rapier = useAsset(importRapier);
|
455
|
+
const [isPaused, setIsPaused] = useState(_paused);
|
456
|
+
useEffect(() => {
|
457
|
+
setIsPaused(_paused);
|
458
|
+
}, [_paused]);
|
451
459
|
const worldRef = useRef();
|
452
460
|
const getWorldRef = useRef(() => {
|
453
461
|
if (!worldRef.current) {
|
@@ -467,7 +475,6 @@ const Physics = ({
|
|
467
475
|
return () => {
|
468
476
|
if (world) {
|
469
477
|
world.free();
|
470
|
-
worldRef.current = undefined;
|
471
478
|
}
|
472
479
|
};
|
473
480
|
}, []); // Update gravity
|
@@ -479,22 +486,46 @@ const Physics = ({
|
|
479
486
|
world.gravity = vectorArrayToVector3(_gravity);
|
480
487
|
}
|
481
488
|
}, [_gravity]);
|
482
|
-
const
|
483
|
-
|
489
|
+
const [steppingState] = useState({
|
490
|
+
time: 0,
|
491
|
+
lastTime: 0,
|
492
|
+
accumulator: 0
|
493
|
+
});
|
494
|
+
useFrame((_, delta) => {
|
484
495
|
const world = worldRef.current;
|
485
|
-
if (!world) return;
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
496
|
+
if (!world) return;
|
497
|
+
world.timestep = _timeStep;
|
498
|
+
/**
|
499
|
+
* Fixed timeStep simulation progression
|
500
|
+
* @see https://gafferongames.com/post/fix_your_timestep/
|
501
|
+
*/
|
502
|
+
|
503
|
+
let previousTranslations = {}; // don't step time forwards if paused
|
504
|
+
|
505
|
+
const nowTime = steppingState.time += _paused ? 0 : delta * 1000;
|
506
|
+
const timeStepMs = _timeStep * 1000;
|
507
|
+
const timeSinceLast = nowTime - steppingState.lastTime;
|
508
|
+
steppingState.lastTime = nowTime;
|
509
|
+
steppingState.accumulator += timeSinceLast;
|
510
|
+
|
511
|
+
if (!_paused) {
|
512
|
+
let subSteps = 0;
|
513
|
+
|
514
|
+
while (steppingState.accumulator >= timeStepMs && subSteps < _maxSubSteps) {
|
515
|
+
// Collect previous state
|
516
|
+
world.bodies.forEach(b => {
|
517
|
+
previousTranslations[b.handle] = {
|
518
|
+
rotation: rapierQuaternionToQuaternion(b.rotation()).normalize(),
|
519
|
+
translation: rapierVector3ToVector3(b.translation())
|
520
|
+
};
|
521
|
+
});
|
522
|
+
world.step(eventQueue);
|
523
|
+
subSteps++;
|
524
|
+
steppingState.accumulator -= timeStepMs;
|
525
|
+
}
|
495
526
|
}
|
496
527
|
|
497
|
-
|
528
|
+
const interpolationAlpha = steppingState.accumulator % timeStepMs / timeStepMs; // Update meshes
|
498
529
|
|
499
530
|
rigidBodyStates.forEach((state, handle) => {
|
500
531
|
const rigidBody = world.getRigidBody(handle);
|
@@ -520,7 +551,12 @@ const Physics = ({
|
|
520
551
|
return;
|
521
552
|
}
|
522
553
|
|
523
|
-
|
554
|
+
let oldState = previousTranslations[rigidBody.handle];
|
555
|
+
let newTranslation = rapierVector3ToVector3(rigidBody.translation());
|
556
|
+
let newRotation = rapierQuaternionToQuaternion(rigidBody.rotation());
|
557
|
+
let interpolatedTranslation = oldState ? oldState.translation.lerp(newTranslation, 1) : newTranslation;
|
558
|
+
let interpolatedRotation = oldState ? oldState.rotation.slerp(newRotation, interpolationAlpha) : newRotation;
|
559
|
+
state.setMatrix(_matrix4.compose(interpolatedTranslation, interpolatedRotation, state.worldScale).premultiply(state.invertedMatrixWorld));
|
524
560
|
|
525
561
|
if (state.mesh instanceof InstancedMesh) {
|
526
562
|
state.mesh.instanceMatrix.needsUpdate = true;
|
@@ -570,7 +606,6 @@ const Physics = ({
|
|
570
606
|
});
|
571
607
|
}
|
572
608
|
});
|
573
|
-
time.current = now;
|
574
609
|
});
|
575
610
|
const api = useMemo(() => createWorldApi(getWorldRef), []);
|
576
611
|
const context = useMemo(() => ({
|
@@ -582,13 +617,32 @@ const Physics = ({
|
|
582
617
|
},
|
583
618
|
colliderMeshes,
|
584
619
|
rigidBodyStates,
|
585
|
-
rigidBodyEvents
|
586
|
-
|
620
|
+
rigidBodyEvents,
|
621
|
+
isPaused
|
622
|
+
}), [isPaused]);
|
587
623
|
return /*#__PURE__*/React.createElement(RapierContext.Provider, {
|
588
624
|
value: context
|
589
625
|
}, children);
|
590
626
|
};
|
591
627
|
|
628
|
+
function _extends() {
|
629
|
+
_extends = Object.assign || function (target) {
|
630
|
+
for (var i = 1; i < arguments.length; i++) {
|
631
|
+
var source = arguments[i];
|
632
|
+
|
633
|
+
for (var key in source) {
|
634
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
635
|
+
target[key] = source[key];
|
636
|
+
}
|
637
|
+
}
|
638
|
+
}
|
639
|
+
|
640
|
+
return target;
|
641
|
+
};
|
642
|
+
|
643
|
+
return _extends.apply(this, arguments);
|
644
|
+
}
|
645
|
+
|
592
646
|
function _objectWithoutPropertiesLoose(source, excluded) {
|
593
647
|
if (source == null) return {};
|
594
648
|
var target = {};
|
@@ -855,7 +909,8 @@ const usePrismaticJoint = (body1, body2, [body1Anchor, body2Anchor, axis]) => {
|
|
855
909
|
return useImpulseJoint(body1, body2, rapier.JointData.prismatic(vectorArrayToVector3(body1Anchor), vectorArrayToVector3(body2Anchor), vectorArrayToVector3(axis)));
|
856
910
|
};
|
857
911
|
|
858
|
-
const _excluded$1 = ["children"]
|
912
|
+
const _excluded$1 = ["children"],
|
913
|
+
_excluded2 = ["type", "position", "rotation"];
|
859
914
|
const RigidBodyContext = /*#__PURE__*/createContext(undefined);
|
860
915
|
const useRigidBodyContext = () => useContext(RigidBodyContext); // RigidBody
|
861
916
|
|
@@ -866,6 +921,9 @@ const RigidBody = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
866
921
|
props = _objectWithoutProperties(_ref, _excluded$1);
|
867
922
|
|
868
923
|
const [object, api] = useRigidBody(props);
|
924
|
+
|
925
|
+
const objectProps = _objectWithoutProperties(props, _excluded2);
|
926
|
+
|
869
927
|
useImperativeHandle(ref, () => api);
|
870
928
|
return /*#__PURE__*/React.createElement(RigidBodyContext.Provider, {
|
871
929
|
value: {
|
@@ -874,9 +932,9 @@ const RigidBody = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
874
932
|
hasCollisionEvents: !!(props.onCollisionEnter || props.onCollisionExit),
|
875
933
|
options: props
|
876
934
|
}
|
877
|
-
}, /*#__PURE__*/React.createElement("object3D", {
|
935
|
+
}, /*#__PURE__*/React.createElement("object3D", _extends({
|
878
936
|
ref: object
|
879
|
-
}, children));
|
937
|
+
}, objectProps), children));
|
880
938
|
});
|
881
939
|
|
882
940
|
const MeshCollider = ({
|
@@ -1166,24 +1224,6 @@ const InstancedRigidBodies = /*#__PURE__*/forwardRef((props, ref) => {
|
|
1166
1224
|
}, props.children));
|
1167
1225
|
});
|
1168
1226
|
|
1169
|
-
function _extends() {
|
1170
|
-
_extends = Object.assign || function (target) {
|
1171
|
-
for (var i = 1; i < arguments.length; i++) {
|
1172
|
-
var source = arguments[i];
|
1173
|
-
|
1174
|
-
for (var key in source) {
|
1175
|
-
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
1176
|
-
target[key] = source[key];
|
1177
|
-
}
|
1178
|
-
}
|
1179
|
-
}
|
1180
|
-
|
1181
|
-
return target;
|
1182
|
-
};
|
1183
|
-
|
1184
|
-
return _extends.apply(this, arguments);
|
1185
|
-
}
|
1186
|
-
|
1187
1227
|
const _excluded = ["children"];
|
1188
1228
|
|
1189
1229
|
const AnyCollider = _ref => {
|
package/package.json
CHANGED
package/readme.md
CHANGED
@@ -251,6 +251,7 @@ In order, but also not necessarily:
|
|
251
251
|
- [x] Collision events
|
252
252
|
- [x] Colliders outside RigidBodies
|
253
253
|
- [x] InstancedMesh support
|
254
|
+
- [x] Timestep improvements for determinism
|
254
255
|
- [ ] Normalize and improve collision events (add events to single Colliders, InstancedRigidBodies, etc)
|
255
256
|
- [ ] Docs
|
256
257
|
- [ ] CodeSandbox examples
|