@react-three/rapier 0.6.2 → 0.6.5
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 -2
- package/dist/declarations/src/Physics.d.ts +14 -6
- package/dist/declarations/src/RigidBody.d.ts +12 -4
- package/dist/declarations/src/api.d.ts +16 -0
- package/dist/declarations/src/types.d.ts +6 -2
- package/dist/declarations/src/utils.d.ts +11 -2
- package/dist/react-three-rapier.cjs.dev.js +154 -50
- package/dist/react-three-rapier.cjs.prod.js +154 -50
- package/dist/react-three-rapier.esm.js +155 -51
- package/package.json +1 -1
- package/readme.md +10 -0
@@ -2,10 +2,9 @@ import React from "react";
|
|
2
2
|
import { InstancedRigidBodyApi } from "./api";
|
3
3
|
import { RigidBodyProps } from "./RigidBody";
|
4
4
|
import { Vector3Array } from "./types";
|
5
|
-
interface InstancedRigidBodiesProps extends Omit<RigidBodyProps, "position" | "rotation" | "onCollisionEnter" | "onCollisionExit"> {
|
5
|
+
export interface InstancedRigidBodiesProps extends Omit<RigidBodyProps, "position" | "rotation" | "onCollisionEnter" | "onCollisionExit"> {
|
6
6
|
positions?: Vector3Array[];
|
7
7
|
rotations?: Vector3Array[];
|
8
8
|
scales?: Vector3Array[];
|
9
9
|
}
|
10
10
|
export declare const InstancedRigidBodies: React.ForwardRefExoticComponent<InstancedRigidBodiesProps & React.RefAttributes<InstancedRigidBodyApi>>;
|
11
|
-
export {};
|
@@ -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,14 +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.
|
60
|
+
*
|
61
|
+
* @defaultValue 10
|
62
|
+
*/
|
63
|
+
maxSubSteps?: number;
|
64
|
+
/**
|
65
|
+
* Pause the physics simulation
|
58
66
|
*
|
59
|
-
* @defaultValue
|
67
|
+
* @defaultValue false
|
60
68
|
*/
|
61
|
-
|
69
|
+
paused?: boolean;
|
62
70
|
}
|
63
71
|
export declare const Physics: FC<RapierWorldProps>;
|
64
72
|
export {};
|
@@ -1,12 +1,20 @@
|
|
1
|
-
import React, { MutableRefObject } from "react";
|
1
|
+
import React, { MutableRefObject, RefObject } from "react";
|
2
2
|
import { ReactNode } from "react";
|
3
3
|
import { Object3D } from "three";
|
4
|
+
import { InstancedRigidBodyApi } from "./api";
|
5
|
+
import { InstancedRigidBodiesProps } from "./InstancedRigidBodies";
|
4
6
|
import { RigidBodyApi, UseRigidBodyOptions } from "./types";
|
7
|
+
export declare const RigidBodyContext: React.Context<{
|
8
|
+
ref: RefObject<Object3D> | MutableRefObject<Object3D>;
|
9
|
+
api: RigidBodyApi | InstancedRigidBodyApi;
|
10
|
+
hasCollisionEvents: boolean;
|
11
|
+
options: UseRigidBodyOptions | InstancedRigidBodiesProps;
|
12
|
+
}>;
|
5
13
|
export declare const useRigidBodyContext: () => {
|
6
|
-
ref: MutableRefObject<Object3D>;
|
7
|
-
api: RigidBodyApi;
|
14
|
+
ref: RefObject<Object3D> | MutableRefObject<Object3D>;
|
15
|
+
api: RigidBodyApi | InstancedRigidBodyApi;
|
8
16
|
hasCollisionEvents: boolean;
|
9
|
-
options: UseRigidBodyOptions;
|
17
|
+
options: UseRigidBodyOptions | InstancedRigidBodiesProps;
|
10
18
|
};
|
11
19
|
export interface RigidBodyProps extends UseRigidBodyOptions {
|
12
20
|
children?: ReactNode;
|
@@ -75,6 +75,22 @@ export interface RigidBodyApi {
|
|
75
75
|
* Sets the angular velocity of this rigid-body.
|
76
76
|
*/
|
77
77
|
setAngvel(velocity: Vector3Object): void;
|
78
|
+
/**
|
79
|
+
* The linear damping of this rigid-body.
|
80
|
+
*/
|
81
|
+
linearDamping(): number;
|
82
|
+
/**
|
83
|
+
* Sets the linear damping factor applied to this rigid-body.
|
84
|
+
*/
|
85
|
+
setLinearDamping(factor: number): void;
|
86
|
+
/**
|
87
|
+
* The angular damping of this rigid-body.
|
88
|
+
*/
|
89
|
+
angularDamping(): number;
|
90
|
+
/**
|
91
|
+
* Sets the anugular damping factor applied to this rigid-body.
|
92
|
+
*/
|
93
|
+
setAngularDamping(factor: number): void;
|
78
94
|
/**
|
79
95
|
* If this rigid body is kinematic, sets its future rotation after the next timestep integration.
|
80
96
|
*
|
@@ -1,8 +1,8 @@
|
|
1
1
|
import { MutableRefObject } from "react";
|
2
|
-
import { CoefficientCombineRule,
|
2
|
+
import { CoefficientCombineRule, Collider as RapierCollider, RigidBody as RapierRigidBody, TempContactManifold } from "@dimforge/rapier3d-compat";
|
3
3
|
import { createColliderApi, createJointApi, createRigidBodyApi, createWorldApi } from "./api";
|
4
|
-
export { RapierRigidBody, RapierCollider };
|
5
4
|
export { CoefficientCombineRule as CoefficientCombineRule } from "@dimforge/rapier3d-compat";
|
5
|
+
export { RapierRigidBody, RapierCollider };
|
6
6
|
export declare type RefGetter<T> = MutableRefObject<() => T | undefined>;
|
7
7
|
export declare type RigidBodyAutoCollider = "ball" | "cuboid" | "hull" | "trimesh" | false;
|
8
8
|
export interface UseRigidBodyAPI {
|
@@ -126,6 +126,10 @@ export interface UseRigidBodyOptions {
|
|
126
126
|
* default: true
|
127
127
|
*/
|
128
128
|
canSleep?: boolean;
|
129
|
+
/** The linear damping coefficient of this rigid-body.*/
|
130
|
+
linearDamping?: number;
|
131
|
+
/** The angular damping coefficient of this rigid-body.*/
|
132
|
+
angularDamping?: number;
|
129
133
|
/** The linear velocity of this body.
|
130
134
|
* default: zero velocity
|
131
135
|
*/
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { Collider, ColliderDesc, RigidBody, RigidBodyDesc, Vector3 as RapierVector3
|
1
|
+
import { Collider, ColliderDesc, Quaternion as RapierQuaternion, RigidBody, RigidBodyDesc, Vector3 as RapierVector3 } from "@dimforge/rapier3d-compat";
|
2
2
|
import { BufferGeometry, Matrix4, Object3D, Quaternion, Vector3 } from "three";
|
3
3
|
import { RigidBodyApi, RigidBodyAutoCollider, RigidBodyShape, RigidBodyTypeString, UseColliderOptions, UseRigidBodyOptions, Vector3Array, WorldApi } from "./types";
|
4
4
|
export declare const vectorArrayToVector3: (arr: Vector3Array) => Vector3;
|
@@ -26,7 +26,16 @@ interface CreateColliderFromOptions {
|
|
26
26
|
}): Collider;
|
27
27
|
}
|
28
28
|
export declare const createColliderFromOptions: CreateColliderFromOptions;
|
29
|
-
|
29
|
+
interface CreateCollidersFromChildren {
|
30
|
+
(options: {
|
31
|
+
object: Object3D;
|
32
|
+
rigidBody?: Pick<RigidBodyApi | RigidBody, "handle">;
|
33
|
+
options: UseRigidBodyOptions;
|
34
|
+
world: WorldApi;
|
35
|
+
ignoreMeshColliders: boolean;
|
36
|
+
}): Collider[];
|
37
|
+
}
|
38
|
+
export declare const createCollidersFromChildren: CreateCollidersFromChildren;
|
30
39
|
export declare const colliderDescFromGeometry: (geometry: BufferGeometry, colliders: RigidBodyAutoCollider, scale: Vector3, hasCollisionEvents: boolean) => ColliderDesc;
|
31
40
|
export declare const scaleVertices: (vertices: ArrayLike<number>, scale: Vector3) => number[];
|
32
41
|
export declare const rigidBodyDescFromOptions: (options: UseRigidBodyOptions) => RigidBodyDesc;
|
@@ -154,13 +154,19 @@ const isChildOfMeshCollider = child => {
|
|
154
154
|
return flag;
|
155
155
|
};
|
156
156
|
|
157
|
-
const createCollidersFromChildren = (
|
157
|
+
const createCollidersFromChildren = ({
|
158
|
+
object,
|
159
|
+
rigidBody,
|
160
|
+
options,
|
161
|
+
world,
|
162
|
+
ignoreMeshColliders: _ignoreMeshColliders = true
|
163
|
+
}) => {
|
158
164
|
const hasCollisionEvents = !!(options.onCollisionEnter || options.onCollisionExit);
|
159
165
|
const colliders = [];
|
160
166
|
new three.Vector3();
|
161
|
-
object.
|
167
|
+
object.traverseVisible(child => {
|
162
168
|
if ("isMesh" in child) {
|
163
|
-
if (
|
169
|
+
if (_ignoreMeshColliders && isChildOfMeshCollider(child)) return;
|
164
170
|
const {
|
165
171
|
geometry
|
166
172
|
} = child;
|
@@ -201,7 +207,7 @@ const createCollidersFromChildren = (object, rigidBody, options, world, ignoreMe
|
|
201
207
|
z: rz,
|
202
208
|
w: rw
|
203
209
|
});
|
204
|
-
const actualRigidBody = world.getRigidBody(rigidBody
|
210
|
+
const actualRigidBody = rigidBody ? world.getRigidBody(rigidBody.handle) : undefined;
|
205
211
|
const collider = world.createCollider(desc, actualRigidBody);
|
206
212
|
colliders.push(collider);
|
207
213
|
}
|
@@ -267,11 +273,13 @@ const scaleVertices = (vertices, scale) => {
|
|
267
273
|
return scaledVerts;
|
268
274
|
};
|
269
275
|
const rigidBodyDescFromOptions = options => {
|
270
|
-
var _options$linearVeloci, _options$angularVeloc, _options$gravityScale, _options$canSleep, _options$ccd, _options$enabledRotat, _options$enabledTrans;
|
276
|
+
var _options$linearVeloci, _options$angularVeloc, _options$angularDampi, _options$linearDampin, _options$gravityScale, _options$canSleep, _options$ccd, _options$enabledRotat, _options$enabledTrans;
|
271
277
|
|
272
278
|
const type = rigidBodyTypeFromString((options === null || options === void 0 ? void 0 : options.type) || "dynamic");
|
273
279
|
const [lvx, lvy, lvz] = (_options$linearVeloci = options === null || options === void 0 ? void 0 : options.linearVelocity) !== null && _options$linearVeloci !== void 0 ? _options$linearVeloci : [0, 0, 0];
|
274
280
|
const [avx, avy, avz] = (_options$angularVeloc = options === null || options === void 0 ? void 0 : options.angularVelocity) !== null && _options$angularVeloc !== void 0 ? _options$angularVeloc : [0, 0, 0];
|
281
|
+
const angularDamping = (_options$angularDampi = options === null || options === void 0 ? void 0 : options.angularDamping) !== null && _options$angularDampi !== void 0 ? _options$angularDampi : 0;
|
282
|
+
const linearDamping = (_options$linearDampin = options === null || options === void 0 ? void 0 : options.linearDamping) !== null && _options$linearDampin !== void 0 ? _options$linearDampin : 0;
|
275
283
|
const gravityScale = (_options$gravityScale = options === null || options === void 0 ? void 0 : options.gravityScale) !== null && _options$gravityScale !== void 0 ? _options$gravityScale : 1;
|
276
284
|
const canSleep = (_options$canSleep = options === null || options === void 0 ? void 0 : options.canSleep) !== null && _options$canSleep !== void 0 ? _options$canSleep : true;
|
277
285
|
const ccdEnabled = (_options$ccd = options === null || options === void 0 ? void 0 : options.ccd) !== null && _options$ccd !== void 0 ? _options$ccd : false;
|
@@ -281,7 +289,7 @@ const rigidBodyDescFromOptions = options => {
|
|
281
289
|
x: avx,
|
282
290
|
y: avy,
|
283
291
|
z: avz
|
284
|
-
}).setGravityScale(gravityScale).setCanSleep(canSleep).setCcdEnabled(ccdEnabled).enabledRotations(erx, ery, erz).enabledTranslations(etx, ety, etz);
|
292
|
+
}).setLinearDamping(linearDamping).setAngularDamping(angularDamping).setGravityScale(gravityScale).setCanSleep(canSleep).setCcdEnabled(ccdEnabled).enabledRotations(erx, ery, erz).enabledTranslations(etx, ety, etz);
|
285
293
|
if (options.lockRotations) desc.lockRotations();
|
286
294
|
if (options.lockTranslations) desc.lockTranslations();
|
287
295
|
return desc;
|
@@ -366,6 +374,18 @@ const createRigidBodyApi = ref => {
|
|
366
374
|
},
|
367
375
|
|
368
376
|
setAngvel: velocity => ref.current().setAngvel(velocity, true),
|
377
|
+
|
378
|
+
linearDamping() {
|
379
|
+
return ref.current().linearDamping();
|
380
|
+
},
|
381
|
+
|
382
|
+
setLinearDamping: factor => ref.current().setLinearDamping(factor),
|
383
|
+
|
384
|
+
angularDamping() {
|
385
|
+
return ref.current().angularDamping();
|
386
|
+
},
|
387
|
+
|
388
|
+
setAngularDamping: factor => ref.current().setAngularDamping(factor),
|
369
389
|
setNextKinematicRotation: ({
|
370
390
|
x,
|
371
391
|
y,
|
@@ -449,9 +469,15 @@ const Physics = ({
|
|
449
469
|
colliders: _colliders = "cuboid",
|
450
470
|
gravity: _gravity = [0, -9.81, 0],
|
451
471
|
children,
|
452
|
-
timeStep: _timeStep =
|
472
|
+
timeStep: _timeStep = 1 / 60,
|
473
|
+
maxSubSteps: _maxSubSteps = 10,
|
474
|
+
paused: _paused = false
|
453
475
|
}) => {
|
454
476
|
const rapier = useAsset.useAsset(importRapier);
|
477
|
+
const [isPaused, setIsPaused] = React.useState(_paused);
|
478
|
+
React.useEffect(() => {
|
479
|
+
setIsPaused(_paused);
|
480
|
+
}, [_paused]);
|
455
481
|
const worldRef = React.useRef();
|
456
482
|
const getWorldRef = React.useRef(() => {
|
457
483
|
if (!worldRef.current) {
|
@@ -471,7 +497,6 @@ const Physics = ({
|
|
471
497
|
return () => {
|
472
498
|
if (world) {
|
473
499
|
world.free();
|
474
|
-
worldRef.current = undefined;
|
475
500
|
}
|
476
501
|
};
|
477
502
|
}, []); // Update gravity
|
@@ -483,22 +508,46 @@ const Physics = ({
|
|
483
508
|
world.gravity = vectorArrayToVector3(_gravity);
|
484
509
|
}
|
485
510
|
}, [_gravity]);
|
486
|
-
const
|
487
|
-
|
511
|
+
const [steppingState] = React.useState({
|
512
|
+
time: 0,
|
513
|
+
lastTime: 0,
|
514
|
+
accumulator: 0
|
515
|
+
});
|
516
|
+
fiber.useFrame((_, delta) => {
|
488
517
|
const world = worldRef.current;
|
489
|
-
if (!world) return;
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
518
|
+
if (!world) return;
|
519
|
+
world.timestep = _timeStep;
|
520
|
+
/**
|
521
|
+
* Fixed timeStep simulation progression
|
522
|
+
* @see https://gafferongames.com/post/fix_your_timestep/
|
523
|
+
*/
|
524
|
+
|
525
|
+
let previousTranslations = {}; // don't step time forwards if paused
|
526
|
+
|
527
|
+
const nowTime = steppingState.time += _paused ? 0 : delta * 1000;
|
528
|
+
const timeStepMs = _timeStep * 1000;
|
529
|
+
const timeSinceLast = nowTime - steppingState.lastTime;
|
530
|
+
steppingState.lastTime = nowTime;
|
531
|
+
steppingState.accumulator += timeSinceLast;
|
532
|
+
|
533
|
+
if (!_paused) {
|
534
|
+
let subSteps = 0;
|
535
|
+
|
536
|
+
while (steppingState.accumulator >= timeStepMs && subSteps < _maxSubSteps) {
|
537
|
+
// Collect previous state
|
538
|
+
world.bodies.forEach(b => {
|
539
|
+
previousTranslations[b.handle] = {
|
540
|
+
rotation: b.rotation(),
|
541
|
+
translation: b.translation()
|
542
|
+
};
|
543
|
+
});
|
544
|
+
world.step(eventQueue);
|
545
|
+
subSteps++;
|
546
|
+
steppingState.accumulator -= timeStepMs;
|
547
|
+
}
|
499
548
|
}
|
500
549
|
|
501
|
-
|
550
|
+
const interpolationAlpha = steppingState.accumulator % timeStepMs / timeStepMs; // Update meshes
|
502
551
|
|
503
552
|
rigidBodyStates.forEach((state, handle) => {
|
504
553
|
const rigidBody = world.getRigidBody(handle);
|
@@ -524,7 +573,12 @@ const Physics = ({
|
|
524
573
|
return;
|
525
574
|
}
|
526
575
|
|
527
|
-
|
576
|
+
let oldState = previousTranslations[rigidBody.handle];
|
577
|
+
let newTranslation = rapierVector3ToVector3(rigidBody.translation());
|
578
|
+
let newRotation = rapierQuaternionToQuaternion(rigidBody.rotation());
|
579
|
+
let interpolatedTranslation = oldState ? rapierVector3ToVector3(oldState.translation).lerp(newTranslation, interpolationAlpha) : newTranslation;
|
580
|
+
let interpolatedRotation = oldState ? rapierQuaternionToQuaternion(oldState.rotation).slerp(newRotation, interpolationAlpha) : newRotation;
|
581
|
+
state.setMatrix(_matrix4.compose(interpolatedTranslation, interpolatedRotation, state.worldScale).premultiply(state.invertedMatrixWorld));
|
528
582
|
|
529
583
|
if (state.mesh instanceof three.InstancedMesh) {
|
530
584
|
state.mesh.instanceMatrix.needsUpdate = true;
|
@@ -574,7 +628,6 @@ const Physics = ({
|
|
574
628
|
});
|
575
629
|
}
|
576
630
|
});
|
577
|
-
time.current = now;
|
578
631
|
});
|
579
632
|
const api = React.useMemo(() => createWorldApi(getWorldRef), []);
|
580
633
|
const context = React.useMemo(() => ({
|
@@ -586,8 +639,9 @@ const Physics = ({
|
|
586
639
|
},
|
587
640
|
colliderMeshes,
|
588
641
|
rigidBodyStates,
|
589
|
-
rigidBodyEvents
|
590
|
-
|
642
|
+
rigidBodyEvents,
|
643
|
+
isPaused
|
644
|
+
}), [isPaused]);
|
591
645
|
return /*#__PURE__*/React__default["default"].createElement(RapierContext.Provider, {
|
592
646
|
value: context
|
593
647
|
}, children);
|
@@ -730,9 +784,15 @@ const useRigidBody = (options = {}) => {
|
|
730
784
|
rigidBody.resetForces(false);
|
731
785
|
rigidBody.resetTorques(false);
|
732
786
|
const colliderSetting = (_ref = (_options$colliders = options === null || options === void 0 ? void 0 : options.colliders) !== null && _options$colliders !== void 0 ? _options$colliders : physicsOptions.colliders) !== null && _ref !== void 0 ? _ref : false;
|
733
|
-
const autoColliders = colliderSetting !== false ? createCollidersFromChildren(
|
734
|
-
|
735
|
-
|
787
|
+
const autoColliders = colliderSetting !== false ? createCollidersFromChildren({
|
788
|
+
object: ref.current,
|
789
|
+
rigidBody,
|
790
|
+
options: _objectSpread2(_objectSpread2({}, options), {}, {
|
791
|
+
colliders: colliderSetting
|
792
|
+
}),
|
793
|
+
world,
|
794
|
+
ignoreMeshColliders: true
|
795
|
+
}) : [];
|
736
796
|
rigidBodyStates.set(rigidBody.handle, {
|
737
797
|
mesh: ref.current,
|
738
798
|
invertedMatrixWorld: ref.current.parent.matrixWorld.clone().invert(),
|
@@ -897,9 +957,18 @@ const MeshCollider = ({
|
|
897
957
|
var _ref;
|
898
958
|
|
899
959
|
const colliderSetting = (_ref = type !== null && type !== void 0 ? type : physicsOptions.colliders) !== null && _ref !== void 0 ? _ref : false;
|
900
|
-
|
901
|
-
|
902
|
-
|
960
|
+
|
961
|
+
if ("raw" in api) {
|
962
|
+
autoColliders = createCollidersFromChildren({
|
963
|
+
object: object.current,
|
964
|
+
rigidBody: api,
|
965
|
+
options: _objectSpread2(_objectSpread2({}, options), {}, {
|
966
|
+
colliders: colliderSetting
|
967
|
+
}),
|
968
|
+
world,
|
969
|
+
ignoreMeshColliders: false
|
970
|
+
});
|
971
|
+
}
|
903
972
|
}
|
904
973
|
|
905
974
|
return () => {
|
@@ -1046,7 +1115,7 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1046
1115
|
|
1047
1116
|
return instancesRef.current;
|
1048
1117
|
});
|
1049
|
-
React.
|
1118
|
+
React.useLayoutEffect(() => {
|
1050
1119
|
const colliders = [];
|
1051
1120
|
const rigidBodies = instancesRefGetter.current();
|
1052
1121
|
|
@@ -1072,15 +1141,21 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1072
1141
|
scale.multiply(s);
|
1073
1142
|
}
|
1074
1143
|
|
1075
|
-
const colliderDesc = colliderDescFromGeometry(mesh.geometry, props.colliders || physicsOptions.colliders, scale, false // Collisions currently not enabled for instances
|
1076
|
-
);
|
1077
1144
|
const rigidBody = world.createRigidBody(rigidBodyDesc);
|
1078
1145
|
const matrix = new three.Matrix4();
|
1079
1146
|
mesh.getMatrixAt(index, matrix);
|
1080
1147
|
const {
|
1081
1148
|
position,
|
1082
1149
|
rotation
|
1083
|
-
} = decomposeMatrix4(matrix);
|
1150
|
+
} = decomposeMatrix4(matrix);
|
1151
|
+
|
1152
|
+
if (props.colliders !== false) {
|
1153
|
+
const colliderDesc = colliderDescFromGeometry(mesh.geometry, props.colliders !== undefined ? props.colliders : physicsOptions.colliders, scale, false // Collisions currently not enabled for instances
|
1154
|
+
);
|
1155
|
+
const collider = world.createCollider(colliderDesc, rigidBody);
|
1156
|
+
colliders.push(collider);
|
1157
|
+
} // Set positions
|
1158
|
+
|
1084
1159
|
|
1085
1160
|
if (props.positions && props.positions[index]) {
|
1086
1161
|
rigidBody.setTranslation(vectorArrayToVector3(props.positions[index]), true);
|
@@ -1096,7 +1171,6 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1096
1171
|
rigidBody.setRotation(rotation, true);
|
1097
1172
|
}
|
1098
1173
|
|
1099
|
-
const collider = world.createCollider(colliderDesc, rigidBody);
|
1100
1174
|
rigidBodyStates.set(rigidBody.handle, {
|
1101
1175
|
mesh: mesh,
|
1102
1176
|
isSleeping: false,
|
@@ -1117,7 +1191,6 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1117
1191
|
}
|
1118
1192
|
|
1119
1193
|
});
|
1120
|
-
colliders.push(collider);
|
1121
1194
|
rigidBodies.push({
|
1122
1195
|
rigidBody,
|
1123
1196
|
api
|
@@ -1136,10 +1209,19 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1136
1209
|
};
|
1137
1210
|
}
|
1138
1211
|
}, []);
|
1139
|
-
React.
|
1140
|
-
|
1212
|
+
const api = React.useMemo(() => createInstancedRigidBodiesApi(instancesRefGetter), []);
|
1213
|
+
React.useImperativeHandle(ref, () => api); // console.log(api);
|
1214
|
+
|
1215
|
+
return /*#__PURE__*/React__default["default"].createElement(RigidBodyContext.Provider, {
|
1216
|
+
value: {
|
1217
|
+
ref: object,
|
1218
|
+
api,
|
1219
|
+
hasCollisionEvents: false,
|
1220
|
+
options: props
|
1221
|
+
}
|
1222
|
+
}, /*#__PURE__*/React__default["default"].createElement("object3D", {
|
1141
1223
|
ref: object
|
1142
|
-
}, props.children);
|
1224
|
+
}, props.children));
|
1143
1225
|
});
|
1144
1226
|
|
1145
1227
|
function _extends() {
|
@@ -1174,18 +1256,40 @@ const AnyCollider = _ref => {
|
|
1174
1256
|
const rigidBodyContext = useRigidBodyContext();
|
1175
1257
|
const ref = React.useRef(null);
|
1176
1258
|
React.useEffect(() => {
|
1177
|
-
var _rigidBodyContext$api;
|
1178
|
-
|
1179
1259
|
const scale = ref.current.getWorldScale(new three.Vector3());
|
1180
|
-
const
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1260
|
+
const colliders = []; // If this is an InstancedRigidBody api
|
1261
|
+
|
1262
|
+
if (rigidBodyContext && "at" in rigidBodyContext.api) {
|
1263
|
+
rigidBodyContext.api.forEach((body, index) => {
|
1264
|
+
var _rigidBodyContext$opt, _rigidBodyContext$opt2;
|
1265
|
+
|
1266
|
+
let instanceScale = scale.clone();
|
1267
|
+
|
1268
|
+
if ("scales" in rigidBodyContext.options && rigidBodyContext !== null && rigidBodyContext !== void 0 && (_rigidBodyContext$opt = rigidBodyContext.options) !== null && _rigidBodyContext$opt !== void 0 && (_rigidBodyContext$opt2 = _rigidBodyContext$opt.scales) !== null && _rigidBodyContext$opt2 !== void 0 && _rigidBodyContext$opt2[index]) {
|
1269
|
+
instanceScale.multiply(vectorArrayToVector3(rigidBodyContext.options.scales[index]));
|
1270
|
+
}
|
1271
|
+
|
1272
|
+
colliders.push(createColliderFromOptions({
|
1273
|
+
options: props,
|
1274
|
+
world,
|
1275
|
+
rigidBody: body.raw(),
|
1276
|
+
scale: instanceScale,
|
1277
|
+
hasCollisionEvents: rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.hasCollisionEvents
|
1278
|
+
}));
|
1279
|
+
});
|
1280
|
+
} else {
|
1281
|
+
colliders.push(createColliderFromOptions({
|
1282
|
+
options: props,
|
1283
|
+
world,
|
1284
|
+
// Initiate with a rigidbody, or undefined, because colliders can exist without a rigid body
|
1285
|
+
rigidBody: rigidBodyContext && "raw" in rigidBodyContext.api ? rigidBodyContext.api.raw() : undefined,
|
1286
|
+
scale,
|
1287
|
+
hasCollisionEvents: rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.hasCollisionEvents
|
1288
|
+
}));
|
1289
|
+
}
|
1290
|
+
|
1187
1291
|
return () => {
|
1188
|
-
world.removeCollider(collider);
|
1292
|
+
colliders.forEach(collider => world.removeCollider(collider));
|
1189
1293
|
};
|
1190
1294
|
}, []);
|
1191
1295
|
return /*#__PURE__*/React__default["default"].createElement("object3D", {
|
@@ -154,13 +154,19 @@ const isChildOfMeshCollider = child => {
|
|
154
154
|
return flag;
|
155
155
|
};
|
156
156
|
|
157
|
-
const createCollidersFromChildren = (
|
157
|
+
const createCollidersFromChildren = ({
|
158
|
+
object,
|
159
|
+
rigidBody,
|
160
|
+
options,
|
161
|
+
world,
|
162
|
+
ignoreMeshColliders: _ignoreMeshColliders = true
|
163
|
+
}) => {
|
158
164
|
const hasCollisionEvents = !!(options.onCollisionEnter || options.onCollisionExit);
|
159
165
|
const colliders = [];
|
160
166
|
new three.Vector3();
|
161
|
-
object.
|
167
|
+
object.traverseVisible(child => {
|
162
168
|
if ("isMesh" in child) {
|
163
|
-
if (
|
169
|
+
if (_ignoreMeshColliders && isChildOfMeshCollider(child)) return;
|
164
170
|
const {
|
165
171
|
geometry
|
166
172
|
} = child;
|
@@ -201,7 +207,7 @@ const createCollidersFromChildren = (object, rigidBody, options, world, ignoreMe
|
|
201
207
|
z: rz,
|
202
208
|
w: rw
|
203
209
|
});
|
204
|
-
const actualRigidBody = world.getRigidBody(rigidBody
|
210
|
+
const actualRigidBody = rigidBody ? world.getRigidBody(rigidBody.handle) : undefined;
|
205
211
|
const collider = world.createCollider(desc, actualRigidBody);
|
206
212
|
colliders.push(collider);
|
207
213
|
}
|
@@ -267,11 +273,13 @@ const scaleVertices = (vertices, scale) => {
|
|
267
273
|
return scaledVerts;
|
268
274
|
};
|
269
275
|
const rigidBodyDescFromOptions = options => {
|
270
|
-
var _options$linearVeloci, _options$angularVeloc, _options$gravityScale, _options$canSleep, _options$ccd, _options$enabledRotat, _options$enabledTrans;
|
276
|
+
var _options$linearVeloci, _options$angularVeloc, _options$angularDampi, _options$linearDampin, _options$gravityScale, _options$canSleep, _options$ccd, _options$enabledRotat, _options$enabledTrans;
|
271
277
|
|
272
278
|
const type = rigidBodyTypeFromString((options === null || options === void 0 ? void 0 : options.type) || "dynamic");
|
273
279
|
const [lvx, lvy, lvz] = (_options$linearVeloci = options === null || options === void 0 ? void 0 : options.linearVelocity) !== null && _options$linearVeloci !== void 0 ? _options$linearVeloci : [0, 0, 0];
|
274
280
|
const [avx, avy, avz] = (_options$angularVeloc = options === null || options === void 0 ? void 0 : options.angularVelocity) !== null && _options$angularVeloc !== void 0 ? _options$angularVeloc : [0, 0, 0];
|
281
|
+
const angularDamping = (_options$angularDampi = options === null || options === void 0 ? void 0 : options.angularDamping) !== null && _options$angularDampi !== void 0 ? _options$angularDampi : 0;
|
282
|
+
const linearDamping = (_options$linearDampin = options === null || options === void 0 ? void 0 : options.linearDamping) !== null && _options$linearDampin !== void 0 ? _options$linearDampin : 0;
|
275
283
|
const gravityScale = (_options$gravityScale = options === null || options === void 0 ? void 0 : options.gravityScale) !== null && _options$gravityScale !== void 0 ? _options$gravityScale : 1;
|
276
284
|
const canSleep = (_options$canSleep = options === null || options === void 0 ? void 0 : options.canSleep) !== null && _options$canSleep !== void 0 ? _options$canSleep : true;
|
277
285
|
const ccdEnabled = (_options$ccd = options === null || options === void 0 ? void 0 : options.ccd) !== null && _options$ccd !== void 0 ? _options$ccd : false;
|
@@ -281,7 +289,7 @@ const rigidBodyDescFromOptions = options => {
|
|
281
289
|
x: avx,
|
282
290
|
y: avy,
|
283
291
|
z: avz
|
284
|
-
}).setGravityScale(gravityScale).setCanSleep(canSleep).setCcdEnabled(ccdEnabled).enabledRotations(erx, ery, erz).enabledTranslations(etx, ety, etz);
|
292
|
+
}).setLinearDamping(linearDamping).setAngularDamping(angularDamping).setGravityScale(gravityScale).setCanSleep(canSleep).setCcdEnabled(ccdEnabled).enabledRotations(erx, ery, erz).enabledTranslations(etx, ety, etz);
|
285
293
|
if (options.lockRotations) desc.lockRotations();
|
286
294
|
if (options.lockTranslations) desc.lockTranslations();
|
287
295
|
return desc;
|
@@ -366,6 +374,18 @@ const createRigidBodyApi = ref => {
|
|
366
374
|
},
|
367
375
|
|
368
376
|
setAngvel: velocity => ref.current().setAngvel(velocity, true),
|
377
|
+
|
378
|
+
linearDamping() {
|
379
|
+
return ref.current().linearDamping();
|
380
|
+
},
|
381
|
+
|
382
|
+
setLinearDamping: factor => ref.current().setLinearDamping(factor),
|
383
|
+
|
384
|
+
angularDamping() {
|
385
|
+
return ref.current().angularDamping();
|
386
|
+
},
|
387
|
+
|
388
|
+
setAngularDamping: factor => ref.current().setAngularDamping(factor),
|
369
389
|
setNextKinematicRotation: ({
|
370
390
|
x,
|
371
391
|
y,
|
@@ -449,9 +469,15 @@ const Physics = ({
|
|
449
469
|
colliders: _colliders = "cuboid",
|
450
470
|
gravity: _gravity = [0, -9.81, 0],
|
451
471
|
children,
|
452
|
-
timeStep: _timeStep =
|
472
|
+
timeStep: _timeStep = 1 / 60,
|
473
|
+
maxSubSteps: _maxSubSteps = 10,
|
474
|
+
paused: _paused = false
|
453
475
|
}) => {
|
454
476
|
const rapier = useAsset.useAsset(importRapier);
|
477
|
+
const [isPaused, setIsPaused] = React.useState(_paused);
|
478
|
+
React.useEffect(() => {
|
479
|
+
setIsPaused(_paused);
|
480
|
+
}, [_paused]);
|
455
481
|
const worldRef = React.useRef();
|
456
482
|
const getWorldRef = React.useRef(() => {
|
457
483
|
if (!worldRef.current) {
|
@@ -471,7 +497,6 @@ const Physics = ({
|
|
471
497
|
return () => {
|
472
498
|
if (world) {
|
473
499
|
world.free();
|
474
|
-
worldRef.current = undefined;
|
475
500
|
}
|
476
501
|
};
|
477
502
|
}, []); // Update gravity
|
@@ -483,22 +508,46 @@ const Physics = ({
|
|
483
508
|
world.gravity = vectorArrayToVector3(_gravity);
|
484
509
|
}
|
485
510
|
}, [_gravity]);
|
486
|
-
const
|
487
|
-
|
511
|
+
const [steppingState] = React.useState({
|
512
|
+
time: 0,
|
513
|
+
lastTime: 0,
|
514
|
+
accumulator: 0
|
515
|
+
});
|
516
|
+
fiber.useFrame((_, delta) => {
|
488
517
|
const world = worldRef.current;
|
489
|
-
if (!world) return;
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
518
|
+
if (!world) return;
|
519
|
+
world.timestep = _timeStep;
|
520
|
+
/**
|
521
|
+
* Fixed timeStep simulation progression
|
522
|
+
* @see https://gafferongames.com/post/fix_your_timestep/
|
523
|
+
*/
|
524
|
+
|
525
|
+
let previousTranslations = {}; // don't step time forwards if paused
|
526
|
+
|
527
|
+
const nowTime = steppingState.time += _paused ? 0 : delta * 1000;
|
528
|
+
const timeStepMs = _timeStep * 1000;
|
529
|
+
const timeSinceLast = nowTime - steppingState.lastTime;
|
530
|
+
steppingState.lastTime = nowTime;
|
531
|
+
steppingState.accumulator += timeSinceLast;
|
532
|
+
|
533
|
+
if (!_paused) {
|
534
|
+
let subSteps = 0;
|
535
|
+
|
536
|
+
while (steppingState.accumulator >= timeStepMs && subSteps < _maxSubSteps) {
|
537
|
+
// Collect previous state
|
538
|
+
world.bodies.forEach(b => {
|
539
|
+
previousTranslations[b.handle] = {
|
540
|
+
rotation: b.rotation(),
|
541
|
+
translation: b.translation()
|
542
|
+
};
|
543
|
+
});
|
544
|
+
world.step(eventQueue);
|
545
|
+
subSteps++;
|
546
|
+
steppingState.accumulator -= timeStepMs;
|
547
|
+
}
|
499
548
|
}
|
500
549
|
|
501
|
-
|
550
|
+
const interpolationAlpha = steppingState.accumulator % timeStepMs / timeStepMs; // Update meshes
|
502
551
|
|
503
552
|
rigidBodyStates.forEach((state, handle) => {
|
504
553
|
const rigidBody = world.getRigidBody(handle);
|
@@ -524,7 +573,12 @@ const Physics = ({
|
|
524
573
|
return;
|
525
574
|
}
|
526
575
|
|
527
|
-
|
576
|
+
let oldState = previousTranslations[rigidBody.handle];
|
577
|
+
let newTranslation = rapierVector3ToVector3(rigidBody.translation());
|
578
|
+
let newRotation = rapierQuaternionToQuaternion(rigidBody.rotation());
|
579
|
+
let interpolatedTranslation = oldState ? rapierVector3ToVector3(oldState.translation).lerp(newTranslation, interpolationAlpha) : newTranslation;
|
580
|
+
let interpolatedRotation = oldState ? rapierQuaternionToQuaternion(oldState.rotation).slerp(newRotation, interpolationAlpha) : newRotation;
|
581
|
+
state.setMatrix(_matrix4.compose(interpolatedTranslation, interpolatedRotation, state.worldScale).premultiply(state.invertedMatrixWorld));
|
528
582
|
|
529
583
|
if (state.mesh instanceof three.InstancedMesh) {
|
530
584
|
state.mesh.instanceMatrix.needsUpdate = true;
|
@@ -574,7 +628,6 @@ const Physics = ({
|
|
574
628
|
});
|
575
629
|
}
|
576
630
|
});
|
577
|
-
time.current = now;
|
578
631
|
});
|
579
632
|
const api = React.useMemo(() => createWorldApi(getWorldRef), []);
|
580
633
|
const context = React.useMemo(() => ({
|
@@ -586,8 +639,9 @@ const Physics = ({
|
|
586
639
|
},
|
587
640
|
colliderMeshes,
|
588
641
|
rigidBodyStates,
|
589
|
-
rigidBodyEvents
|
590
|
-
|
642
|
+
rigidBodyEvents,
|
643
|
+
isPaused
|
644
|
+
}), [isPaused]);
|
591
645
|
return /*#__PURE__*/React__default["default"].createElement(RapierContext.Provider, {
|
592
646
|
value: context
|
593
647
|
}, children);
|
@@ -730,9 +784,15 @@ const useRigidBody = (options = {}) => {
|
|
730
784
|
rigidBody.resetForces(false);
|
731
785
|
rigidBody.resetTorques(false);
|
732
786
|
const colliderSetting = (_ref = (_options$colliders = options === null || options === void 0 ? void 0 : options.colliders) !== null && _options$colliders !== void 0 ? _options$colliders : physicsOptions.colliders) !== null && _ref !== void 0 ? _ref : false;
|
733
|
-
const autoColliders = colliderSetting !== false ? createCollidersFromChildren(
|
734
|
-
|
735
|
-
|
787
|
+
const autoColliders = colliderSetting !== false ? createCollidersFromChildren({
|
788
|
+
object: ref.current,
|
789
|
+
rigidBody,
|
790
|
+
options: _objectSpread2(_objectSpread2({}, options), {}, {
|
791
|
+
colliders: colliderSetting
|
792
|
+
}),
|
793
|
+
world,
|
794
|
+
ignoreMeshColliders: true
|
795
|
+
}) : [];
|
736
796
|
rigidBodyStates.set(rigidBody.handle, {
|
737
797
|
mesh: ref.current,
|
738
798
|
invertedMatrixWorld: ref.current.parent.matrixWorld.clone().invert(),
|
@@ -897,9 +957,18 @@ const MeshCollider = ({
|
|
897
957
|
var _ref;
|
898
958
|
|
899
959
|
const colliderSetting = (_ref = type !== null && type !== void 0 ? type : physicsOptions.colliders) !== null && _ref !== void 0 ? _ref : false;
|
900
|
-
|
901
|
-
|
902
|
-
|
960
|
+
|
961
|
+
if ("raw" in api) {
|
962
|
+
autoColliders = createCollidersFromChildren({
|
963
|
+
object: object.current,
|
964
|
+
rigidBody: api,
|
965
|
+
options: _objectSpread2(_objectSpread2({}, options), {}, {
|
966
|
+
colliders: colliderSetting
|
967
|
+
}),
|
968
|
+
world,
|
969
|
+
ignoreMeshColliders: false
|
970
|
+
});
|
971
|
+
}
|
903
972
|
}
|
904
973
|
|
905
974
|
return () => {
|
@@ -1046,7 +1115,7 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1046
1115
|
|
1047
1116
|
return instancesRef.current;
|
1048
1117
|
});
|
1049
|
-
React.
|
1118
|
+
React.useLayoutEffect(() => {
|
1050
1119
|
const colliders = [];
|
1051
1120
|
const rigidBodies = instancesRefGetter.current();
|
1052
1121
|
|
@@ -1072,15 +1141,21 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1072
1141
|
scale.multiply(s);
|
1073
1142
|
}
|
1074
1143
|
|
1075
|
-
const colliderDesc = colliderDescFromGeometry(mesh.geometry, props.colliders || physicsOptions.colliders, scale, false // Collisions currently not enabled for instances
|
1076
|
-
);
|
1077
1144
|
const rigidBody = world.createRigidBody(rigidBodyDesc);
|
1078
1145
|
const matrix = new three.Matrix4();
|
1079
1146
|
mesh.getMatrixAt(index, matrix);
|
1080
1147
|
const {
|
1081
1148
|
position,
|
1082
1149
|
rotation
|
1083
|
-
} = decomposeMatrix4(matrix);
|
1150
|
+
} = decomposeMatrix4(matrix);
|
1151
|
+
|
1152
|
+
if (props.colliders !== false) {
|
1153
|
+
const colliderDesc = colliderDescFromGeometry(mesh.geometry, props.colliders !== undefined ? props.colliders : physicsOptions.colliders, scale, false // Collisions currently not enabled for instances
|
1154
|
+
);
|
1155
|
+
const collider = world.createCollider(colliderDesc, rigidBody);
|
1156
|
+
colliders.push(collider);
|
1157
|
+
} // Set positions
|
1158
|
+
|
1084
1159
|
|
1085
1160
|
if (props.positions && props.positions[index]) {
|
1086
1161
|
rigidBody.setTranslation(vectorArrayToVector3(props.positions[index]), true);
|
@@ -1096,7 +1171,6 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1096
1171
|
rigidBody.setRotation(rotation, true);
|
1097
1172
|
}
|
1098
1173
|
|
1099
|
-
const collider = world.createCollider(colliderDesc, rigidBody);
|
1100
1174
|
rigidBodyStates.set(rigidBody.handle, {
|
1101
1175
|
mesh: mesh,
|
1102
1176
|
isSleeping: false,
|
@@ -1117,7 +1191,6 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1117
1191
|
}
|
1118
1192
|
|
1119
1193
|
});
|
1120
|
-
colliders.push(collider);
|
1121
1194
|
rigidBodies.push({
|
1122
1195
|
rigidBody,
|
1123
1196
|
api
|
@@ -1136,10 +1209,19 @@ const InstancedRigidBodies = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
1136
1209
|
};
|
1137
1210
|
}
|
1138
1211
|
}, []);
|
1139
|
-
React.
|
1140
|
-
|
1212
|
+
const api = React.useMemo(() => createInstancedRigidBodiesApi(instancesRefGetter), []);
|
1213
|
+
React.useImperativeHandle(ref, () => api); // console.log(api);
|
1214
|
+
|
1215
|
+
return /*#__PURE__*/React__default["default"].createElement(RigidBodyContext.Provider, {
|
1216
|
+
value: {
|
1217
|
+
ref: object,
|
1218
|
+
api,
|
1219
|
+
hasCollisionEvents: false,
|
1220
|
+
options: props
|
1221
|
+
}
|
1222
|
+
}, /*#__PURE__*/React__default["default"].createElement("object3D", {
|
1141
1223
|
ref: object
|
1142
|
-
}, props.children);
|
1224
|
+
}, props.children));
|
1143
1225
|
});
|
1144
1226
|
|
1145
1227
|
function _extends() {
|
@@ -1174,18 +1256,40 @@ const AnyCollider = _ref => {
|
|
1174
1256
|
const rigidBodyContext = useRigidBodyContext();
|
1175
1257
|
const ref = React.useRef(null);
|
1176
1258
|
React.useEffect(() => {
|
1177
|
-
var _rigidBodyContext$api;
|
1178
|
-
|
1179
1259
|
const scale = ref.current.getWorldScale(new three.Vector3());
|
1180
|
-
const
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1260
|
+
const colliders = []; // If this is an InstancedRigidBody api
|
1261
|
+
|
1262
|
+
if (rigidBodyContext && "at" in rigidBodyContext.api) {
|
1263
|
+
rigidBodyContext.api.forEach((body, index) => {
|
1264
|
+
var _rigidBodyContext$opt, _rigidBodyContext$opt2;
|
1265
|
+
|
1266
|
+
let instanceScale = scale.clone();
|
1267
|
+
|
1268
|
+
if ("scales" in rigidBodyContext.options && rigidBodyContext !== null && rigidBodyContext !== void 0 && (_rigidBodyContext$opt = rigidBodyContext.options) !== null && _rigidBodyContext$opt !== void 0 && (_rigidBodyContext$opt2 = _rigidBodyContext$opt.scales) !== null && _rigidBodyContext$opt2 !== void 0 && _rigidBodyContext$opt2[index]) {
|
1269
|
+
instanceScale.multiply(vectorArrayToVector3(rigidBodyContext.options.scales[index]));
|
1270
|
+
}
|
1271
|
+
|
1272
|
+
colliders.push(createColliderFromOptions({
|
1273
|
+
options: props,
|
1274
|
+
world,
|
1275
|
+
rigidBody: body.raw(),
|
1276
|
+
scale: instanceScale,
|
1277
|
+
hasCollisionEvents: rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.hasCollisionEvents
|
1278
|
+
}));
|
1279
|
+
});
|
1280
|
+
} else {
|
1281
|
+
colliders.push(createColliderFromOptions({
|
1282
|
+
options: props,
|
1283
|
+
world,
|
1284
|
+
// Initiate with a rigidbody, or undefined, because colliders can exist without a rigid body
|
1285
|
+
rigidBody: rigidBodyContext && "raw" in rigidBodyContext.api ? rigidBodyContext.api.raw() : undefined,
|
1286
|
+
scale,
|
1287
|
+
hasCollisionEvents: rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.hasCollisionEvents
|
1288
|
+
}));
|
1289
|
+
}
|
1290
|
+
|
1187
1291
|
return () => {
|
1188
|
-
world.removeCollider(collider);
|
1292
|
+
colliders.forEach(collider => world.removeCollider(collider));
|
1189
1293
|
};
|
1190
1294
|
}, []);
|
1191
1295
|
return /*#__PURE__*/React__default["default"].createElement("object3D", {
|
@@ -1,6 +1,6 @@
|
|
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';
|
@@ -129,13 +129,19 @@ const isChildOfMeshCollider = child => {
|
|
129
129
|
return flag;
|
130
130
|
};
|
131
131
|
|
132
|
-
const createCollidersFromChildren = (
|
132
|
+
const createCollidersFromChildren = ({
|
133
|
+
object,
|
134
|
+
rigidBody,
|
135
|
+
options,
|
136
|
+
world,
|
137
|
+
ignoreMeshColliders: _ignoreMeshColliders = true
|
138
|
+
}) => {
|
133
139
|
const hasCollisionEvents = !!(options.onCollisionEnter || options.onCollisionExit);
|
134
140
|
const colliders = [];
|
135
141
|
new Vector3();
|
136
|
-
object.
|
142
|
+
object.traverseVisible(child => {
|
137
143
|
if ("isMesh" in child) {
|
138
|
-
if (
|
144
|
+
if (_ignoreMeshColliders && isChildOfMeshCollider(child)) return;
|
139
145
|
const {
|
140
146
|
geometry
|
141
147
|
} = child;
|
@@ -176,7 +182,7 @@ const createCollidersFromChildren = (object, rigidBody, options, world, ignoreMe
|
|
176
182
|
z: rz,
|
177
183
|
w: rw
|
178
184
|
});
|
179
|
-
const actualRigidBody = world.getRigidBody(rigidBody
|
185
|
+
const actualRigidBody = rigidBody ? world.getRigidBody(rigidBody.handle) : undefined;
|
180
186
|
const collider = world.createCollider(desc, actualRigidBody);
|
181
187
|
colliders.push(collider);
|
182
188
|
}
|
@@ -242,11 +248,13 @@ const scaleVertices = (vertices, scale) => {
|
|
242
248
|
return scaledVerts;
|
243
249
|
};
|
244
250
|
const rigidBodyDescFromOptions = options => {
|
245
|
-
var _options$linearVeloci, _options$angularVeloc, _options$gravityScale, _options$canSleep, _options$ccd, _options$enabledRotat, _options$enabledTrans;
|
251
|
+
var _options$linearVeloci, _options$angularVeloc, _options$angularDampi, _options$linearDampin, _options$gravityScale, _options$canSleep, _options$ccd, _options$enabledRotat, _options$enabledTrans;
|
246
252
|
|
247
253
|
const type = rigidBodyTypeFromString((options === null || options === void 0 ? void 0 : options.type) || "dynamic");
|
248
254
|
const [lvx, lvy, lvz] = (_options$linearVeloci = options === null || options === void 0 ? void 0 : options.linearVelocity) !== null && _options$linearVeloci !== void 0 ? _options$linearVeloci : [0, 0, 0];
|
249
255
|
const [avx, avy, avz] = (_options$angularVeloc = options === null || options === void 0 ? void 0 : options.angularVelocity) !== null && _options$angularVeloc !== void 0 ? _options$angularVeloc : [0, 0, 0];
|
256
|
+
const angularDamping = (_options$angularDampi = options === null || options === void 0 ? void 0 : options.angularDamping) !== null && _options$angularDampi !== void 0 ? _options$angularDampi : 0;
|
257
|
+
const linearDamping = (_options$linearDampin = options === null || options === void 0 ? void 0 : options.linearDamping) !== null && _options$linearDampin !== void 0 ? _options$linearDampin : 0;
|
250
258
|
const gravityScale = (_options$gravityScale = options === null || options === void 0 ? void 0 : options.gravityScale) !== null && _options$gravityScale !== void 0 ? _options$gravityScale : 1;
|
251
259
|
const canSleep = (_options$canSleep = options === null || options === void 0 ? void 0 : options.canSleep) !== null && _options$canSleep !== void 0 ? _options$canSleep : true;
|
252
260
|
const ccdEnabled = (_options$ccd = options === null || options === void 0 ? void 0 : options.ccd) !== null && _options$ccd !== void 0 ? _options$ccd : false;
|
@@ -256,7 +264,7 @@ const rigidBodyDescFromOptions = options => {
|
|
256
264
|
x: avx,
|
257
265
|
y: avy,
|
258
266
|
z: avz
|
259
|
-
}).setGravityScale(gravityScale).setCanSleep(canSleep).setCcdEnabled(ccdEnabled).enabledRotations(erx, ery, erz).enabledTranslations(etx, ety, etz);
|
267
|
+
}).setLinearDamping(linearDamping).setAngularDamping(angularDamping).setGravityScale(gravityScale).setCanSleep(canSleep).setCcdEnabled(ccdEnabled).enabledRotations(erx, ery, erz).enabledTranslations(etx, ety, etz);
|
260
268
|
if (options.lockRotations) desc.lockRotations();
|
261
269
|
if (options.lockTranslations) desc.lockTranslations();
|
262
270
|
return desc;
|
@@ -341,6 +349,18 @@ const createRigidBodyApi = ref => {
|
|
341
349
|
},
|
342
350
|
|
343
351
|
setAngvel: velocity => ref.current().setAngvel(velocity, true),
|
352
|
+
|
353
|
+
linearDamping() {
|
354
|
+
return ref.current().linearDamping();
|
355
|
+
},
|
356
|
+
|
357
|
+
setLinearDamping: factor => ref.current().setLinearDamping(factor),
|
358
|
+
|
359
|
+
angularDamping() {
|
360
|
+
return ref.current().angularDamping();
|
361
|
+
},
|
362
|
+
|
363
|
+
setAngularDamping: factor => ref.current().setAngularDamping(factor),
|
344
364
|
setNextKinematicRotation: ({
|
345
365
|
x,
|
346
366
|
y,
|
@@ -424,9 +444,15 @@ const Physics = ({
|
|
424
444
|
colliders: _colliders = "cuboid",
|
425
445
|
gravity: _gravity = [0, -9.81, 0],
|
426
446
|
children,
|
427
|
-
timeStep: _timeStep =
|
447
|
+
timeStep: _timeStep = 1 / 60,
|
448
|
+
maxSubSteps: _maxSubSteps = 10,
|
449
|
+
paused: _paused = false
|
428
450
|
}) => {
|
429
451
|
const rapier = useAsset(importRapier);
|
452
|
+
const [isPaused, setIsPaused] = useState(_paused);
|
453
|
+
useEffect(() => {
|
454
|
+
setIsPaused(_paused);
|
455
|
+
}, [_paused]);
|
430
456
|
const worldRef = useRef();
|
431
457
|
const getWorldRef = useRef(() => {
|
432
458
|
if (!worldRef.current) {
|
@@ -446,7 +472,6 @@ const Physics = ({
|
|
446
472
|
return () => {
|
447
473
|
if (world) {
|
448
474
|
world.free();
|
449
|
-
worldRef.current = undefined;
|
450
475
|
}
|
451
476
|
};
|
452
477
|
}, []); // Update gravity
|
@@ -458,22 +483,46 @@ const Physics = ({
|
|
458
483
|
world.gravity = vectorArrayToVector3(_gravity);
|
459
484
|
}
|
460
485
|
}, [_gravity]);
|
461
|
-
const
|
462
|
-
|
486
|
+
const [steppingState] = useState({
|
487
|
+
time: 0,
|
488
|
+
lastTime: 0,
|
489
|
+
accumulator: 0
|
490
|
+
});
|
491
|
+
useFrame((_, delta) => {
|
463
492
|
const world = worldRef.current;
|
464
|
-
if (!world) return;
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
493
|
+
if (!world) return;
|
494
|
+
world.timestep = _timeStep;
|
495
|
+
/**
|
496
|
+
* Fixed timeStep simulation progression
|
497
|
+
* @see https://gafferongames.com/post/fix_your_timestep/
|
498
|
+
*/
|
499
|
+
|
500
|
+
let previousTranslations = {}; // don't step time forwards if paused
|
501
|
+
|
502
|
+
const nowTime = steppingState.time += _paused ? 0 : delta * 1000;
|
503
|
+
const timeStepMs = _timeStep * 1000;
|
504
|
+
const timeSinceLast = nowTime - steppingState.lastTime;
|
505
|
+
steppingState.lastTime = nowTime;
|
506
|
+
steppingState.accumulator += timeSinceLast;
|
507
|
+
|
508
|
+
if (!_paused) {
|
509
|
+
let subSteps = 0;
|
510
|
+
|
511
|
+
while (steppingState.accumulator >= timeStepMs && subSteps < _maxSubSteps) {
|
512
|
+
// Collect previous state
|
513
|
+
world.bodies.forEach(b => {
|
514
|
+
previousTranslations[b.handle] = {
|
515
|
+
rotation: b.rotation(),
|
516
|
+
translation: b.translation()
|
517
|
+
};
|
518
|
+
});
|
519
|
+
world.step(eventQueue);
|
520
|
+
subSteps++;
|
521
|
+
steppingState.accumulator -= timeStepMs;
|
522
|
+
}
|
474
523
|
}
|
475
524
|
|
476
|
-
|
525
|
+
const interpolationAlpha = steppingState.accumulator % timeStepMs / timeStepMs; // Update meshes
|
477
526
|
|
478
527
|
rigidBodyStates.forEach((state, handle) => {
|
479
528
|
const rigidBody = world.getRigidBody(handle);
|
@@ -499,7 +548,12 @@ const Physics = ({
|
|
499
548
|
return;
|
500
549
|
}
|
501
550
|
|
502
|
-
|
551
|
+
let oldState = previousTranslations[rigidBody.handle];
|
552
|
+
let newTranslation = rapierVector3ToVector3(rigidBody.translation());
|
553
|
+
let newRotation = rapierQuaternionToQuaternion(rigidBody.rotation());
|
554
|
+
let interpolatedTranslation = oldState ? rapierVector3ToVector3(oldState.translation).lerp(newTranslation, interpolationAlpha) : newTranslation;
|
555
|
+
let interpolatedRotation = oldState ? rapierQuaternionToQuaternion(oldState.rotation).slerp(newRotation, interpolationAlpha) : newRotation;
|
556
|
+
state.setMatrix(_matrix4.compose(interpolatedTranslation, interpolatedRotation, state.worldScale).premultiply(state.invertedMatrixWorld));
|
503
557
|
|
504
558
|
if (state.mesh instanceof InstancedMesh) {
|
505
559
|
state.mesh.instanceMatrix.needsUpdate = true;
|
@@ -549,7 +603,6 @@ const Physics = ({
|
|
549
603
|
});
|
550
604
|
}
|
551
605
|
});
|
552
|
-
time.current = now;
|
553
606
|
});
|
554
607
|
const api = useMemo(() => createWorldApi(getWorldRef), []);
|
555
608
|
const context = useMemo(() => ({
|
@@ -561,8 +614,9 @@ const Physics = ({
|
|
561
614
|
},
|
562
615
|
colliderMeshes,
|
563
616
|
rigidBodyStates,
|
564
|
-
rigidBodyEvents
|
565
|
-
|
617
|
+
rigidBodyEvents,
|
618
|
+
isPaused
|
619
|
+
}), [isPaused]);
|
566
620
|
return /*#__PURE__*/React.createElement(RapierContext.Provider, {
|
567
621
|
value: context
|
568
622
|
}, children);
|
@@ -705,9 +759,15 @@ const useRigidBody = (options = {}) => {
|
|
705
759
|
rigidBody.resetForces(false);
|
706
760
|
rigidBody.resetTorques(false);
|
707
761
|
const colliderSetting = (_ref = (_options$colliders = options === null || options === void 0 ? void 0 : options.colliders) !== null && _options$colliders !== void 0 ? _options$colliders : physicsOptions.colliders) !== null && _ref !== void 0 ? _ref : false;
|
708
|
-
const autoColliders = colliderSetting !== false ? createCollidersFromChildren(
|
709
|
-
|
710
|
-
|
762
|
+
const autoColliders = colliderSetting !== false ? createCollidersFromChildren({
|
763
|
+
object: ref.current,
|
764
|
+
rigidBody,
|
765
|
+
options: _objectSpread2(_objectSpread2({}, options), {}, {
|
766
|
+
colliders: colliderSetting
|
767
|
+
}),
|
768
|
+
world,
|
769
|
+
ignoreMeshColliders: true
|
770
|
+
}) : [];
|
711
771
|
rigidBodyStates.set(rigidBody.handle, {
|
712
772
|
mesh: ref.current,
|
713
773
|
invertedMatrixWorld: ref.current.parent.matrixWorld.clone().invert(),
|
@@ -872,9 +932,18 @@ const MeshCollider = ({
|
|
872
932
|
var _ref;
|
873
933
|
|
874
934
|
const colliderSetting = (_ref = type !== null && type !== void 0 ? type : physicsOptions.colliders) !== null && _ref !== void 0 ? _ref : false;
|
875
|
-
|
876
|
-
|
877
|
-
|
935
|
+
|
936
|
+
if ("raw" in api) {
|
937
|
+
autoColliders = createCollidersFromChildren({
|
938
|
+
object: object.current,
|
939
|
+
rigidBody: api,
|
940
|
+
options: _objectSpread2(_objectSpread2({}, options), {}, {
|
941
|
+
colliders: colliderSetting
|
942
|
+
}),
|
943
|
+
world,
|
944
|
+
ignoreMeshColliders: false
|
945
|
+
});
|
946
|
+
}
|
878
947
|
}
|
879
948
|
|
880
949
|
return () => {
|
@@ -1021,7 +1090,7 @@ const InstancedRigidBodies = /*#__PURE__*/forwardRef((props, ref) => {
|
|
1021
1090
|
|
1022
1091
|
return instancesRef.current;
|
1023
1092
|
});
|
1024
|
-
|
1093
|
+
useLayoutEffect(() => {
|
1025
1094
|
const colliders = [];
|
1026
1095
|
const rigidBodies = instancesRefGetter.current();
|
1027
1096
|
|
@@ -1047,15 +1116,21 @@ const InstancedRigidBodies = /*#__PURE__*/forwardRef((props, ref) => {
|
|
1047
1116
|
scale.multiply(s);
|
1048
1117
|
}
|
1049
1118
|
|
1050
|
-
const colliderDesc = colliderDescFromGeometry(mesh.geometry, props.colliders || physicsOptions.colliders, scale, false // Collisions currently not enabled for instances
|
1051
|
-
);
|
1052
1119
|
const rigidBody = world.createRigidBody(rigidBodyDesc);
|
1053
1120
|
const matrix = new Matrix4();
|
1054
1121
|
mesh.getMatrixAt(index, matrix);
|
1055
1122
|
const {
|
1056
1123
|
position,
|
1057
1124
|
rotation
|
1058
|
-
} = decomposeMatrix4(matrix);
|
1125
|
+
} = decomposeMatrix4(matrix);
|
1126
|
+
|
1127
|
+
if (props.colliders !== false) {
|
1128
|
+
const colliderDesc = colliderDescFromGeometry(mesh.geometry, props.colliders !== undefined ? props.colliders : physicsOptions.colliders, scale, false // Collisions currently not enabled for instances
|
1129
|
+
);
|
1130
|
+
const collider = world.createCollider(colliderDesc, rigidBody);
|
1131
|
+
colliders.push(collider);
|
1132
|
+
} // Set positions
|
1133
|
+
|
1059
1134
|
|
1060
1135
|
if (props.positions && props.positions[index]) {
|
1061
1136
|
rigidBody.setTranslation(vectorArrayToVector3(props.positions[index]), true);
|
@@ -1071,7 +1146,6 @@ const InstancedRigidBodies = /*#__PURE__*/forwardRef((props, ref) => {
|
|
1071
1146
|
rigidBody.setRotation(rotation, true);
|
1072
1147
|
}
|
1073
1148
|
|
1074
|
-
const collider = world.createCollider(colliderDesc, rigidBody);
|
1075
1149
|
rigidBodyStates.set(rigidBody.handle, {
|
1076
1150
|
mesh: mesh,
|
1077
1151
|
isSleeping: false,
|
@@ -1092,7 +1166,6 @@ const InstancedRigidBodies = /*#__PURE__*/forwardRef((props, ref) => {
|
|
1092
1166
|
}
|
1093
1167
|
|
1094
1168
|
});
|
1095
|
-
colliders.push(collider);
|
1096
1169
|
rigidBodies.push({
|
1097
1170
|
rigidBody,
|
1098
1171
|
api
|
@@ -1111,10 +1184,19 @@ const InstancedRigidBodies = /*#__PURE__*/forwardRef((props, ref) => {
|
|
1111
1184
|
};
|
1112
1185
|
}
|
1113
1186
|
}, []);
|
1114
|
-
|
1115
|
-
|
1187
|
+
const api = useMemo(() => createInstancedRigidBodiesApi(instancesRefGetter), []);
|
1188
|
+
useImperativeHandle(ref, () => api); // console.log(api);
|
1189
|
+
|
1190
|
+
return /*#__PURE__*/React.createElement(RigidBodyContext.Provider, {
|
1191
|
+
value: {
|
1192
|
+
ref: object,
|
1193
|
+
api,
|
1194
|
+
hasCollisionEvents: false,
|
1195
|
+
options: props
|
1196
|
+
}
|
1197
|
+
}, /*#__PURE__*/React.createElement("object3D", {
|
1116
1198
|
ref: object
|
1117
|
-
}, props.children);
|
1199
|
+
}, props.children));
|
1118
1200
|
});
|
1119
1201
|
|
1120
1202
|
function _extends() {
|
@@ -1149,18 +1231,40 @@ const AnyCollider = _ref => {
|
|
1149
1231
|
const rigidBodyContext = useRigidBodyContext();
|
1150
1232
|
const ref = useRef(null);
|
1151
1233
|
useEffect(() => {
|
1152
|
-
var _rigidBodyContext$api;
|
1153
|
-
|
1154
1234
|
const scale = ref.current.getWorldScale(new Vector3());
|
1155
|
-
const
|
1156
|
-
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1235
|
+
const colliders = []; // If this is an InstancedRigidBody api
|
1236
|
+
|
1237
|
+
if (rigidBodyContext && "at" in rigidBodyContext.api) {
|
1238
|
+
rigidBodyContext.api.forEach((body, index) => {
|
1239
|
+
var _rigidBodyContext$opt, _rigidBodyContext$opt2;
|
1240
|
+
|
1241
|
+
let instanceScale = scale.clone();
|
1242
|
+
|
1243
|
+
if ("scales" in rigidBodyContext.options && rigidBodyContext !== null && rigidBodyContext !== void 0 && (_rigidBodyContext$opt = rigidBodyContext.options) !== null && _rigidBodyContext$opt !== void 0 && (_rigidBodyContext$opt2 = _rigidBodyContext$opt.scales) !== null && _rigidBodyContext$opt2 !== void 0 && _rigidBodyContext$opt2[index]) {
|
1244
|
+
instanceScale.multiply(vectorArrayToVector3(rigidBodyContext.options.scales[index]));
|
1245
|
+
}
|
1246
|
+
|
1247
|
+
colliders.push(createColliderFromOptions({
|
1248
|
+
options: props,
|
1249
|
+
world,
|
1250
|
+
rigidBody: body.raw(),
|
1251
|
+
scale: instanceScale,
|
1252
|
+
hasCollisionEvents: rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.hasCollisionEvents
|
1253
|
+
}));
|
1254
|
+
});
|
1255
|
+
} else {
|
1256
|
+
colliders.push(createColliderFromOptions({
|
1257
|
+
options: props,
|
1258
|
+
world,
|
1259
|
+
// Initiate with a rigidbody, or undefined, because colliders can exist without a rigid body
|
1260
|
+
rigidBody: rigidBodyContext && "raw" in rigidBodyContext.api ? rigidBodyContext.api.raw() : undefined,
|
1261
|
+
scale,
|
1262
|
+
hasCollisionEvents: rigidBodyContext === null || rigidBodyContext === void 0 ? void 0 : rigidBodyContext.hasCollisionEvents
|
1263
|
+
}));
|
1264
|
+
}
|
1265
|
+
|
1162
1266
|
return () => {
|
1163
|
-
world.removeCollider(collider);
|
1267
|
+
colliders.forEach(collider => world.removeCollider(collider));
|
1164
1268
|
};
|
1165
1269
|
}, []);
|
1166
1270
|
return /*#__PURE__*/React.createElement("object3D", {
|
package/package.json
CHANGED
package/readme.md
CHANGED
@@ -2,6 +2,11 @@
|
|
2
2
|
<img src="https://raw.githubusercontent.com/pmndrs/react-three-rapier/HEAD/packages/react-three-rapier/misc/hero.svg" alt="@react-three/rapier" />
|
3
3
|
</p>
|
4
4
|
|
5
|
+
<p align="center">
|
6
|
+
<img src="https://img.shields.io/npm/v/@react-three/rapier?style=for-the-badge&colorA=0099DA&colorB=ffffff" />
|
7
|
+
<img src="https://img.shields.io/discord/740090768164651008?style=for-the-badge&colorA=0099DA&colorB=ffffff&label=discord&logo=discord&logoColor=ffffff)](https://discord.gg/ZZjjNvJ" />
|
8
|
+
</p>
|
9
|
+
|
5
10
|
<p align="center">⚠️ Under heavy development. All APIs are subject to change. ⚠️</p>
|
6
11
|
|
7
12
|
## Usage
|
@@ -121,6 +126,8 @@ Instanced meshes can also be used and have automatic colliders generated from th
|
|
121
126
|
|
122
127
|
By wrapping the `InstancedMesh` in `<InstancedRigidBodies />`, each instance will be attached to an individual `RigidBody`.
|
123
128
|
|
129
|
+
> Note: Custom colliders (compound shapes) for InstancedMesh is currently not supported
|
130
|
+
|
124
131
|
```tsx
|
125
132
|
import { InstancedRigidBodies } from "@react-three/rapier";
|
126
133
|
|
@@ -166,6 +173,8 @@ const Scene = () => {
|
|
166
173
|
<instancedMesh args={[undefined, undefined, COUNT]}>
|
167
174
|
<sphereBufferGeometry args={[0.2]} />
|
168
175
|
<meshPhysicalGeometry color="blue" />
|
176
|
+
|
177
|
+
<CuboidCollider args={[0.1, 0.2, 0.1]} />
|
169
178
|
</instancedMesh>
|
170
179
|
</InstancedRigidBodies>
|
171
180
|
);
|
@@ -242,6 +251,7 @@ In order, but also not necessarily:
|
|
242
251
|
- [x] Collision events
|
243
252
|
- [x] Colliders outside RigidBodies
|
244
253
|
- [x] InstancedMesh support
|
254
|
+
- [x] Timestep improvements for determinism
|
245
255
|
- [ ] Normalize and improve collision events (add events to single Colliders, InstancedRigidBodies, etc)
|
246
256
|
- [ ] Docs
|
247
257
|
- [ ] CodeSandbox examples
|