@react-three/rapier 0.8.2 → 0.10.0

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.
@@ -1,10 +1,51 @@
1
- import { EventQueue, RigidBodyDesc, ColliderDesc, ActiveEvents } from '@dimforge/rapier3d-compat';
1
+ import { ColliderDesc, ActiveEvents, RigidBodyDesc, EventQueue } from '@dimforge/rapier3d-compat';
2
2
  export { CoefficientCombineRule, Collider as RapierCollider, RigidBody as RapierRigidBody } from '@dimforge/rapier3d-compat';
3
- import React, { useRef, useState, useEffect, useMemo, createContext, useContext, memo, forwardRef, useImperativeHandle, useLayoutEffect } from 'react';
4
- import { useAsset } from 'use-asset';
5
- import { useFrame } from '@react-three/fiber';
3
+ import { useFrame, useThree } from '@react-three/fiber';
4
+ import React, { useMemo, useEffect, useContext, useState, useRef, memo, createContext, useCallback, forwardRef, useImperativeHandle, useLayoutEffect } from 'react';
6
5
  import { Quaternion, Euler, Vector3, Object3D, Matrix4, MathUtils, InstancedMesh, BufferAttribute, DynamicDrawUsage } from 'three';
7
- import { mergeVertices } from 'three-stdlib';
6
+ import { useAsset } from 'use-asset';
7
+ import { mergeVertices, VertexNormalsHelper } from 'three-stdlib';
8
+
9
+ function _defineProperty(obj, key, value) {
10
+ if (key in obj) {
11
+ Object.defineProperty(obj, key, {
12
+ value: value,
13
+ enumerable: true,
14
+ configurable: true,
15
+ writable: true
16
+ });
17
+ } else {
18
+ obj[key] = value;
19
+ }
20
+
21
+ return obj;
22
+ }
23
+
24
+ function ownKeys(object, enumerableOnly) {
25
+ var keys = Object.keys(object);
26
+
27
+ if (Object.getOwnPropertySymbols) {
28
+ var symbols = Object.getOwnPropertySymbols(object);
29
+ enumerableOnly && (symbols = symbols.filter(function (sym) {
30
+ return Object.getOwnPropertyDescriptor(object, sym).enumerable;
31
+ })), keys.push.apply(keys, symbols);
32
+ }
33
+
34
+ return keys;
35
+ }
36
+
37
+ function _objectSpread2(target) {
38
+ for (var i = 1; i < arguments.length; i++) {
39
+ var source = null != arguments[i] ? arguments[i] : {};
40
+ i % 2 ? ownKeys(Object(source), !0).forEach(function (key) {
41
+ _defineProperty(target, key, source[key]);
42
+ }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) {
43
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
44
+ });
45
+ }
46
+
47
+ return target;
48
+ }
8
49
 
9
50
  const _quaternion = new Quaternion();
10
51
  new Euler();
@@ -213,375 +254,304 @@ const createJointApi = ref => {
213
254
  };
214
255
  };
215
256
 
216
- const RapierContext = /*#__PURE__*/createContext(undefined);
217
-
218
- const importRapier = async () => {
219
- let r = await import('@dimforge/rapier3d-compat');
220
- await r.init();
221
- return r;
222
- };
223
-
224
- const Physics = ({
225
- colliders: _colliders = "cuboid",
226
- gravity: _gravity = [0, -9.81, 0],
227
- children,
228
- timeStep: _timeStep = 1 / 60,
229
- paused: _paused = false,
230
- updatePriority
231
- }) => {
232
- const rapier = useAsset(importRapier);
233
- const worldRef = useRef();
234
- const getWorldRef = useRef(() => {
235
- if (!worldRef.current) {
236
- const world = new rapier.World(vectorArrayToVector3(_gravity));
237
- worldRef.current = world;
238
- }
239
-
240
- return worldRef.current;
241
- });
242
- const [rigidBodyStates] = useState(() => new Map());
243
- const [colliderStates] = useState(() => new Map());
244
- const [rigidBodyEvents] = useState(() => new Map());
245
- const [colliderEvents] = useState(() => new Map());
246
- const [eventQueue] = useState(() => new EventQueue(false)); // Init world
247
-
248
- useEffect(() => {
249
- const world = getWorldRef.current();
250
- return () => {
251
- if (world) {
252
- world.free();
253
- worldRef.current = undefined;
254
- }
255
- };
256
- }, []); // Update gravity
257
+ const scaleColliderArgs = (shape, args, scale) => {
258
+ const newArgs = args.slice(); // Heightfield uses a vector
257
259
 
258
- useEffect(() => {
259
- const world = worldRef.current;
260
+ if (shape === "heightfield") {
261
+ const s = newArgs[3];
262
+ s.x *= scale.x;
263
+ s.x *= scale.y;
264
+ s.x *= scale.z;
265
+ return newArgs;
266
+ } // Trimesh and convex scale the vertices
260
267
 
261
- if (world) {
262
- world.gravity = vectorArrayToVector3(_gravity);
263
- }
264
- }, [_gravity]);
265
- const [steppingState] = useState({
266
- accumulator: 0
267
- });
268
- /* Check if the timestep is supposed to be variable. We'll do this here
269
- once so we don't have to string-check every frame. */
270
268
 
271
- const timeStepVariable = _timeStep === "vary";
272
- useFrame((_, dt) => {
273
- const world = worldRef.current;
274
- if (!world) return;
275
- /**
276
- * Fixed timeStep simulation progression
277
- * @see https://gafferongames.com/post/fix_your_timestep/
278
- */
269
+ if (shape === "trimesh" || shape === "convexHull") {
270
+ newArgs[0] = scaleVertices(newArgs[0], scale);
271
+ return newArgs;
272
+ } // Prepfill with some extra
279
273
 
280
- const clampedDelta = MathUtils.clamp(dt, 0, 0.2);
281
274
 
282
- if (timeStepVariable) {
283
- world.timestep = clampedDelta;
284
- if (!_paused) world.step(eventQueue);
285
- } else {
286
- world.timestep = _timeStep; // don't step time forwards if paused
287
- // Increase accumulator
275
+ const scaleArray = [scale.x, scale.y, scale.z, scale.x, scale.x];
276
+ return newArgs.map((arg, index) => scaleArray[index] * arg);
277
+ };
278
+ const createColliderFromOptions = (options, world, scale, rigidBody) => {
279
+ const scaledArgs = scaleColliderArgs(options.shape, options.args, scale); // @ts-ignore
288
280
 
289
- steppingState.accumulator += _paused ? 0 : clampedDelta;
281
+ const desc = ColliderDesc[options.shape](...scaledArgs);
282
+ return world.createCollider(desc, rigidBody);
283
+ };
284
+ const massPropertiesConflictError = "Please pick ONLY ONE of the `density`, `mass` and `massProperties` options.";
290
285
 
291
- if (!_paused) {
292
- while (steppingState.accumulator >= _timeStep) {
293
- world.step(eventQueue);
294
- steppingState.accumulator -= _timeStep;
295
- }
296
- }
286
+ const setColliderMassOptions = (collider, options) => {
287
+ if (options.density !== undefined) {
288
+ if (options.mass !== undefined || options.massProperties !== undefined) {
289
+ throw new Error(massPropertiesConflictError);
297
290
  }
298
291
 
299
- const interpolationAlpha = timeStepVariable ? 1 : steppingState.accumulator % _timeStep / _timeStep; // Update meshes
300
-
301
- rigidBodyStates.forEach((state, handle) => {
302
- const rigidBody = world.getRigidBody(handle);
303
- const events = rigidBodyEvents.get(handle);
292
+ collider.setDensity(options.density);
293
+ return;
294
+ }
304
295
 
305
- if (events !== null && events !== void 0 && events.onSleep || events !== null && events !== void 0 && events.onWake) {
306
- if (rigidBody.isSleeping() && !state.isSleeping) {
307
- var _events$onSleep;
296
+ if (options.mass !== undefined) {
297
+ if (options.massProperties !== undefined) {
298
+ throw new Error(massPropertiesConflictError);
299
+ }
308
300
 
309
- events === null || events === void 0 ? void 0 : (_events$onSleep = events.onSleep) === null || _events$onSleep === void 0 ? void 0 : _events$onSleep.call(events);
310
- }
301
+ collider.setMass(options.mass);
302
+ return;
303
+ }
311
304
 
312
- if (!rigidBody.isSleeping() && state.isSleeping) {
313
- var _events$onWake;
305
+ if (options.massProperties !== undefined) {
306
+ collider.setMassProperties(options.massProperties.mass, options.massProperties.centerOfMass, options.massProperties.principalAngularInertia, options.massProperties.angularInertiaLocalFrame);
307
+ }
308
+ };
314
309
 
315
- events === null || events === void 0 ? void 0 : (_events$onWake = events.onWake) === null || _events$onWake === void 0 ? void 0 : _events$onWake.call(events);
316
- }
310
+ const mutableColliderOptions = {
311
+ sensor: (collider, value) => {
312
+ collider.setSensor(value);
313
+ },
314
+ collisionGroups: (collider, value) => {
315
+ collider.setCollisionGroups(value);
316
+ },
317
+ solverGroups: (collider, value) => {
318
+ collider.setSolverGroups(value);
319
+ },
320
+ friction: (collider, value) => {
321
+ collider.setFriction(value);
322
+ },
323
+ frictionCombineRule: (collider, value) => {
324
+ collider.setFrictionCombineRule(value);
325
+ },
326
+ restitution: (collider, value) => {
327
+ collider.setRestitution(value);
328
+ },
329
+ restitutionCombineRule: (collider, value) => {
330
+ collider.setRestitutionCombineRule(value);
331
+ },
332
+ // To make sure the options all mutalbe options are listed
333
+ quaternion: () => {},
334
+ position: () => {},
335
+ rotation: () => {},
336
+ scale: () => {}
337
+ };
338
+ const mutableColliderOptionKeys = Object.keys(mutableColliderOptions);
339
+ const setColliderOptions = (collider, options, states) => {
340
+ const state = states.get(collider.handle);
317
341
 
318
- state.isSleeping = rigidBody.isSleeping();
319
- }
342
+ if (state) {
343
+ var _state$worldParent;
320
344
 
321
- if (!rigidBody || rigidBody.isSleeping() || !state.setMatrix) {
322
- return;
323
- }
345
+ // Update collider position based on the object's position
346
+ const parentWorldScale = state.object.parent.getWorldScale(_vector3);
347
+ const parentInvertedWorldMatrix = (_state$worldParent = state.worldParent) === null || _state$worldParent === void 0 ? void 0 : _state$worldParent.matrixWorld.clone().invert();
348
+ state.object.updateWorldMatrix(true, false);
324
349
 
325
- let t = rigidBody.translation();
326
- let r = rigidBody.rotation(); // Get new position
350
+ _matrix4.copy(state.object.matrixWorld);
327
351
 
328
- _matrix4.compose(t, rapierQuaternionToQuaternion(r), state.scale).premultiply(state.invertedWorldMatrix).decompose(_position, _rotation, _scale);
352
+ if (parentInvertedWorldMatrix) {
353
+ _matrix4.premultiply(parentInvertedWorldMatrix);
354
+ }
329
355
 
330
- if (state.object instanceof InstancedMesh) {
331
- state.setMatrix(_matrix4);
332
- state.object.instanceMatrix.needsUpdate = true;
333
- } else {
334
- // Interpolate from last position
335
- state.object.position.lerp(_position, interpolationAlpha);
336
- state.object.quaternion.slerp(_rotation, interpolationAlpha);
337
- }
338
- });
339
- eventQueue.drainCollisionEvents((handle1, handle2, started) => {
340
- var _collider1$parent, _collider2$parent;
356
+ _matrix4.decompose(_position, _rotation, _scale);
341
357
 
342
- const collider1 = world.getCollider(handle1);
343
- const collider2 = world.getCollider(handle2);
344
- const rigidBodyHandle1 = (_collider1$parent = collider1.parent()) === null || _collider1$parent === void 0 ? void 0 : _collider1$parent.handle;
345
- const rigidBodyHandle2 = (_collider2$parent = collider2.parent()) === null || _collider2$parent === void 0 ? void 0 : _collider2$parent.handle; // Collision Events
358
+ if (collider.parent()) {
359
+ collider.setTranslationWrtParent({
360
+ x: _position.x * parentWorldScale.x,
361
+ y: _position.y * parentWorldScale.y,
362
+ z: _position.z * parentWorldScale.z
363
+ });
364
+ collider.setRotationWrtParent(_rotation);
365
+ } else {
366
+ collider.setTranslation({
367
+ x: _position.x * parentWorldScale.x,
368
+ y: _position.y * parentWorldScale.y,
369
+ z: _position.z * parentWorldScale.z
370
+ });
371
+ collider.setRotation(_rotation);
372
+ }
346
373
 
347
- if (!collider1 || !collider2) {
348
- return;
374
+ mutableColliderOptionKeys.forEach(key => {
375
+ if (key in options) {
376
+ const option = options[key];
377
+ mutableColliderOptions[key](collider, // @ts-ignore Option does not want to fit into the function, but it will
378
+ option, options);
349
379
  }
380
+ }); // handle mass separately, because the assignments
381
+ // are exclusive.
350
382
 
351
- const collider1Events = colliderEvents.get(collider1.handle);
352
- const collider2Events = colliderEvents.get(collider2.handle);
353
- const rigidBody1 = rigidBodyHandle1 ? world.getRigidBody(rigidBodyHandle1) : undefined;
354
- const rigidBody2 = rigidBodyHandle2 ? world.getRigidBody(rigidBodyHandle2) : undefined;
355
- const rigidBody1Events = rigidBodyHandle1 ? rigidBodyEvents.get(rigidBodyHandle1) : undefined;
356
- const rigidBody2Events = rigidBodyHandle2 ? rigidBodyEvents.get(rigidBodyHandle2) : undefined;
357
- const collider1State = colliderStates.get(collider1.handle);
358
- const collider2State = colliderStates.get(collider2.handle);
359
- const rigidBody1State = rigidBodyHandle1 ? rigidBodyStates.get(rigidBodyHandle1) : undefined;
360
- const rigidBody2State = rigidBodyHandle2 ? rigidBodyStates.get(rigidBodyHandle2) : undefined;
383
+ setColliderMassOptions(collider, options);
384
+ }
385
+ };
386
+ const useUpdateColliderOptions = (collidersRef, props, states) => {
387
+ // TODO: Improve this, split each prop into its own effect
388
+ const mutablePropsAsFlatArray = useMemo(() => mutableColliderOptionKeys.flatMap(key => {
389
+ return vectorToTuple(props[key]);
390
+ }), [props]);
391
+ useEffect(() => {
392
+ collidersRef.current.forEach(collider => {
393
+ setColliderOptions(collider, props, states);
394
+ });
395
+ }, mutablePropsAsFlatArray);
396
+ };
361
397
 
362
- if (started) {
363
- world.contactPair(collider1, collider2, (manifold, flipped) => {
364
- var _rigidBody1Events$onC, _rigidBody2Events$onC, _collider1Events$onCo, _collider2Events$onCo;
398
+ const isChildOfMeshCollider = child => {
399
+ let flag = false;
400
+ child.traverseAncestors(a => {
401
+ if (a.userData.r3RapierType === "MeshCollider") flag = true;
402
+ });
403
+ return flag;
404
+ };
365
405
 
366
- /* RigidBody events */
367
- rigidBody1Events === null || rigidBody1Events === void 0 ? void 0 : (_rigidBody1Events$onC = rigidBody1Events.onCollisionEnter) === null || _rigidBody1Events$onC === void 0 ? void 0 : _rigidBody1Events$onC.call(rigidBody1Events, {
368
- rigidBody: rigidBody2,
369
- collider: collider2,
370
- colliderObject: collider2State === null || collider2State === void 0 ? void 0 : collider2State.object,
371
- rigidBodyObject: rigidBody2State === null || rigidBody2State === void 0 ? void 0 : rigidBody2State.object,
372
- manifold,
373
- flipped
374
- });
375
- rigidBody2Events === null || rigidBody2Events === void 0 ? void 0 : (_rigidBody2Events$onC = rigidBody2Events.onCollisionEnter) === null || _rigidBody2Events$onC === void 0 ? void 0 : _rigidBody2Events$onC.call(rigidBody2Events, {
376
- rigidBody: rigidBody1,
377
- collider: collider1,
378
- colliderObject: collider1State === null || collider1State === void 0 ? void 0 : collider1State.object,
379
- rigidBodyObject: rigidBody1State === null || rigidBody1State === void 0 ? void 0 : rigidBody1State.object,
380
- manifold,
381
- flipped
382
- });
383
- /* Collider events */
384
-
385
- collider1Events === null || collider1Events === void 0 ? void 0 : (_collider1Events$onCo = collider1Events.onCollisionEnter) === null || _collider1Events$onCo === void 0 ? void 0 : _collider1Events$onCo.call(collider1Events, {
386
- rigidBody: rigidBody2,
387
- collider: collider2,
388
- colliderObject: collider2State === null || collider2State === void 0 ? void 0 : collider2State.object,
389
- rigidBodyObject: rigidBody2State === null || rigidBody2State === void 0 ? void 0 : rigidBody2State.object,
390
- manifold,
391
- flipped
392
- });
393
- collider2Events === null || collider2Events === void 0 ? void 0 : (_collider2Events$onCo = collider2Events.onCollisionEnter) === null || _collider2Events$onCo === void 0 ? void 0 : _collider2Events$onCo.call(collider2Events, {
394
- rigidBody: rigidBody1,
395
- collider: collider1,
396
- colliderObject: collider1State === null || collider1State === void 0 ? void 0 : collider1State.object,
397
- rigidBodyObject: rigidBody1State === null || rigidBody1State === void 0 ? void 0 : rigidBody1State.object,
398
- manifold,
399
- flipped
400
- });
401
- });
402
- } else {
403
- var _rigidBody1Events$onC2, _rigidBody2Events$onC2, _collider1Events$onCo2, _collider2Events$onCo2;
404
-
405
- rigidBody1Events === null || rigidBody1Events === void 0 ? void 0 : (_rigidBody1Events$onC2 = rigidBody1Events.onCollisionExit) === null || _rigidBody1Events$onC2 === void 0 ? void 0 : _rigidBody1Events$onC2.call(rigidBody1Events, {
406
- rigidBody: rigidBody2,
407
- collider: collider2
408
- });
409
- rigidBody2Events === null || rigidBody2Events === void 0 ? void 0 : (_rigidBody2Events$onC2 = rigidBody2Events.onCollisionExit) === null || _rigidBody2Events$onC2 === void 0 ? void 0 : _rigidBody2Events$onC2.call(rigidBody2Events, {
410
- rigidBody: rigidBody1,
411
- collider: collider1
412
- });
413
- collider1Events === null || collider1Events === void 0 ? void 0 : (_collider1Events$onCo2 = collider1Events.onCollisionExit) === null || _collider1Events$onCo2 === void 0 ? void 0 : _collider1Events$onCo2.call(collider1Events, {
414
- rigidBody: rigidBody2,
415
- collider: collider2
416
- });
417
- collider2Events === null || collider2Events === void 0 ? void 0 : (_collider2Events$onCo2 = collider2Events.onCollisionExit) === null || _collider2Events$onCo2 === void 0 ? void 0 : _collider2Events$onCo2.call(collider2Events, {
418
- rigidBody: rigidBody1,
419
- collider: collider1
420
- });
421
- } // Sensor Intersections
422
-
423
-
424
- if (started) {
425
- if (world.intersectionPair(collider1, collider2)) {
426
- var _rigidBody1Events$onI, _rigidBody2Events$onI, _collider1Events$onIn, _collider2Events$onIn;
427
-
428
- rigidBody1Events === null || rigidBody1Events === void 0 ? void 0 : (_rigidBody1Events$onI = rigidBody1Events.onIntersectionEnter) === null || _rigidBody1Events$onI === void 0 ? void 0 : _rigidBody1Events$onI.call(rigidBody1Events, {
429
- rigidBody: rigidBody2,
430
- collider: collider2,
431
- colliderObject: collider2State === null || collider2State === void 0 ? void 0 : collider2State.object,
432
- rigidBodyObject: rigidBody2State === null || rigidBody2State === void 0 ? void 0 : rigidBody2State.object
433
- });
434
- rigidBody2Events === null || rigidBody2Events === void 0 ? void 0 : (_rigidBody2Events$onI = rigidBody2Events.onIntersectionEnter) === null || _rigidBody2Events$onI === void 0 ? void 0 : _rigidBody2Events$onI.call(rigidBody2Events, {
435
- rigidBody: rigidBody1,
436
- collider: collider1,
437
- colliderObject: collider1State === null || collider1State === void 0 ? void 0 : collider1State.object,
438
- rigidBodyObject: rigidBody1State === null || rigidBody1State === void 0 ? void 0 : rigidBody1State.object
439
- });
440
- collider1Events === null || collider1Events === void 0 ? void 0 : (_collider1Events$onIn = collider1Events.onIntersectionEnter) === null || _collider1Events$onIn === void 0 ? void 0 : _collider1Events$onIn.call(collider1Events, {
441
- rigidBody: rigidBody2,
442
- collider: collider2,
443
- colliderObject: collider2State === null || collider2State === void 0 ? void 0 : collider2State.object,
444
- rigidBodyObject: rigidBody2State === null || rigidBody2State === void 0 ? void 0 : rigidBody2State.object
445
- });
446
- collider2Events === null || collider2Events === void 0 ? void 0 : (_collider2Events$onIn = collider2Events.onIntersectionEnter) === null || _collider2Events$onIn === void 0 ? void 0 : _collider2Events$onIn.call(collider2Events, {
447
- rigidBody: rigidBody1,
448
- collider: collider1,
449
- colliderObject: collider1State === null || collider1State === void 0 ? void 0 : collider1State.object,
450
- rigidBodyObject: rigidBody1State === null || rigidBody1State === void 0 ? void 0 : rigidBody1State.object
451
- });
452
- }
453
- } else {
454
- var _rigidBody1Events$onI2, _rigidBody2Events$onI2, _collider1Events$onIn2, _collider2Events$onIn2;
455
-
456
- rigidBody1Events === null || rigidBody1Events === void 0 ? void 0 : (_rigidBody1Events$onI2 = rigidBody1Events.onIntersectionExit) === null || _rigidBody1Events$onI2 === void 0 ? void 0 : _rigidBody1Events$onI2.call(rigidBody1Events, {
457
- rigidBody: rigidBody2,
458
- collider: collider2
459
- });
460
- rigidBody2Events === null || rigidBody2Events === void 0 ? void 0 : (_rigidBody2Events$onI2 = rigidBody2Events.onIntersectionExit) === null || _rigidBody2Events$onI2 === void 0 ? void 0 : _rigidBody2Events$onI2.call(rigidBody2Events, {
461
- rigidBody: rigidBody1,
462
- collider: collider1
463
- });
464
- collider1Events === null || collider1Events === void 0 ? void 0 : (_collider1Events$onIn2 = collider1Events.onIntersectionExit) === null || _collider1Events$onIn2 === void 0 ? void 0 : _collider1Events$onIn2.call(collider1Events, {
465
- rigidBody: rigidBody2,
466
- collider: collider2
467
- });
468
- collider2Events === null || collider2Events === void 0 ? void 0 : (_collider2Events$onIn2 = collider2Events.onIntersectionExit) === null || _collider2Events$onIn2 === void 0 ? void 0 : _collider2Events$onIn2.call(collider2Events, {
469
- rigidBody: rigidBody1,
470
- collider: collider1
471
- });
472
- }
473
- });
474
- }, updatePriority);
475
- const api = useMemo(() => createWorldApi(getWorldRef), []);
476
- const context = useMemo(() => ({
477
- rapier,
478
- world: api,
479
- physicsOptions: {
480
- colliders: _colliders,
481
- gravity: _gravity
482
- },
483
- rigidBodyStates,
484
- colliderStates,
485
- rigidBodyEvents,
486
- colliderEvents,
487
- isPaused: _paused
488
- }), [_paused]);
489
- return /*#__PURE__*/React.createElement(RapierContext.Provider, {
490
- value: context
491
- }, children);
406
+ const createColliderState = (collider, object, rigidBodyObject) => {
407
+ return {
408
+ collider,
409
+ worldParent: rigidBodyObject || undefined,
410
+ object
411
+ };
412
+ };
413
+ const autoColliderMap = {
414
+ cuboid: "cuboid",
415
+ ball: "ball",
416
+ hull: "convexHull",
417
+ trimesh: "trimesh"
492
418
  };
419
+ const createColliderPropsFromChildren = ({
420
+ object,
421
+ ignoreMeshColliders: _ignoreMeshColliders = true,
422
+ options
423
+ }) => {
424
+ const colliderProps = [];
425
+ object.updateWorldMatrix(true, false);
426
+ const invertedParentMatrixWorld = object.matrixWorld.clone().invert();
493
427
 
494
- function _extends() {
495
- _extends = Object.assign ? Object.assign.bind() : function (target) {
496
- for (var i = 1; i < arguments.length; i++) {
497
- var source = arguments[i];
428
+ const colliderFromChild = child => {
429
+ if ("isMesh" in child) {
430
+ if (_ignoreMeshColliders && isChildOfMeshCollider(child)) return;
431
+ const worldScale = child.getWorldScale(_scale);
432
+ const shape = autoColliderMap[options.colliders || "cuboid"];
433
+ child.updateWorldMatrix(true, false);
498
434
 
499
- for (var key in source) {
500
- if (Object.prototype.hasOwnProperty.call(source, key)) {
501
- target[key] = source[key];
502
- }
503
- }
504
- }
435
+ _matrix4.copy(child.matrixWorld).premultiply(invertedParentMatrixWorld).decompose(_position, _rotation, _scale);
505
436
 
506
- return target;
437
+ const rotationEuler = new Euler().setFromQuaternion(_rotation, "XYZ");
438
+ const {
439
+ geometry
440
+ } = child;
441
+ const {
442
+ args,
443
+ offset
444
+ } = getColliderArgsFromGeometry(geometry, options.colliders || "cuboid");
445
+ colliderProps.push(_objectSpread2(_objectSpread2({}, options), {}, {
446
+ args: args,
447
+ shape: shape,
448
+ rotation: [rotationEuler.x, rotationEuler.y, rotationEuler.z],
449
+ position: [_position.x + offset.x * worldScale.x, _position.y + offset.y * worldScale.y, _position.z + offset.z * worldScale.z],
450
+ scale: [worldScale.x, worldScale.y, worldScale.z]
451
+ }));
452
+ }
507
453
  };
508
- return _extends.apply(this, arguments);
509
- }
510
-
511
- function _objectWithoutPropertiesLoose(source, excluded) {
512
- if (source == null) return {};
513
- var target = {};
514
- var sourceKeys = Object.keys(source);
515
- var key, i;
516
454
 
517
- for (i = 0; i < sourceKeys.length; i++) {
518
- key = sourceKeys[i];
519
- if (excluded.indexOf(key) >= 0) continue;
520
- target[key] = source[key];
455
+ if (options.includeInvisible) {
456
+ object.traverse(colliderFromChild);
457
+ } else {
458
+ object.traverseVisible(colliderFromChild);
521
459
  }
522
460
 
523
- return target;
524
- }
525
-
526
- function _objectWithoutProperties(source, excluded) {
527
- if (source == null) return {};
528
- var target = _objectWithoutPropertiesLoose(source, excluded);
529
- var key, i;
461
+ return colliderProps;
462
+ };
463
+ const getColliderArgsFromGeometry = (geometry, colliders) => {
464
+ switch (colliders) {
465
+ case "cuboid":
466
+ {
467
+ geometry.computeBoundingBox();
468
+ const {
469
+ boundingBox
470
+ } = geometry;
471
+ const size = boundingBox.getSize(new Vector3());
472
+ return {
473
+ args: [size.x / 2, size.y / 2, size.z / 2],
474
+ offset: boundingBox.getCenter(new Vector3())
475
+ };
476
+ }
530
477
 
531
- if (Object.getOwnPropertySymbols) {
532
- var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
478
+ case "ball":
479
+ {
480
+ geometry.computeBoundingSphere();
481
+ const {
482
+ boundingSphere
483
+ } = geometry;
484
+ const radius = boundingSphere.radius;
485
+ return {
486
+ args: [radius],
487
+ offset: boundingSphere.center
488
+ };
489
+ }
533
490
 
534
- for (i = 0; i < sourceSymbolKeys.length; i++) {
535
- key = sourceSymbolKeys[i];
536
- if (excluded.indexOf(key) >= 0) continue;
537
- if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
538
- target[key] = source[key];
539
- }
540
- }
491
+ case "trimesh":
492
+ {
493
+ var _clonedGeometry$index;
541
494
 
542
- return target;
543
- }
495
+ const clonedGeometry = geometry.index ? geometry.clone() : mergeVertices(geometry);
496
+ return {
497
+ args: [clonedGeometry.attributes.position.array, (_clonedGeometry$index = clonedGeometry.index) === null || _clonedGeometry$index === void 0 ? void 0 : _clonedGeometry$index.array],
498
+ offset: new Vector3()
499
+ };
500
+ }
544
501
 
545
- function _defineProperty(obj, key, value) {
546
- if (key in obj) {
547
- Object.defineProperty(obj, key, {
548
- value: value,
549
- enumerable: true,
550
- configurable: true,
551
- writable: true
552
- });
553
- } else {
554
- obj[key] = value;
502
+ case "hull":
503
+ {
504
+ const g = geometry.clone();
505
+ return {
506
+ args: [g.attributes.position.array],
507
+ offset: new Vector3()
508
+ };
509
+ }
555
510
  }
556
511
 
557
- return obj;
558
- }
559
-
560
- function ownKeys(object, enumerableOnly) {
561
- var keys = Object.keys(object);
512
+ return {
513
+ args: [],
514
+ offset: new Vector3()
515
+ };
516
+ };
517
+ const useColliderEvents = (collidersRef, props, events) => {
518
+ const {
519
+ onCollisionEnter,
520
+ onCollisionExit,
521
+ onIntersectionEnter,
522
+ onIntersectionExit,
523
+ onContactForce
524
+ } = props;
525
+ useEffect(() => {
526
+ var _collidersRef$current;
562
527
 
563
- if (Object.getOwnPropertySymbols) {
564
- var symbols = Object.getOwnPropertySymbols(object);
565
- enumerableOnly && (symbols = symbols.filter(function (sym) {
566
- return Object.getOwnPropertyDescriptor(object, sym).enumerable;
567
- })), keys.push.apply(keys, symbols);
568
- }
528
+ (_collidersRef$current = collidersRef.current) === null || _collidersRef$current === void 0 ? void 0 : _collidersRef$current.forEach(collider => {
529
+ const hasCollisionEvent = !!(onCollisionEnter || onCollisionExit || onIntersectionEnter || onIntersectionExit);
530
+ const hasContactForceEvent = !!onContactForce;
569
531
 
570
- return keys;
571
- }
532
+ if (hasCollisionEvent && hasContactForceEvent) {
533
+ collider.setActiveEvents(ActiveEvents.COLLISION_EVENTS | ActiveEvents.CONTACT_FORCE_EVENTS);
534
+ } else if (hasCollisionEvent) {
535
+ collider.setActiveEvents(ActiveEvents.COLLISION_EVENTS);
536
+ } else if (hasContactForceEvent) {
537
+ collider.setActiveEvents(ActiveEvents.CONTACT_FORCE_EVENTS);
538
+ }
572
539
 
573
- function _objectSpread2(target) {
574
- for (var i = 1; i < arguments.length; i++) {
575
- var source = null != arguments[i] ? arguments[i] : {};
576
- i % 2 ? ownKeys(Object(source), !0).forEach(function (key) {
577
- _defineProperty(target, key, source[key]);
578
- }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) {
579
- Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
540
+ events.set(collider.handle, {
541
+ onCollisionEnter,
542
+ onCollisionExit,
543
+ onIntersectionEnter,
544
+ onIntersectionExit,
545
+ onContactForce
546
+ });
580
547
  });
581
- }
548
+ return () => {
549
+ var _collidersRef$current2;
582
550
 
583
- return target;
584
- }
551
+ (_collidersRef$current2 = collidersRef.current) === null || _collidersRef$current2 === void 0 ? void 0 : _collidersRef$current2.forEach(collider => events.delete(collider.handle));
552
+ };
553
+ }, [onCollisionEnter, onCollisionExit, onIntersectionEnter, onIntersectionExit, onContactForce]);
554
+ };
585
555
 
586
556
  const rigidBodyDescFromOptions = options => {
587
557
  const type = rigidBodyTypeFromString((options === null || options === void 0 ? void 0 : options.type) || "dynamic");
@@ -642,6 +612,9 @@ const mutableRigidBodyOptions = {
642
612
  ccd: (rb, value) => {
643
613
  rb.enableCcd(value);
644
614
  },
615
+ userData: (rb, value) => {
616
+ rb.userData = value;
617
+ },
645
618
  position: () => {},
646
619
  rotation: () => {},
647
620
  quaternion: () => {},
@@ -725,443 +698,626 @@ const useRigidBodyEvents = (rigidBodyRef, props, events) => {
725
698
  }, [onWake, onSleep, onCollisionEnter, onCollisionExit, onIntersectionEnter, onIntersectionExit]);
726
699
  };
727
700
 
728
- const scaleColliderArgs = (shape, args, scale) => {
729
- const newArgs = args.slice(); // Heightfield uses a vector
701
+ const useRapier = () => {
702
+ return useContext(RapierContext);
703
+ };
704
+ const useChildColliderProps = (ref, options, ignoreMeshColliders = true) => {
705
+ const [colliderProps, setColliderProps] = useState([]);
706
+ useEffect(() => {
707
+ const object = ref.current;
730
708
 
731
- if (shape === "heightfield") {
732
- const s = newArgs[3];
733
- s.x *= scale.x;
734
- s.x *= scale.y;
735
- s.x *= scale.z;
736
- return newArgs;
737
- } // Trimesh and convex scale the vertices
709
+ if (object && options.colliders !== false) {
710
+ setColliderProps(createColliderPropsFromChildren({
711
+ object: ref.current,
712
+ options,
713
+ ignoreMeshColliders
714
+ }));
715
+ }
716
+ }, [options.colliders]);
717
+ return colliderProps;
718
+ };
719
+ const useRigidBody = (options = {}) => {
720
+ const {
721
+ world,
722
+ rigidBodyStates,
723
+ physicsOptions,
724
+ rigidBodyEvents
725
+ } = useRapier();
726
+ const ref = useRef();
727
+ const mergedOptions = useMemo(() => {
728
+ return _objectSpread2(_objectSpread2(_objectSpread2({}, physicsOptions), options), {}, {
729
+ children: undefined
730
+ });
731
+ }, [physicsOptions, options]);
732
+ const childColliderProps = useChildColliderProps(ref, mergedOptions); // Create rigidbody
738
733
 
734
+ const rigidBodyRef = useRef();
735
+ const getRigidBodyRef = useRef(() => {
736
+ if (!rigidBodyRef.current) {
737
+ const desc = rigidBodyDescFromOptions(options);
738
+ const rigidBody = world.createRigidBody(desc);
739
+ rigidBodyRef.current = world.getRigidBody(rigidBody.handle);
740
+ }
739
741
 
740
- if (shape === "trimesh" || shape === "convexHull") {
741
- newArgs[0] = scaleVertices(newArgs[0], scale);
742
- return newArgs;
743
- } // Prepfill with some extra
742
+ return rigidBodyRef.current;
743
+ }); // Setup
744
744
 
745
+ useEffect(() => {
746
+ const rigidBody = getRigidBodyRef.current();
747
+ rigidBodyRef.current = rigidBody;
745
748
 
746
- const scaleArray = [scale.x, scale.y, scale.z, scale.x, scale.x];
747
- return newArgs.map((arg, index) => scaleArray[index] * arg);
748
- };
749
- const createColliderFromOptions = (options, world, scale, rigidBody) => {
750
- const scaledArgs = scaleColliderArgs(options.shape, options.args, scale); // @ts-ignore
749
+ if (!ref.current) {
750
+ ref.current = new Object3D();
751
+ }
751
752
 
752
- const desc = ColliderDesc[options.shape](...scaledArgs);
753
- return world.createCollider(desc, rigidBody);
754
- };
755
- const massPropertiesConflictError = "Please pick ONLY ONE of the `density`, `mass` and `massProperties` options.";
753
+ rigidBodyStates.set(rigidBody.handle, createRigidBodyState({
754
+ rigidBody,
755
+ object: ref.current
756
+ }));
757
+ return () => {
758
+ world.removeRigidBody(rigidBody);
759
+ rigidBodyStates.delete(rigidBody.handle);
760
+ rigidBodyRef.current = undefined;
761
+ };
762
+ }, []);
763
+ useUpdateRigidBodyOptions(rigidBodyRef, mergedOptions, rigidBodyStates);
764
+ useRigidBodyEvents(rigidBodyRef, mergedOptions, rigidBodyEvents);
765
+ const api = useMemo(() => createRigidBodyApi(getRigidBodyRef), []);
766
+ return [ref, api, childColliderProps];
767
+ }; // Joints
756
768
 
757
- const setColliderMassOptions = (collider, options) => {
758
- if (options.density !== undefined) {
759
- if (options.mass !== undefined || options.massProperties !== undefined) {
760
- throw new Error(massPropertiesConflictError);
769
+ const useImpulseJoint = (body1, body2, params) => {
770
+ const {
771
+ world
772
+ } = useRapier();
773
+ const jointRef = useRef();
774
+ const getJointRef = useRef(() => {
775
+ if (!jointRef.current) {
776
+ let rb1;
777
+ let rb2;
778
+
779
+ if ("current" in body1 && body1.current && "current" in body2 && body2.current) {
780
+ rb1 = world.getRigidBody(body1.current.handle);
781
+ rb2 = world.getRigidBody(body2.current.handle);
782
+ const newJoint = world.createImpulseJoint(params, rb1, rb2);
783
+ jointRef.current = newJoint;
784
+ }
761
785
  }
762
786
 
763
- collider.setDensity(options.density);
764
- return;
765
- }
787
+ return jointRef.current;
788
+ });
789
+ useEffect(() => {
790
+ const joint = getJointRef.current();
791
+ return () => {
792
+ if (joint) {
793
+ world.removeImpulseJoint(joint);
794
+ jointRef.current = undefined;
795
+ }
796
+ };
797
+ }, []);
798
+ const api = useMemo(() => createJointApi(getJointRef), []);
799
+ return api;
800
+ };
801
+ /**
802
+ *
803
+ * A fixed joint ensures that two rigid-bodies don't move relative to each other.
804
+ * Fixed joints are characterized by one local frame (represented by an isometry) on each rigid-body.
805
+ * The fixed-joint makes these frames coincide in world-space.
806
+ */
766
807
 
767
- if (options.mass !== undefined) {
768
- if (options.massProperties !== undefined) {
769
- throw new Error(massPropertiesConflictError);
770
- }
808
+ const useFixedJoint = (body1, body2, [body1Anchor, body1LocalFrame, body2Anchor, body2LocalFrame]) => {
809
+ const {
810
+ rapier
811
+ } = useRapier();
812
+ return useImpulseJoint(body1, body2, rapier.JointData.fixed(vectorArrayToVector3(body1Anchor), _objectSpread2(_objectSpread2({}, vectorArrayToVector3(body1LocalFrame)), {}, {
813
+ w: 1
814
+ }), vectorArrayToVector3(body2Anchor), _objectSpread2(_objectSpread2({}, vectorArrayToVector3(body2LocalFrame)), {}, {
815
+ w: 1
816
+ })));
817
+ };
818
+ /**
819
+ * The spherical joint ensures that two points on the local-spaces of two rigid-bodies always coincide (it prevents any relative
820
+ * translational motion at this points). This is typically used to simulate ragdolls arms, pendulums, etc.
821
+ * They are characterized by one local anchor on each rigid-body. Each anchor represents the location of the
822
+ * points that need to coincide on the local-space of each rigid-body.
823
+ */
771
824
 
772
- collider.setMass(options.mass);
773
- return;
774
- }
825
+ const useSphericalJoint = (body1, body2, [body1Anchor, body2Anchor]) => {
826
+ const {
827
+ rapier
828
+ } = useRapier();
829
+ return useImpulseJoint(body1, body2, rapier.JointData.spherical(vectorArrayToVector3(body1Anchor), vectorArrayToVector3(body2Anchor)));
830
+ };
831
+ /**
832
+ * The revolute joint prevents any relative movement between two rigid-bodies, except for relative
833
+ * rotations along one axis. This is typically used to simulate wheels, fans, etc.
834
+ * They are characterized by one local anchor as well as one local axis on each rigid-body.
835
+ */
775
836
 
776
- if (options.massProperties !== undefined) {
777
- collider.setMassProperties(options.massProperties.mass, options.massProperties.centerOfMass, options.massProperties.principalAngularInertia, options.massProperties.angularInertiaLocalFrame);
778
- }
837
+ const useRevoluteJoint = (body1, body2, [body1Anchor, body2Anchor, axis]) => {
838
+ const {
839
+ rapier
840
+ } = useRapier();
841
+ return useImpulseJoint(body1, body2, rapier.JointData.revolute(vectorArrayToVector3(body1Anchor), vectorArrayToVector3(body2Anchor), vectorArrayToVector3(axis)));
779
842
  };
843
+ /**
844
+ * The prismatic joint prevents any relative movement between two rigid-bodies, except for relative translations along one axis.
845
+ * It is characterized by one local anchor as well as one local axis on each rigid-body. In 3D, an optional
846
+ * local tangent axis can be specified for each rigid-body.
847
+ */
780
848
 
781
- const mutableColliderOptions = {
782
- sensor: (collider, value) => {
783
- collider.setSensor(value);
784
- },
785
- collisionGroups: (collider, value) => {
786
- collider.setCollisionGroups(value);
787
- },
788
- solverGroups: (collider, value) => {
789
- collider.setSolverGroups(value);
790
- },
791
- friction: (collider, value) => {
792
- collider.setFriction(value);
793
- },
794
- frictionCombineRule: (collider, value) => {
795
- collider.setFrictionCombineRule(value);
796
- },
797
- restitution: (collider, value) => {
798
- collider.setRestitution(value);
799
- },
800
- restitutionCombineRule: (collider, value) => {
801
- collider.setRestitutionCombineRule(value);
802
- },
803
- // To make sure the options all mutalbe options are listed
804
- quaternion: () => {},
805
- position: () => {},
806
- rotation: () => {},
807
- scale: () => {}
849
+ const usePrismaticJoint = (body1, body2, [body1Anchor, body2Anchor, axis]) => {
850
+ const {
851
+ rapier
852
+ } = useRapier();
853
+ return useImpulseJoint(body1, body2, rapier.JointData.prismatic(vectorArrayToVector3(body1Anchor), vectorArrayToVector3(body2Anchor), vectorArrayToVector3(axis)));
808
854
  };
809
- const mutableColliderOptionKeys = Object.keys(mutableColliderOptions);
810
- const setColliderOptions = (collider, options, states) => {
811
- const state = states.get(collider.handle);
812
855
 
813
- if (state) {
814
- // Update collider position based on the object's position
815
- const parentWorldScale = state.object.parent.getWorldScale(_vector3);
816
- state.object.updateWorldMatrix(true, false);
856
+ const calcForceByType = {
857
+ static: (s, m2, r, d, G) => s,
858
+ linear: (s, m2, r, d, G) => s * (d / r),
859
+ newtonian: (s, m2, r, d, G) => G * s * m2 / Math.pow(d, 2)
860
+ };
861
+ const applyAttractorForceOnRigidBody = (rigidBody, {
862
+ object,
863
+ strength,
864
+ range,
865
+ gravitationalConstant,
866
+ collisionGroups,
867
+ type
868
+ }) => {
869
+ const rbPosition = rigidBody.translation();
817
870
 
818
- _matrix4.copy(state.object.matrixWorld).premultiply(state.worldParent.matrixWorld.clone().invert()).decompose(_position, _rotation, _scale);
871
+ _position.set(rbPosition.x, rbPosition.y, rbPosition.z);
819
872
 
820
- if (collider.parent()) {
821
- collider.setTranslationWrtParent({
822
- x: _position.x * parentWorldScale.x,
823
- y: _position.y * parentWorldScale.y,
824
- z: _position.z * parentWorldScale.z
825
- });
826
- collider.setRotationWrtParent(_rotation);
827
- } else {
828
- collider.setTranslation({
829
- x: _position.x * parentWorldScale.x,
830
- y: _position.y * parentWorldScale.y,
831
- z: _position.z * parentWorldScale.z
832
- });
833
- collider.setRotation(_rotation);
834
- }
873
+ const worldPosition = object.getWorldPosition(new Vector3());
874
+ const distance = worldPosition.distanceTo(_position);
835
875
 
836
- mutableColliderOptionKeys.forEach(key => {
837
- if (key in options) {
838
- const option = options[key];
839
- mutableColliderOptions[key](collider, // @ts-ignore Option does not want to fit into the function, but it will
840
- option, options);
876
+ if (distance < range) {
877
+ let force = calcForceByType[type](strength, rigidBody.mass(), range, distance, gravitationalConstant); // Prevent wild forces when Attractors collide
878
+
879
+ force = force === Infinity ? strength : force; // Naively test if the rigidBody contains a collider in one of the collision groups
880
+
881
+ let isRigidBodyInCollisionGroup = collisionGroups === undefined ? true : false;
882
+
883
+ if (collisionGroups !== undefined) {
884
+ for (let i = 0; i < rigidBody.numColliders(); i++) {
885
+ const collider = rigidBody.collider(i);
886
+ const colliderCollisionGroups = collider.collisionGroups();
887
+
888
+ if ((collisionGroups >> 16 & colliderCollisionGroups) != 0 && (colliderCollisionGroups >> 16 & collisionGroups) != 0) {
889
+ isRigidBodyInCollisionGroup = true;
890
+ break;
891
+ }
841
892
  }
842
- }); // handle mass separately, because the assignments
843
- // are exclusive.
893
+ }
844
894
 
845
- setColliderMassOptions(collider, options);
895
+ if (isRigidBodyInCollisionGroup) {
896
+ _vector3.set(0, 0, 0).subVectors(worldPosition, _position).normalize().multiplyScalar(force);
897
+
898
+ rigidBody.applyImpulse(_vector3, true);
899
+ }
846
900
  }
847
901
  };
848
- const useUpdateColliderOptions = (collidersRef, props, states) => {
849
- // TODO: Improve this, split each prop into its own effect
850
- const mutablePropsAsFlatArray = useMemo(() => mutableColliderOptionKeys.flatMap(key => {
851
- return vectorToTuple(props[key]);
852
- }), [props]);
902
+ const Attractor = /*#__PURE__*/memo(props => {
903
+ const {
904
+ position = [0, 0, 0],
905
+ strength = 1,
906
+ range = 10,
907
+ type = "static",
908
+ gravitationalConstant = 6.673e-11,
909
+ collisionGroups
910
+ } = props;
911
+ const {
912
+ attractorStates
913
+ } = useRapier();
914
+ const object = useRef(null);
853
915
  useEffect(() => {
854
- collidersRef.current.forEach(collider => {
855
- setColliderOptions(collider, props, states);
856
- });
857
- }, mutablePropsAsFlatArray);
858
- };
916
+ var _object$current;
917
+
918
+ let uuid = ((_object$current = object.current) === null || _object$current === void 0 ? void 0 : _object$current.uuid) || "_";
919
+
920
+ if (object.current) {
921
+ attractorStates.set(uuid, {
922
+ object: object.current,
923
+ strength,
924
+ range,
925
+ type,
926
+ gravitationalConstant,
927
+ collisionGroups
928
+ });
929
+ }
859
930
 
860
- const isChildOfMeshCollider = child => {
861
- let flag = false;
862
- child.traverseAncestors(a => {
863
- if (a.userData.r3RapierType === "MeshCollider") flag = true;
931
+ return () => {
932
+ attractorStates.delete(uuid);
933
+ };
934
+ }, [props]);
935
+ return /*#__PURE__*/React.createElement("object3D", {
936
+ ref: object,
937
+ position: position
864
938
  });
865
- return flag;
866
- };
939
+ });
940
+
941
+ const RapierContext = /*#__PURE__*/createContext(undefined);
942
+
943
+ const getCollisionPayloadFromSource = (target, other) => {
944
+ var _target$collider$stat, _target$rigidBody$sta, _other$collider$state, _other$rigidBody$stat, _other$collider$state2, _other$rigidBody$stat2;
867
945
 
868
- const createColliderState = (collider, object, rigidBodyObject) => {
869
946
  return {
870
- collider,
871
- worldParent: rigidBodyObject || object.parent,
872
- object
947
+ target: {
948
+ rigidBody: target.rigidBody.object,
949
+ collider: target.collider.object,
950
+ colliderObject: (_target$collider$stat = target.collider.state) === null || _target$collider$stat === void 0 ? void 0 : _target$collider$stat.object,
951
+ rigidBodyObject: (_target$rigidBody$sta = target.rigidBody.state) === null || _target$rigidBody$sta === void 0 ? void 0 : _target$rigidBody$sta.object
952
+ },
953
+ other: {
954
+ rigidBody: other.rigidBody.object,
955
+ collider: other.collider.object,
956
+ colliderObject: (_other$collider$state = other.collider.state) === null || _other$collider$state === void 0 ? void 0 : _other$collider$state.object,
957
+ rigidBodyObject: (_other$rigidBody$stat = other.rigidBody.state) === null || _other$rigidBody$stat === void 0 ? void 0 : _other$rigidBody$stat.object
958
+ },
959
+ rigidBody: other.rigidBody.object,
960
+ collider: other.collider.object,
961
+ colliderObject: (_other$collider$state2 = other.collider.state) === null || _other$collider$state2 === void 0 ? void 0 : _other$collider$state2.object,
962
+ rigidBodyObject: (_other$rigidBody$stat2 = other.rigidBody.state) === null || _other$rigidBody$stat2 === void 0 ? void 0 : _other$rigidBody$stat2.object
873
963
  };
874
964
  };
875
- const autoColliderMap = {
876
- cuboid: "cuboid",
877
- ball: "ball",
878
- hull: "convexHull",
879
- trimesh: "trimesh"
965
+
966
+ const importRapier = async () => {
967
+ let r = await import('@dimforge/rapier3d-compat');
968
+ await r.init();
969
+ return r;
880
970
  };
881
- const createColliderPropsFromChildren = ({
882
- object,
883
- ignoreMeshColliders: _ignoreMeshColliders = true,
884
- options
971
+
972
+ const Physics = ({
973
+ colliders: _colliders = "cuboid",
974
+ gravity: _gravity = [0, -9.81, 0],
975
+ children,
976
+ timeStep: _timeStep = 1 / 60,
977
+ paused: _paused = false,
978
+ updatePriority,
979
+ interpolate: _interpolate = true
885
980
  }) => {
886
- const colliderProps = [];
887
- object.updateWorldMatrix(true, false);
888
- const invertedParentMatrixWorld = object.matrixWorld.clone().invert();
981
+ const rapier = useAsset(importRapier);
982
+ const worldRef = useRef();
983
+ const getWorldRef = useRef(() => {
984
+ if (!worldRef.current) {
985
+ const world = new rapier.World(vectorArrayToVector3(_gravity));
986
+ worldRef.current = world;
987
+ }
889
988
 
890
- const colliderFromChild = child => {
891
- if ("isMesh" in child) {
892
- if (_ignoreMeshColliders && isChildOfMeshCollider(child)) return;
893
- const worldScale = child.getWorldScale(_scale);
894
- const shape = autoColliderMap[options.colliders || "cuboid"];
895
- child.updateWorldMatrix(true, false);
989
+ return worldRef.current;
990
+ });
991
+ const [rigidBodyStates] = useState(() => new Map());
992
+ const [colliderStates] = useState(() => new Map());
993
+ const [rigidBodyEvents] = useState(() => new Map());
994
+ const [colliderEvents] = useState(() => new Map());
995
+ const [eventQueue] = useState(() => new EventQueue(false));
996
+ const [attractorStates] = useState(() => new Map()); // Init world
896
997
 
897
- _matrix4.copy(child.matrixWorld).premultiply(invertedParentMatrixWorld).decompose(_position, _rotation, _scale);
998
+ useEffect(() => {
999
+ const world = getWorldRef.current();
1000
+ return () => {
1001
+ if (world) {
1002
+ world.free();
1003
+ worldRef.current = undefined;
1004
+ }
1005
+ };
1006
+ }, []); // Update gravity
898
1007
 
899
- const rotationEuler = new Euler().setFromQuaternion(_rotation, "XYZ");
900
- const {
901
- geometry
902
- } = child;
903
- const {
904
- args,
905
- offset
906
- } = getColliderArgsFromGeometry(geometry, options.colliders || "cuboid");
907
- colliderProps.push(_objectSpread2(_objectSpread2({}, options), {}, {
908
- args: args,
909
- shape: shape,
910
- rotation: [rotationEuler.x, rotationEuler.y, rotationEuler.z],
911
- position: [_position.x + offset.x * worldScale.x, _position.y + offset.y * worldScale.y, _position.z + offset.z * worldScale.z],
912
- scale: [worldScale.x, worldScale.y, worldScale.z]
913
- }));
1008
+ useEffect(() => {
1009
+ const world = worldRef.current;
1010
+
1011
+ if (world) {
1012
+ world.gravity = vectorArrayToVector3(_gravity);
914
1013
  }
915
- };
1014
+ }, [_gravity]);
1015
+ const getSourceFromColliderHandle = useCallback(handle => {
1016
+ const world = worldRef.current;
916
1017
 
917
- if (options.includeInvisible) {
918
- object.traverse(colliderFromChild);
919
- } else {
920
- object.traverseVisible(colliderFromChild);
921
- }
1018
+ if (world) {
1019
+ var _collider$parent;
1020
+
1021
+ const collider = world.getCollider(handle);
1022
+ const colEvents = colliderEvents.get(handle);
1023
+ const colliderState = colliderStates.get(handle);
1024
+ const rigidBodyHandle = collider === null || collider === void 0 ? void 0 : (_collider$parent = collider.parent()) === null || _collider$parent === void 0 ? void 0 : _collider$parent.handle;
1025
+ const rigidBody = rigidBodyHandle !== undefined ? world.getRigidBody(rigidBodyHandle) : undefined;
1026
+ const rbEvents = rigidBody && rigidBodyHandle !== undefined ? rigidBodyEvents.get(rigidBodyHandle) : undefined;
1027
+ const rigidBodyState = rigidBodyHandle !== undefined ? rigidBodyStates.get(rigidBodyHandle) : undefined;
1028
+ const source = {
1029
+ collider: {
1030
+ object: collider,
1031
+ events: colEvents,
1032
+ state: colliderState
1033
+ },
1034
+ rigidBody: {
1035
+ object: rigidBody,
1036
+ events: rbEvents,
1037
+ state: rigidBodyState
1038
+ }
1039
+ };
1040
+ return source;
1041
+ }
1042
+ }, []);
1043
+ const [steppingState] = useState({
1044
+ previousState: {},
1045
+ accumulator: 0
1046
+ });
1047
+ const step = useCallback(dt => {
1048
+ const world = worldRef.current;
1049
+ if (!world) return;
1050
+ /* Check if the timestep is supposed to be variable. We'll do this here
1051
+ once so we don't have to string-check every frame. */
922
1052
 
923
- return colliderProps;
924
- };
925
- const getColliderArgsFromGeometry = (geometry, colliders) => {
926
- switch (colliders) {
927
- case "cuboid":
928
- {
929
- geometry.computeBoundingBox();
930
- const {
931
- boundingBox
932
- } = geometry;
933
- const size = boundingBox.getSize(new Vector3());
934
- return {
935
- args: [size.x / 2, size.y / 2, size.z / 2],
936
- offset: boundingBox.getCenter(new Vector3())
937
- };
1053
+ const timeStepVariable = _timeStep === "vary";
1054
+ /**
1055
+ * Fixed timeStep simulation progression
1056
+ * @see https://gafferongames.com/post/fix_your_timestep/
1057
+ */
1058
+
1059
+ const clampedDelta = MathUtils.clamp(dt, 0, 0.2);
1060
+
1061
+ if (timeStepVariable) {
1062
+ world.timestep = clampedDelta;
1063
+ world.step(eventQueue);
1064
+ } else {
1065
+ world.timestep = _timeStep; // don't step time forwards if paused
1066
+ // Increase accumulator
1067
+
1068
+ steppingState.accumulator += clampedDelta;
1069
+
1070
+ while (steppingState.accumulator >= _timeStep) {
1071
+ world.forEachRigidBody(body => {
1072
+ // Set up previous state
1073
+ // needed for accurate interpolations if the world steps more than once
1074
+ if (_interpolate) {
1075
+ steppingState.previousState = {};
1076
+ steppingState.previousState[body.handle] = {
1077
+ position: body.translation(),
1078
+ rotation: body.rotation()
1079
+ };
1080
+ } // Apply attractors
1081
+
1082
+
1083
+ attractorStates.forEach(attractorState => {
1084
+ applyAttractorForceOnRigidBody(body, attractorState);
1085
+ });
1086
+ });
1087
+ world.step(eventQueue);
1088
+ steppingState.accumulator -= _timeStep;
938
1089
  }
1090
+ }
939
1091
 
940
- case "ball":
941
- {
942
- geometry.computeBoundingSphere();
943
- const {
944
- boundingSphere
945
- } = geometry;
946
- const radius = boundingSphere.radius;
947
- return {
948
- args: [radius],
949
- offset: boundingSphere.center
950
- };
1092
+ const interpolationAlpha = timeStepVariable || !_interpolate || _paused ? 1 : steppingState.accumulator / _timeStep; // Update meshes
1093
+
1094
+ rigidBodyStates.forEach((state, handle) => {
1095
+ const rigidBody = world.getRigidBody(handle);
1096
+ const events = rigidBodyEvents.get(handle);
1097
+
1098
+ if (events !== null && events !== void 0 && events.onSleep || events !== null && events !== void 0 && events.onWake) {
1099
+ if (rigidBody.isSleeping() && !state.isSleeping) {
1100
+ var _events$onSleep;
1101
+
1102
+ events === null || events === void 0 ? void 0 : (_events$onSleep = events.onSleep) === null || _events$onSleep === void 0 ? void 0 : _events$onSleep.call(events);
1103
+ }
1104
+
1105
+ if (!rigidBody.isSleeping() && state.isSleeping) {
1106
+ var _events$onWake;
1107
+
1108
+ events === null || events === void 0 ? void 0 : (_events$onWake = events.onWake) === null || _events$onWake === void 0 ? void 0 : _events$onWake.call(events);
1109
+ }
1110
+
1111
+ state.isSleeping = rigidBody.isSleeping();
951
1112
  }
952
1113
 
953
- case "trimesh":
954
- {
955
- var _clonedGeometry$index;
1114
+ if (!rigidBody || rigidBody.isSleeping() || !state.setMatrix) {
1115
+ return;
1116
+ } // New states
956
1117
 
957
- const clonedGeometry = geometry.index ? geometry.clone() : mergeVertices(geometry);
958
- return {
959
- args: [clonedGeometry.attributes.position.array, (_clonedGeometry$index = clonedGeometry.index) === null || _clonedGeometry$index === void 0 ? void 0 : _clonedGeometry$index.array],
960
- offset: new Vector3()
961
- };
1118
+
1119
+ let t = rigidBody.translation();
1120
+ let r = rigidBody.rotation();
1121
+ let previousState = steppingState.previousState[handle];
1122
+
1123
+ if (previousState) {
1124
+ // Get previous simulated world position
1125
+ _matrix4.compose(previousState.position, rapierQuaternionToQuaternion(previousState.rotation), state.scale).premultiply(state.invertedWorldMatrix).decompose(_position, _rotation, _scale); // Apply previous tick position
1126
+
1127
+
1128
+ if (!(state.object instanceof InstancedMesh)) {
1129
+ state.object.position.copy(_position);
1130
+ state.object.quaternion.copy(_rotation);
1131
+ }
1132
+ } // Get new position
1133
+
1134
+
1135
+ _matrix4.compose(t, rapierQuaternionToQuaternion(r), state.scale).premultiply(state.invertedWorldMatrix).decompose(_position, _rotation, _scale);
1136
+
1137
+ if (state.object instanceof InstancedMesh) {
1138
+ state.setMatrix(_matrix4);
1139
+ state.object.instanceMatrix.needsUpdate = true;
1140
+ } else {
1141
+ // Interpolate to new position
1142
+ state.object.position.lerp(_position, interpolationAlpha);
1143
+ state.object.quaternion.slerp(_rotation, interpolationAlpha);
962
1144
  }
1145
+ });
1146
+ eventQueue.drainCollisionEvents((handle1, handle2, started) => {
1147
+ const source1 = getSourceFromColliderHandle(handle1);
1148
+ const source2 = getSourceFromColliderHandle(handle2); // Collision Events
963
1149
 
964
- case "hull":
965
- {
966
- const g = geometry.clone();
967
- return {
968
- args: [g.attributes.position.array],
969
- offset: new Vector3()
970
- };
1150
+ if (!(source1 !== null && source1 !== void 0 && source1.collider.object) || !(source2 !== null && source2 !== void 0 && source2.collider.object)) {
1151
+ return;
971
1152
  }
972
- }
973
1153
 
974
- return {
975
- args: [],
976
- offset: new Vector3()
977
- };
978
- };
979
- const useColliderEvents = (collidersRef, props, events) => {
980
- const {
981
- onCollisionEnter,
982
- onCollisionExit,
983
- onIntersectionEnter,
984
- onIntersectionExit
985
- } = props;
986
- useEffect(() => {
987
- var _collidersRef$current;
1154
+ const collisionPayload1 = getCollisionPayloadFromSource(source1, source2);
1155
+ const collisionPayload2 = getCollisionPayloadFromSource(source2, source1);
1156
+
1157
+ if (started) {
1158
+ world.contactPair(source1.collider.object, source2.collider.object, (manifold, flipped) => {
1159
+ var _source1$rigidBody$ev, _source1$rigidBody$ev2, _source2$rigidBody$ev, _source2$rigidBody$ev2, _source1$collider$eve, _source1$collider$eve2, _source2$collider$eve, _source2$collider$eve2;
1160
+
1161
+ /* RigidBody events */
1162
+ (_source1$rigidBody$ev = source1.rigidBody.events) === null || _source1$rigidBody$ev === void 0 ? void 0 : (_source1$rigidBody$ev2 = _source1$rigidBody$ev.onCollisionEnter) === null || _source1$rigidBody$ev2 === void 0 ? void 0 : _source1$rigidBody$ev2.call(_source1$rigidBody$ev, _objectSpread2(_objectSpread2({}, collisionPayload1), {}, {
1163
+ manifold,
1164
+ flipped
1165
+ }));
1166
+ (_source2$rigidBody$ev = source2.rigidBody.events) === null || _source2$rigidBody$ev === void 0 ? void 0 : (_source2$rigidBody$ev2 = _source2$rigidBody$ev.onCollisionEnter) === null || _source2$rigidBody$ev2 === void 0 ? void 0 : _source2$rigidBody$ev2.call(_source2$rigidBody$ev, _objectSpread2(_objectSpread2({}, collisionPayload2), {}, {
1167
+ manifold,
1168
+ flipped
1169
+ }));
1170
+ /* Collider events */
1171
+
1172
+ (_source1$collider$eve = source1.collider.events) === null || _source1$collider$eve === void 0 ? void 0 : (_source1$collider$eve2 = _source1$collider$eve.onCollisionEnter) === null || _source1$collider$eve2 === void 0 ? void 0 : _source1$collider$eve2.call(_source1$collider$eve, _objectSpread2(_objectSpread2({}, collisionPayload1), {}, {
1173
+ manifold,
1174
+ flipped
1175
+ }));
1176
+ (_source2$collider$eve = source2.collider.events) === null || _source2$collider$eve === void 0 ? void 0 : (_source2$collider$eve2 = _source2$collider$eve.onCollisionEnter) === null || _source2$collider$eve2 === void 0 ? void 0 : _source2$collider$eve2.call(_source2$collider$eve, _objectSpread2(_objectSpread2({}, collisionPayload2), {}, {
1177
+ manifold,
1178
+ flipped
1179
+ }));
1180
+ });
1181
+ } else {
1182
+ var _source1$rigidBody$ev3, _source1$rigidBody$ev4, _source2$rigidBody$ev3, _source2$rigidBody$ev4, _source1$collider$eve3, _source1$collider$eve4, _source2$collider$eve3, _source2$collider$eve4;
1183
+
1184
+ (_source1$rigidBody$ev3 = source1.rigidBody.events) === null || _source1$rigidBody$ev3 === void 0 ? void 0 : (_source1$rigidBody$ev4 = _source1$rigidBody$ev3.onCollisionExit) === null || _source1$rigidBody$ev4 === void 0 ? void 0 : _source1$rigidBody$ev4.call(_source1$rigidBody$ev3, collisionPayload1);
1185
+ (_source2$rigidBody$ev3 = source2.rigidBody.events) === null || _source2$rigidBody$ev3 === void 0 ? void 0 : (_source2$rigidBody$ev4 = _source2$rigidBody$ev3.onCollisionExit) === null || _source2$rigidBody$ev4 === void 0 ? void 0 : _source2$rigidBody$ev4.call(_source2$rigidBody$ev3, collisionPayload2);
1186
+ (_source1$collider$eve3 = source1.collider.events) === null || _source1$collider$eve3 === void 0 ? void 0 : (_source1$collider$eve4 = _source1$collider$eve3.onCollisionExit) === null || _source1$collider$eve4 === void 0 ? void 0 : _source1$collider$eve4.call(_source1$collider$eve3, collisionPayload1);
1187
+ (_source2$collider$eve3 = source2.collider.events) === null || _source2$collider$eve3 === void 0 ? void 0 : (_source2$collider$eve4 = _source2$collider$eve3.onCollisionExit) === null || _source2$collider$eve4 === void 0 ? void 0 : _source2$collider$eve4.call(_source2$collider$eve3, collisionPayload2);
1188
+ } // Sensor Intersections
988
1189
 
989
- (_collidersRef$current = collidersRef.current) === null || _collidersRef$current === void 0 ? void 0 : _collidersRef$current.forEach(collider => {
990
- if (onCollisionEnter || onCollisionExit || onIntersectionEnter || onIntersectionExit) {
991
- collider.setActiveEvents(ActiveEvents.COLLISION_EVENTS);
992
- }
993
1190
 
994
- events.set(collider.handle, {
995
- onCollisionEnter,
996
- onCollisionExit,
997
- onIntersectionEnter,
998
- onIntersectionExit
999
- });
1191
+ if (started) {
1192
+ if (world.intersectionPair(source1.collider.object, source2.collider.object)) {
1193
+ var _source1$rigidBody$ev5, _source1$rigidBody$ev6, _source2$rigidBody$ev5, _source2$rigidBody$ev6, _source1$collider$eve5, _source1$collider$eve6, _source2$collider$eve5, _source2$collider$eve6;
1194
+
1195
+ (_source1$rigidBody$ev5 = source1.rigidBody.events) === null || _source1$rigidBody$ev5 === void 0 ? void 0 : (_source1$rigidBody$ev6 = _source1$rigidBody$ev5.onIntersectionEnter) === null || _source1$rigidBody$ev6 === void 0 ? void 0 : _source1$rigidBody$ev6.call(_source1$rigidBody$ev5, collisionPayload1);
1196
+ (_source2$rigidBody$ev5 = source2.rigidBody.events) === null || _source2$rigidBody$ev5 === void 0 ? void 0 : (_source2$rigidBody$ev6 = _source2$rigidBody$ev5.onIntersectionEnter) === null || _source2$rigidBody$ev6 === void 0 ? void 0 : _source2$rigidBody$ev6.call(_source2$rigidBody$ev5, collisionPayload2);
1197
+ (_source1$collider$eve5 = source1.collider.events) === null || _source1$collider$eve5 === void 0 ? void 0 : (_source1$collider$eve6 = _source1$collider$eve5.onIntersectionEnter) === null || _source1$collider$eve6 === void 0 ? void 0 : _source1$collider$eve6.call(_source1$collider$eve5, collisionPayload1);
1198
+ (_source2$collider$eve5 = source2.collider.events) === null || _source2$collider$eve5 === void 0 ? void 0 : (_source2$collider$eve6 = _source2$collider$eve5.onIntersectionEnter) === null || _source2$collider$eve6 === void 0 ? void 0 : _source2$collider$eve6.call(_source2$collider$eve5, collisionPayload2);
1199
+ }
1200
+ } else {
1201
+ var _source1$rigidBody$ev7, _source1$rigidBody$ev8, _source2$rigidBody$ev7, _source2$rigidBody$ev8, _source1$collider$eve7, _source1$collider$eve8, _source2$collider$eve7, _source2$collider$eve8;
1202
+
1203
+ (_source1$rigidBody$ev7 = source1.rigidBody.events) === null || _source1$rigidBody$ev7 === void 0 ? void 0 : (_source1$rigidBody$ev8 = _source1$rigidBody$ev7.onIntersectionExit) === null || _source1$rigidBody$ev8 === void 0 ? void 0 : _source1$rigidBody$ev8.call(_source1$rigidBody$ev7, collisionPayload1);
1204
+ (_source2$rigidBody$ev7 = source2.rigidBody.events) === null || _source2$rigidBody$ev7 === void 0 ? void 0 : (_source2$rigidBody$ev8 = _source2$rigidBody$ev7.onIntersectionExit) === null || _source2$rigidBody$ev8 === void 0 ? void 0 : _source2$rigidBody$ev8.call(_source2$rigidBody$ev7, collisionPayload2);
1205
+ (_source1$collider$eve7 = source1.collider.events) === null || _source1$collider$eve7 === void 0 ? void 0 : (_source1$collider$eve8 = _source1$collider$eve7.onIntersectionExit) === null || _source1$collider$eve8 === void 0 ? void 0 : _source1$collider$eve8.call(_source1$collider$eve7, collisionPayload1);
1206
+ (_source2$collider$eve7 = source2.collider.events) === null || _source2$collider$eve7 === void 0 ? void 0 : (_source2$collider$eve8 = _source2$collider$eve7.onIntersectionExit) === null || _source2$collider$eve8 === void 0 ? void 0 : _source2$collider$eve8.call(_source2$collider$eve7, collisionPayload2);
1207
+ }
1000
1208
  });
1001
- return () => {
1002
- var _collidersRef$current2;
1209
+ eventQueue.drainContactForceEvents(event => {
1210
+ var _source1$rigidBody$ev9, _source1$rigidBody$ev10, _source2$rigidBody$ev9, _source2$rigidBody$ev10, _source1$collider$eve9, _source1$collider$eve10, _source2$collider$eve9, _source2$collider$eve10;
1003
1211
 
1004
- (_collidersRef$current2 = collidersRef.current) === null || _collidersRef$current2 === void 0 ? void 0 : _collidersRef$current2.forEach(collider => events.delete(collider.handle));
1005
- };
1006
- }, [onCollisionEnter, onCollisionExit, onIntersectionEnter, onIntersectionExit]);
1007
- };
1212
+ const source1 = getSourceFromColliderHandle(event.collider1());
1213
+ const source2 = getSourceFromColliderHandle(event.collider2()); // Collision Events
1008
1214
 
1009
- const useRapier = () => {
1010
- return useContext(RapierContext);
1011
- };
1012
- const useChildColliderProps = (ref, options, ignoreMeshColliders = true) => {
1013
- const [colliderProps, setColliderProps] = useState([]);
1014
- useEffect(() => {
1015
- const object = ref.current;
1215
+ if (!(source1 !== null && source1 !== void 0 && source1.collider.object) || !(source2 !== null && source2 !== void 0 && source2.collider.object)) {
1216
+ return;
1217
+ }
1016
1218
 
1017
- if (object && options.colliders !== false) {
1018
- setColliderProps(createColliderPropsFromChildren({
1019
- object: ref.current,
1020
- options,
1021
- ignoreMeshColliders
1219
+ const collisionPayload1 = getCollisionPayloadFromSource(source1, source2);
1220
+ const collisionPayload2 = getCollisionPayloadFromSource(source2, source1);
1221
+ (_source1$rigidBody$ev9 = source1.rigidBody.events) === null || _source1$rigidBody$ev9 === void 0 ? void 0 : (_source1$rigidBody$ev10 = _source1$rigidBody$ev9.onContactForce) === null || _source1$rigidBody$ev10 === void 0 ? void 0 : _source1$rigidBody$ev10.call(_source1$rigidBody$ev9, _objectSpread2(_objectSpread2({}, collisionPayload1), {}, {
1222
+ totalForce: event.totalForce(),
1223
+ totalForceMagnitude: event.totalForceMagnitude(),
1224
+ maxForceDirection: event.maxForceDirection(),
1225
+ maxForceMagnitude: event.maxForceMagnitude()
1226
+ }));
1227
+ (_source2$rigidBody$ev9 = source2.rigidBody.events) === null || _source2$rigidBody$ev9 === void 0 ? void 0 : (_source2$rigidBody$ev10 = _source2$rigidBody$ev9.onContactForce) === null || _source2$rigidBody$ev10 === void 0 ? void 0 : _source2$rigidBody$ev10.call(_source2$rigidBody$ev9, _objectSpread2(_objectSpread2({}, collisionPayload2), {}, {
1228
+ totalForce: event.totalForce(),
1229
+ totalForceMagnitude: event.totalForceMagnitude(),
1230
+ maxForceDirection: event.maxForceDirection(),
1231
+ maxForceMagnitude: event.maxForceMagnitude()
1232
+ }));
1233
+ (_source1$collider$eve9 = source1.collider.events) === null || _source1$collider$eve9 === void 0 ? void 0 : (_source1$collider$eve10 = _source1$collider$eve9.onContactForce) === null || _source1$collider$eve10 === void 0 ? void 0 : _source1$collider$eve10.call(_source1$collider$eve9, _objectSpread2(_objectSpread2({}, collisionPayload1), {}, {
1234
+ totalForce: event.totalForce(),
1235
+ totalForceMagnitude: event.totalForceMagnitude(),
1236
+ maxForceDirection: event.maxForceDirection(),
1237
+ maxForceMagnitude: event.maxForceMagnitude()
1238
+ }));
1239
+ (_source2$collider$eve9 = source2.collider.events) === null || _source2$collider$eve9 === void 0 ? void 0 : (_source2$collider$eve10 = _source2$collider$eve9.onContactForce) === null || _source2$collider$eve10 === void 0 ? void 0 : _source2$collider$eve10.call(_source2$collider$eve9, _objectSpread2(_objectSpread2({}, collisionPayload2), {}, {
1240
+ totalForce: event.totalForce(),
1241
+ totalForceMagnitude: event.totalForceMagnitude(),
1242
+ maxForceDirection: event.maxForceDirection(),
1243
+ maxForceMagnitude: event.maxForceMagnitude()
1022
1244
  }));
1023
- }
1024
- }, [options.colliders]);
1025
- return colliderProps;
1026
- };
1027
- const useRigidBody = (options = {}) => {
1028
- const {
1029
- world,
1030
- rigidBodyStates,
1031
- physicsOptions,
1032
- rigidBodyEvents
1033
- } = useRapier();
1034
- const ref = useRef();
1035
- const mergedOptions = useMemo(() => {
1036
- return _objectSpread2(_objectSpread2(_objectSpread2({}, physicsOptions), options), {}, {
1037
- children: undefined
1038
1245
  });
1039
- }, [physicsOptions, options]);
1040
- const childColliderProps = useChildColliderProps(ref, mergedOptions); // Create rigidbody
1246
+ }, [_paused, _timeStep, _interpolate]);
1247
+ useFrame((_, dt) => {
1248
+ if (!_paused) step(dt);
1249
+ }, updatePriority);
1250
+ const api = useMemo(() => createWorldApi(getWorldRef), []);
1251
+ const context = useMemo(() => ({
1252
+ rapier,
1253
+ world: api,
1254
+ physicsOptions: {
1255
+ colliders: _colliders,
1256
+ gravity: _gravity
1257
+ },
1258
+ rigidBodyStates,
1259
+ colliderStates,
1260
+ rigidBodyEvents,
1261
+ colliderEvents,
1262
+ attractorStates,
1263
+ isPaused: _paused,
1264
+ step
1265
+ }), [_paused, step]);
1266
+ return /*#__PURE__*/React.createElement(RapierContext.Provider, {
1267
+ value: context
1268
+ }, children);
1269
+ };
1041
1270
 
1042
- const rigidBodyRef = useRef();
1043
- const getRigidBodyRef = useRef(() => {
1044
- if (!rigidBodyRef.current) {
1045
- const desc = rigidBodyDescFromOptions(options);
1046
- const rigidBody = world.createRigidBody(desc);
1047
- rigidBodyRef.current = world.getRigidBody(rigidBody.handle);
1271
+ function _extends() {
1272
+ _extends = Object.assign ? Object.assign.bind() : function (target) {
1273
+ for (var i = 1; i < arguments.length; i++) {
1274
+ var source = arguments[i];
1275
+
1276
+ for (var key in source) {
1277
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
1278
+ target[key] = source[key];
1279
+ }
1280
+ }
1048
1281
  }
1049
1282
 
1050
- return rigidBodyRef.current;
1051
- }); // Setup
1283
+ return target;
1284
+ };
1285
+ return _extends.apply(this, arguments);
1286
+ }
1052
1287
 
1053
- useEffect(() => {
1054
- const rigidBody = getRigidBodyRef.current();
1055
- rigidBodyRef.current = rigidBody;
1288
+ function _objectWithoutPropertiesLoose(source, excluded) {
1289
+ if (source == null) return {};
1290
+ var target = {};
1291
+ var sourceKeys = Object.keys(source);
1292
+ var key, i;
1056
1293
 
1057
- if (!ref.current) {
1058
- ref.current = new Object3D();
1059
- } // isSleeping used for onSleep and onWake events
1294
+ for (i = 0; i < sourceKeys.length; i++) {
1295
+ key = sourceKeys[i];
1296
+ if (excluded.indexOf(key) >= 0) continue;
1297
+ target[key] = source[key];
1298
+ }
1060
1299
 
1300
+ return target;
1301
+ }
1061
1302
 
1062
- ref.current.userData.isSleeping = false;
1063
- rigidBodyStates.set(rigidBody.handle, createRigidBodyState({
1064
- rigidBody,
1065
- object: ref.current
1066
- }));
1067
- return () => {
1068
- world.removeRigidBody(rigidBody);
1069
- rigidBodyStates.delete(rigidBody.handle);
1070
- rigidBodyRef.current = undefined;
1071
- };
1072
- }, []);
1073
- useUpdateRigidBodyOptions(rigidBodyRef, mergedOptions, rigidBodyStates);
1074
- useRigidBodyEvents(rigidBodyRef, mergedOptions, rigidBodyEvents);
1075
- const api = useMemo(() => createRigidBodyApi(getRigidBodyRef), []);
1076
- return [ref, api, childColliderProps];
1077
- }; // Joints
1303
+ function _objectWithoutProperties(source, excluded) {
1304
+ if (source == null) return {};
1305
+ var target = _objectWithoutPropertiesLoose(source, excluded);
1306
+ var key, i;
1078
1307
 
1079
- const useImpulseJoint = (body1, body2, params) => {
1080
- const {
1081
- world
1082
- } = useRapier();
1083
- const jointRef = useRef();
1084
- const getJointRef = useRef(() => {
1085
- if (!jointRef.current) {
1086
- let rb1;
1087
- let rb2;
1308
+ if (Object.getOwnPropertySymbols) {
1309
+ var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
1088
1310
 
1089
- if ("current" in body1 && body1.current && "current" in body2 && body2.current) {
1090
- rb1 = world.getRigidBody(body1.current.handle);
1091
- rb2 = world.getRigidBody(body2.current.handle);
1092
- const newJoint = world.createImpulseJoint(params, rb1, rb2);
1093
- jointRef.current = newJoint;
1094
- }
1311
+ for (i = 0; i < sourceSymbolKeys.length; i++) {
1312
+ key = sourceSymbolKeys[i];
1313
+ if (excluded.indexOf(key) >= 0) continue;
1314
+ if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
1315
+ target[key] = source[key];
1095
1316
  }
1317
+ }
1096
1318
 
1097
- return jointRef.current;
1098
- });
1099
- useEffect(() => {
1100
- const joint = getJointRef.current();
1101
- return () => {
1102
- if (joint) {
1103
- world.removeImpulseJoint(joint);
1104
- jointRef.current = undefined;
1105
- }
1106
- };
1107
- }, []);
1108
- const api = useMemo(() => createJointApi(getJointRef), []);
1109
- return api;
1110
- };
1111
- /**
1112
- *
1113
- * A fixed joint ensures that two rigid-bodies don't move relative to each other.
1114
- * Fixed joints are characterized by one local frame (represented by an isometry) on each rigid-body.
1115
- * The fixed-joint makes these frames coincide in world-space.
1116
- */
1117
-
1118
- const useFixedJoint = (body1, body2, [body1Anchor, body1LocalFrame, body2Anchor, body2LocalFrame]) => {
1119
- const {
1120
- rapier
1121
- } = useRapier();
1122
- return useImpulseJoint(body1, body2, rapier.JointData.fixed(vectorArrayToVector3(body1Anchor), _objectSpread2(_objectSpread2({}, vectorArrayToVector3(body1LocalFrame)), {}, {
1123
- w: 1
1124
- }), vectorArrayToVector3(body2Anchor), _objectSpread2(_objectSpread2({}, vectorArrayToVector3(body2LocalFrame)), {}, {
1125
- w: 1
1126
- })));
1127
- };
1128
- /**
1129
- * The spherical joint ensures that two points on the local-spaces of two rigid-bodies always coincide (it prevents any relative
1130
- * translational motion at this points). This is typically used to simulate ragdolls arms, pendulums, etc.
1131
- * They are characterized by one local anchor on each rigid-body. Each anchor represents the location of the
1132
- * points that need to coincide on the local-space of each rigid-body.
1133
- */
1134
-
1135
- const useSphericalJoint = (body1, body2, [body1Anchor, body2Anchor]) => {
1136
- const {
1137
- rapier
1138
- } = useRapier();
1139
- return useImpulseJoint(body1, body2, rapier.JointData.spherical(vectorArrayToVector3(body1Anchor), vectorArrayToVector3(body2Anchor)));
1140
- };
1141
- /**
1142
- * The revolute joint prevents any relative movement between two rigid-bodies, except for relative
1143
- * rotations along one axis. This is typically used to simulate wheels, fans, etc.
1144
- * They are characterized by one local anchor as well as one local axis on each rigid-body.
1145
- */
1146
-
1147
- const useRevoluteJoint = (body1, body2, [body1Anchor, body2Anchor, axis]) => {
1148
- const {
1149
- rapier
1150
- } = useRapier();
1151
- return useImpulseJoint(body1, body2, rapier.JointData.revolute(vectorArrayToVector3(body1Anchor), vectorArrayToVector3(body2Anchor), vectorArrayToVector3(axis)));
1152
- };
1153
- /**
1154
- * The prismatic joint prevents any relative movement between two rigid-bodies, except for relative translations along one axis.
1155
- * It is characterized by one local anchor as well as one local axis on each rigid-body. In 3D, an optional
1156
- * local tangent axis can be specified for each rigid-body.
1157
- */
1158
-
1159
- const usePrismaticJoint = (body1, body2, [body1Anchor, body2Anchor, axis]) => {
1160
- const {
1161
- rapier
1162
- } = useRapier();
1163
- return useImpulseJoint(body1, body2, rapier.JointData.prismatic(vectorArrayToVector3(body1Anchor), vectorArrayToVector3(body2Anchor), vectorArrayToVector3(axis)));
1164
- };
1319
+ return target;
1320
+ }
1165
1321
 
1166
1322
  // Colliders
1167
1323
  const AnyCollider = /*#__PURE__*/memo( /*#__PURE__*/React.forwardRef((props, forwardedRef) => {
@@ -1170,7 +1326,8 @@ const AnyCollider = /*#__PURE__*/memo( /*#__PURE__*/React.forwardRef((props, for
1170
1326
  position,
1171
1327
  rotation,
1172
1328
  quaternion,
1173
- scale
1329
+ scale,
1330
+ name
1174
1331
  } = props;
1175
1332
  const {
1176
1333
  world,
@@ -1230,7 +1387,8 @@ const AnyCollider = /*#__PURE__*/memo( /*#__PURE__*/React.forwardRef((props, for
1230
1387
  rotation: rotation,
1231
1388
  quaternion: quaternion,
1232
1389
  scale: scale,
1233
- ref: ref
1390
+ ref: ref,
1391
+ name: name
1234
1392
  }, children);
1235
1393
  }));
1236
1394
  const CuboidCollider = /*#__PURE__*/React.forwardRef((props, ref) => {
@@ -1287,6 +1445,15 @@ const ConvexHullCollider = /*#__PURE__*/React.forwardRef((props, ref) => {
1287
1445
  ref: ref
1288
1446
  }));
1289
1447
  });
1448
+ CuboidCollider.displayName = "CuboidCollider";
1449
+ RoundCuboidCollider.displayName = "RoundCuboidCollider";
1450
+ BallCollider.displayName = "BallCollider";
1451
+ CapsuleCollider.displayName = "CapsuleCollider";
1452
+ HeightfieldCollider.displayName = "HeightfieldCollider";
1453
+ TrimeshCollider.displayName = "TrimeshCollider";
1454
+ ConeCollider.displayName = "ConeCollider";
1455
+ CylinderCollider.displayName = "CylinderCollider";
1456
+ ConvexHullCollider.displayName = "ConvexHullCollider";
1290
1457
 
1291
1458
  const _excluded$1 = ["children", "type", "position", "rotation", "scale", "quaternion"];
1292
1459
  const RigidBodyContext = /*#__PURE__*/createContext(undefined);
@@ -1322,6 +1489,7 @@ const RigidBody = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props, ref) => {
1322
1489
  key: index
1323
1490
  }, colliderProps)))));
1324
1491
  }));
1492
+ RigidBody.displayName = "RigidBody";
1325
1493
 
1326
1494
  const MeshCollider = props => {
1327
1495
  const {
@@ -1352,26 +1520,96 @@ const MeshCollider = props => {
1352
1520
  key: index
1353
1521
  }, colliderProps))));
1354
1522
  };
1523
+ MeshCollider.displayName = "MeshCollider";
1524
+
1525
+ function mapsEqual(map1, map2) {
1526
+ var testVal;
1527
+
1528
+ if (map1.size !== map2.size) {
1529
+ return false;
1530
+ }
1531
+
1532
+ for (var [key, val] of map1) {
1533
+ testVal = map2.get(key);
1534
+
1535
+ if (testVal !== val || testVal === undefined && !map2.has(key)) {
1536
+ return false;
1537
+ }
1538
+ }
1539
+
1540
+ return true;
1541
+ }
1542
+
1543
+ const AttractorHelper = props => {
1544
+ const {
1545
+ scene
1546
+ } = useThree();
1547
+ const ref = useRef(null);
1548
+ const normalsHelper = useRef();
1549
+ const color = props.strength > 0 ? 0x0000ff : 0xff0000;
1550
+ useEffect(() => {
1551
+ if (ref.current) {
1552
+ normalsHelper.current = new VertexNormalsHelper(ref.current, props.range, color);
1553
+ normalsHelper.current.frustumCulled = false;
1554
+ scene.add(normalsHelper.current);
1555
+ }
1556
+
1557
+ return () => {
1558
+ if (normalsHelper.current) {
1559
+ scene.remove(normalsHelper.current);
1560
+ }
1561
+ };
1562
+ }, [props]);
1563
+ useFrame(() => {
1564
+ if (ref.current) {
1565
+ var _normalsHelper$curren;
1566
+
1567
+ const worldPosition = props.object.getWorldPosition(_vector3);
1568
+ ref.current.position.copy(worldPosition);
1569
+ (_normalsHelper$curren = normalsHelper.current) === null || _normalsHelper$curren === void 0 ? void 0 : _normalsHelper$curren.update();
1570
+ }
1571
+ });
1572
+ return /*#__PURE__*/React.createElement("mesh", {
1573
+ ref: ref,
1574
+ position: props.object.position,
1575
+ frustumCulled: false
1576
+ }, /*#__PURE__*/React.createElement("sphereGeometry", {
1577
+ args: [0.2, 6, 6]
1578
+ }), /*#__PURE__*/React.createElement("meshBasicMaterial", {
1579
+ color: color,
1580
+ wireframe: true
1581
+ }));
1582
+ };
1355
1583
 
1356
1584
  const Debug = () => {
1357
1585
  const {
1358
- world
1586
+ world,
1587
+ attractorStates
1359
1588
  } = useRapier();
1360
1589
  const ref = useRef(null);
1590
+ const [attractors, setAttractors] = useState([]);
1591
+ const currMap = useRef(new Map());
1361
1592
  useFrame(() => {
1362
1593
  const mesh = ref.current;
1363
1594
  if (!mesh) return;
1364
1595
  const buffers = world.debugRender();
1365
1596
  mesh.geometry.setAttribute("position", new BufferAttribute(buffers.vertices, 3));
1366
- mesh.geometry.setAttribute("color", new BufferAttribute(buffers.colors, 4));
1597
+ mesh.geometry.setAttribute("color", new BufferAttribute(buffers.colors, 4)); // Update attractors
1598
+
1599
+ if (!mapsEqual(currMap.current, attractorStates)) {
1600
+ setAttractors([...attractorStates.values()]);
1601
+ currMap.current = new Map(attractorStates);
1602
+ }
1367
1603
  });
1368
- return /*#__PURE__*/React.createElement("lineSegments", {
1604
+ return /*#__PURE__*/React.createElement("group", null, /*#__PURE__*/React.createElement("lineSegments", {
1369
1605
  ref: ref,
1370
1606
  frustumCulled: false
1371
1607
  }, /*#__PURE__*/React.createElement("lineBasicMaterial", {
1372
1608
  color: 0xffffff,
1373
1609
  vertexColors: true
1374
- }), /*#__PURE__*/React.createElement("bufferGeometry", null));
1610
+ }), /*#__PURE__*/React.createElement("bufferGeometry", null)), attractors.map((attractor, i) => /*#__PURE__*/React.createElement(AttractorHelper, _extends({
1611
+ key: attractor.object.uuid
1612
+ }, attractor))));
1375
1613
  };
1376
1614
 
1377
1615
  const _excluded = ["positions", "rotations", "children"];
@@ -1484,6 +1722,7 @@ const InstancedRigidBodies = /*#__PURE__*/forwardRef((props, ref) => {
1484
1722
  key: index
1485
1723
  }, colliderProps)))));
1486
1724
  });
1725
+ InstancedRigidBodies.displayName = "InstancedRigidBodies";
1487
1726
 
1488
1727
  /**
1489
1728
  * Calculates an InteractionGroup bitmask for use in the `collisionGroups` or `solverGroups`
@@ -1521,4 +1760,4 @@ const interactionGroups = (memberships, filters) => (bitmask(memberships) << 16)
1521
1760
 
1522
1761
  const bitmask = groups => [groups].flat().reduce((acc, layer) => acc | 1 << layer, 0);
1523
1762
 
1524
- export { AnyCollider, BallCollider, CapsuleCollider, ConeCollider, ConvexHullCollider, CuboidCollider, CylinderCollider, Debug, HeightfieldCollider, InstancedRigidBodies, MeshCollider, Physics, RigidBody, RoundCuboidCollider, TrimeshCollider, interactionGroups, useChildColliderProps, useFixedJoint, useImpulseJoint, usePrismaticJoint, useRapier, useRevoluteJoint, useRigidBody, useSphericalJoint };
1763
+ export { AnyCollider, Attractor, BallCollider, CapsuleCollider, ConeCollider, ConvexHullCollider, CuboidCollider, CylinderCollider, Debug, HeightfieldCollider, InstancedRigidBodies, MeshCollider, Physics, RigidBody, RoundCuboidCollider, TrimeshCollider, interactionGroups, useChildColliderProps, useFixedJoint, useImpulseJoint, usePrismaticJoint, useRapier, useRevoluteJoint, useRigidBody, useSphericalJoint };