@threlte/rapier 1.1.2 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,6 +6,8 @@ import { useCollisionGroups } from '../../hooks/useCollisionGroups';
6
6
  import { useHasEventListeners } from '../../hooks/useHasEventListener';
7
7
  import { useRapier } from '../../hooks/useRapier';
8
8
  import { useRigidBody } from '../../hooks/useRigidBody';
9
+ import { useCreateEvent } from '../../lib/useCreateEvent';
10
+ import { useParentRigidbodyObject } from '../../lib/rigidBodyObjectContext';
9
11
  import { applyColliderActiveEvents } from '../../lib/applyColliderActiveEvents';
10
12
  import { createCollidersFromChildren } from '../../lib/createCollidersFromChildren';
11
13
  import { eulerToQuaternion } from '../../lib/eulerToQuaternion';
@@ -22,7 +24,9 @@ export let centerOfMass = undefined;
22
24
  export let principalAngularInertia = undefined;
23
25
  export let angularInertiaLocalFrame = undefined;
24
26
  const group = new Group();
27
+ const { updateRef } = useCreateEvent();
25
28
  const rigidBody = useRigidBody();
29
+ const rigidBodyParentObject = useParentRigidbodyObject();
26
30
  const { world, addColliderToContext, removeColliderFromContext } = useRapier();
27
31
  export let colliders = [];
28
32
  const collisionGroups = useCollisionGroups();
@@ -38,7 +42,7 @@ const cleanup = () => {
38
42
  };
39
43
  export const create = () => {
40
44
  cleanup();
41
- colliders = createCollidersFromChildren(group, shape ?? 'convexHull', world, rigidBody);
45
+ colliders = createCollidersFromChildren(group, shape ?? 'convexHull', world, rigidBody, rigidBodyParentObject);
42
46
  colliders.forEach((c) => addColliderToContext(c, group, dispatcher));
43
47
  collisionGroups.registerColliders(colliders);
44
48
  colliders.forEach((collider) => {
@@ -63,6 +67,7 @@ export const create = () => {
63
67
  collider.setMass(mass);
64
68
  }
65
69
  });
70
+ updateRef(colliders);
66
71
  };
67
72
  onMount(() => {
68
73
  create();
@@ -6,6 +6,8 @@ import { useCollisionGroups } from '../../hooks/useCollisionGroups';
6
6
  import { useHasEventListeners } from '../../hooks/useHasEventListener';
7
7
  import { useRapier } from '../../hooks/useRapier';
8
8
  import { useRigidBody } from '../../hooks/useRigidBody';
9
+ import { useParentRigidbodyObject } from '../../lib/rigidBodyObjectContext';
10
+ import { useCreateEvent } from '../../lib/useCreateEvent';
9
11
  import { applyColliderActiveEvents } from '../../lib/applyColliderActiveEvents';
10
12
  import { eulerToQuaternion } from '../../lib/eulerToQuaternion';
11
13
  import { getWorldPosition, getWorldQuaternion } from '../../lib/getWorldTransforms';
@@ -25,7 +27,9 @@ export let centerOfMass = undefined;
25
27
  export let principalAngularInertia = undefined;
26
28
  export let angularInertiaLocalFrame = undefined;
27
29
  const object = new Object3D();
30
+ const { updateRef } = useCreateEvent();
28
31
  const rigidBody = useRigidBody();
32
+ const parentRigidBodyObject = useParentRigidbodyObject();
29
33
  const hasRigidBodyParent = !!rigidBody;
30
34
  const rapierContext = useRapier();
31
35
  const { world } = rapierContext;
@@ -43,6 +47,8 @@ onMount(async () => {
43
47
  // @ts-ignore
44
48
  const colliderDesc = ColliderDesc[shape](...scaledArgs);
45
49
  collider = world.createCollider(colliderDesc, rigidBody);
50
+ collider.setActiveCollisionTypes(ActiveCollisionTypes.ALL);
51
+ updateRef(collider);
46
52
  /**
47
53
  * Add collider to context
48
54
  */
@@ -54,13 +60,9 @@ onMount(async () => {
54
60
  if (hasRigidBodyParent) {
55
61
  const rigidBodyWorldPos = new Vector3();
56
62
  const rigidBodyWorldQuatInversed = new Quaternion();
57
- object.traverseAncestors((child) => {
58
- if (child.userData.isRigidBody) {
59
- child.getWorldPosition(rigidBodyWorldPos);
60
- child.getWorldQuaternion(rigidBodyWorldQuatInversed);
61
- rigidBodyWorldQuatInversed.invert();
62
- }
63
- });
63
+ parentRigidBodyObject?.getWorldPosition(rigidBodyWorldPos);
64
+ parentRigidBodyObject?.getWorldQuaternion(rigidBodyWorldQuatInversed);
65
+ rigidBodyWorldQuatInversed.invert();
64
66
  const worldPosition = object.getWorldPosition(new Vector3()).sub(rigidBodyWorldPos);
65
67
  const worldRotation = object
66
68
  .getWorldQuaternion(new Quaternion())
@@ -74,33 +76,29 @@ onMount(async () => {
74
76
  }
75
77
  });
76
78
  const { hasEventListeners: colliderHasEventListeners } = useHasEventListeners();
77
- $: {
78
- if (collider) {
79
- applyColliderActiveEvents(collider, colliderHasEventListeners, rigidBody?.userData?.hasEventListeners);
80
- collider.setActiveCollisionTypes(ActiveCollisionTypes.ALL);
81
- collider.setRestitution(restitution ?? 0);
82
- collider.setContactForceEventThreshold(1);
83
- collider.setRestitutionCombineRule(restitutionCombineRule ?? CoefficientCombineRule.Average);
84
- collider.setFriction(friction ?? 0.7);
85
- collider.setFrictionCombineRule(frictionCombineRule ?? CoefficientCombineRule.Average);
86
- collider.setSensor(sensor ?? false);
87
- collider.setContactForceEventThreshold(contactForceEventThreshold ?? 0);
88
- if (density)
89
- collider.setDensity(density);
90
- if (mass) {
91
- if (centerOfMass && principalAngularInertia && angularInertiaLocalFrame) {
92
- collider.setMassProperties(mass, { x: centerOfMass[0], y: centerOfMass[1], z: centerOfMass[2] }, {
93
- x: principalAngularInertia[0],
94
- y: principalAngularInertia[1],
95
- z: principalAngularInertia[2]
96
- }, eulerToQuaternion(angularInertiaLocalFrame));
97
- }
98
- else {
99
- collider.setMass(mass);
100
- }
101
- }
79
+ $: collider?.setRestitution(restitution ?? 0);
80
+ $: collider?.setRestitutionCombineRule(restitutionCombineRule ?? CoefficientCombineRule.Average);
81
+ $: collider?.setFriction(friction ?? 0.7);
82
+ $: collider?.setFrictionCombineRule(frictionCombineRule ?? CoefficientCombineRule.Average);
83
+ $: collider?.setSensor(sensor ?? false);
84
+ $: collider?.setContactForceEventThreshold(contactForceEventThreshold ?? 0);
85
+ $: if (density)
86
+ collider?.setDensity(density);
87
+ $: if (collider && mass) {
88
+ if (centerOfMass && principalAngularInertia && angularInertiaLocalFrame) {
89
+ collider.setMassProperties(mass, { x: centerOfMass[0], y: centerOfMass[1], z: centerOfMass[2] }, {
90
+ x: principalAngularInertia[0],
91
+ y: principalAngularInertia[1],
92
+ z: principalAngularInertia[2]
93
+ }, eulerToQuaternion(angularInertiaLocalFrame));
94
+ }
95
+ else {
96
+ collider.setMass(mass);
102
97
  }
103
98
  }
99
+ $: if (collider) {
100
+ applyColliderActiveEvents(collider, colliderHasEventListeners, rigidBody?.userData?.hasEventListeners);
101
+ }
104
102
  export const refresh = () => {
105
103
  if (!collider)
106
104
  return;
@@ -3,12 +3,7 @@ import { onDestroy } from 'svelte';
3
3
  import { BufferAttribute, BufferGeometry } from 'three';
4
4
  import { useRapier } from '../../hooks/useRapier';
5
5
  const { world, debug } = useRapier();
6
- const buffers = world.debugRender();
7
- const vertices = new BufferAttribute(buffers.vertices, 3);
8
- const colors = new BufferAttribute(buffers.colors, 4);
9
6
  const geometry = new BufferGeometry();
10
- geometry.setAttribute('position', vertices);
11
- geometry.setAttribute('color', colors);
12
7
  debug.set(true);
13
8
  useFrame(() => {
14
9
  const buffers = world.debugRender();
@@ -18,16 +13,15 @@ useFrame(() => {
18
13
  geometry.setAttribute('color', colors);
19
14
  });
20
15
  onDestroy(() => {
21
- geometry.dispose();
22
16
  debug.set(false);
23
17
  });
24
18
  </script>
25
19
 
26
- <T.LineSegments renderOrder={Infinity}>
27
- <T
28
- is={geometry}
29
- attach="geometry"
30
- />
20
+ <T.LineSegments
21
+ frustumCulled={false}
22
+ renderOrder={Infinity}
23
+ >
24
+ <T is={geometry} />
31
25
  <T.LineBasicMaterial
32
26
  vertexColors
33
27
  {...$$restProps}
@@ -5,6 +5,8 @@ import { useHasEventListeners } from '../../hooks/useHasEventListener';
5
5
  import { useRapier } from '../../hooks/useRapier';
6
6
  import { getWorldPosition, getWorldQuaternion, getWorldScale } from '../../lib/getWorldTransforms';
7
7
  import { parseRigidBodyType } from '../../lib/parseRigidBodyType';
8
+ import { setParentRigidbodyObject } from '../../lib/rigidBodyObjectContext';
9
+ import { useCreateEvent } from '../../lib/useCreateEvent';
8
10
  const { world, rapier, addRigidBodyToContext, removeRigidBodyFromContext } = useRapier();
9
11
  export let linearVelocity = undefined;
10
12
  export let angularVelocity = undefined;
@@ -22,11 +24,8 @@ export let dominance = 0;
22
24
  export let enabled = true;
23
25
  export let userData = {};
24
26
  const dispatcher = createRawEventDispatcher();
27
+ const { updateRef } = useCreateEvent();
25
28
  const object = new Object3D();
26
- /**
27
- * Used to traverseAncestors to restore transform
28
- */
29
- object.userData.isRigidBody = true;
30
29
  /**
31
30
  * isSleeping used for events "sleep" and "wake" in `useFrameHandler`
32
31
  */
@@ -56,6 +55,7 @@ const initPosition = async () => {
56
55
  rigidBodyTemp.setTranslation(worldPosition, true);
57
56
  rigidBodyTemp.setRotation(worldQuaternion, true);
58
57
  rigidBody = rigidBodyTemp;
58
+ updateRef(rigidBody);
59
59
  };
60
60
  initPosition();
61
61
  /**
@@ -93,6 +93,10 @@ $: rigidBodyTemp.userData = {
93
93
  * hook onto.
94
94
  */
95
95
  setContext('threlte-rapier-rigidbody', rigidBodyTemp);
96
+ /**
97
+ * Used by child colliders to restore transform
98
+ */
99
+ setParentRigidbodyObject(object);
96
100
  /**
97
101
  * Add the mesh to the context
98
102
  */
@@ -13,4 +13,4 @@ import type { AutoCollidersShapes } from '../types/types';
13
13
  * @param rigidBody
14
14
  * @returns
15
15
  */
16
- export declare const createCollidersFromChildren: (object: Object3D, collidersType: AutoCollidersShapes, world: World, rigidBody?: RigidBody) => Collider[];
16
+ export declare const createCollidersFromChildren: (object: Object3D, collidersType: AutoCollidersShapes, world: World, rigidBody?: RigidBody, rigidBodyParentObject?: Object3D) => Collider[];
@@ -1,5 +1,12 @@
1
1
  import { ActiveEvents, Collider, ColliderDesc, World, RigidBody } from '@dimforge/rapier3d-compat';
2
2
  import { Mesh, Quaternion, Vector3 } from 'three';
3
+ const offset = new Vector3();
4
+ const worldPosition = new Vector3();
5
+ const worldQuaternion = new Quaternion();
6
+ const worldScale = new Vector3();
7
+ const size = new Vector3();
8
+ const rigidBodyWorldPos = new Vector3();
9
+ const rigidBodyWorldQuatInversed = new Quaternion();
3
10
  /**
4
11
  *
5
12
  * Creates collider descriptions including default translations
@@ -12,39 +19,34 @@ import { Mesh, Quaternion, Vector3 } from 'three';
12
19
  * @param rigidBody
13
20
  * @returns
14
21
  */
15
- export const createCollidersFromChildren = (object, collidersType, world, rigidBody) => {
22
+ export const createCollidersFromChildren = (object, collidersType, world, rigidBody, rigidBodyParentObject) => {
16
23
  const colliders = [];
17
24
  let description;
18
- const offset = new Vector3();
19
25
  /**
20
26
  * Trying to find the parent RigidBody.
21
27
  * If we find something, good. If not,
22
28
  * the Colliders are created on the world positions
23
29
  * of the meshes they resemble.
24
30
  */
25
- const rigidBodyWorldPos = new Vector3();
26
- const rigidBodyWorldQuatInversed = new Quaternion();
27
- object.traverseAncestors((child) => {
28
- if (child.userData.isRigidBody) {
29
- child.getWorldPosition(rigidBodyWorldPos);
30
- child.getWorldQuaternion(rigidBodyWorldQuatInversed);
31
- rigidBodyWorldQuatInversed.invert();
32
- }
33
- });
31
+ rigidBodyWorldPos.set(0, 0, 0);
32
+ rigidBodyWorldQuatInversed.set(0, 0, 0, 1);
33
+ rigidBodyParentObject?.getWorldPosition(rigidBodyWorldPos);
34
+ rigidBodyParentObject?.getWorldQuaternion(rigidBodyWorldQuatInversed).invert();
34
35
  object.traverse((child) => {
35
36
  if ('isMesh' in child) {
36
37
  const { geometry } = child;
37
- const worldPos = child.getWorldPosition(new Vector3());
38
- const { x, y, z } = worldPos.sub(rigidBodyWorldPos);
39
- const worldQuat = child.getWorldQuaternion(new Quaternion());
40
- const { x: rx, y: ry, z: rz, w: rw } = worldQuat.clone().premultiply(rigidBodyWorldQuatInversed);
41
- const scale = child.getWorldScale(new Vector3());
38
+ const worldPos = child.getWorldPosition(worldPosition);
39
+ const translation = worldPos.sub(rigidBodyWorldPos);
40
+ const worldQuat = child.getWorldQuaternion(worldQuaternion);
41
+ const rotation = worldQuat.clone().premultiply(rigidBodyWorldQuatInversed);
42
+ const scale = child.getWorldScale(worldScale);
43
+ offset.set(0, 0, 0);
42
44
  switch (collidersType) {
43
45
  case 'cuboid':
44
46
  {
45
47
  geometry.computeBoundingBox();
46
48
  const { boundingBox } = geometry;
47
- const size = boundingBox.getSize(new Vector3());
49
+ boundingBox.getSize(size);
48
50
  boundingBox.getCenter(offset);
49
51
  description = ColliderDesc.cuboid((size.x / 2) * scale.x, (size.y / 2) * scale.y, (size.z / 2) * scale.z);
50
52
  }
@@ -60,31 +62,28 @@ export const createCollidersFromChildren = (object, collidersType, world, rigidB
60
62
  break;
61
63
  case 'trimesh':
62
64
  {
63
- const g = geometry.clone().scale(scale.x, scale.y, scale.z);
64
- description = ColliderDesc.trimesh(g.attributes.position.array, g.index?.array);
65
+ description = ColliderDesc.trimesh(new Float32Array(geometry.attributes.position.array), new Uint32Array(geometry.index?.array ?? []));
65
66
  }
66
67
  break;
67
68
  case 'capsule':
68
69
  {
69
70
  geometry.computeBoundingBox();
70
71
  const { boundingBox } = geometry;
71
- const size = boundingBox.getSize(new Vector3());
72
+ boundingBox.getSize(size);
72
73
  boundingBox.getCenter(offset);
73
74
  const radius = Math.max((size.x / 2) * scale.x, (size.z / 2) * scale.z);
74
75
  description = ColliderDesc.capsule((size.y / 2) * scale.y - radius, radius);
75
76
  }
76
77
  break;
77
78
  case 'convexHull':
78
- // eslint-disable-next-line no-case-declarations
79
- const g = geometry.clone().scale(scale.x, scale.y, scale.z);
80
79
  {
81
- description = ColliderDesc.convexHull(g.attributes.position.array);
80
+ description = ColliderDesc.convexHull(new Float32Array(geometry.attributes.position.array));
82
81
  }
83
82
  break;
84
83
  }
85
84
  description
86
- .setTranslation(x + offset.x, y + offset.y, z + offset.z)
87
- .setRotation({ x: rx, y: ry, z: rz, w: rw })
85
+ .setTranslation(translation.x + offset.x, translation.y + offset.y, translation.z + offset.z)
86
+ .setRotation(rotation)
88
87
  .setActiveEvents(ActiveEvents.COLLISION_EVENTS);
89
88
  const collider = world.createCollider(description, rigidBody);
90
89
  colliders.push(collider);
@@ -1,5 +1,4 @@
1
1
  import RAPIER from '@dimforge/rapier3d-compat';
2
- import { currentWritable } from '@threlte/core';
3
2
  import { readable, writable } from 'svelte/store';
4
3
  export const createRapierContext = (...args) => {
5
4
  const world = new RAPIER.World(...args);
@@ -0,0 +1,2 @@
1
+ export declare const useParentRigidbodyObject: () => THREE.Object3D | undefined;
2
+ export declare const setParentRigidbodyObject: (object3d: THREE.Object3D) => void;
@@ -0,0 +1,8 @@
1
+ import { getContext, setContext } from 'svelte';
2
+ const key = Symbol('threlte-rapier-rigidbody-object3d');
3
+ export const useParentRigidbodyObject = () => {
4
+ return getContext(key);
5
+ };
6
+ export const setParentRigidbodyObject = (object3d) => {
7
+ setContext(key, object3d);
8
+ };
@@ -0,0 +1,3 @@
1
+ export declare const useCreateEvent: <T>() => {
2
+ updateRef: (newRef: T) => void;
3
+ };
@@ -0,0 +1,31 @@
1
+ import { onDestroy } from 'svelte';
2
+ import { createRawEventDispatcher } from '@threlte/core';
3
+ export const useCreateEvent = () => {
4
+ const dispatchRaw = createRawEventDispatcher();
5
+ const cleanupFunctions = [];
6
+ let ref = undefined;
7
+ const dispatchCreateEvent = () => {
8
+ // call every cleanup function
9
+ cleanupFunctions.forEach((cleanup) => cleanup());
10
+ // clear the cleanup functions array
11
+ cleanupFunctions.length = 0;
12
+ const cleanup = (callback) => {
13
+ // add cleanup function to array
14
+ cleanupFunctions.push(callback);
15
+ };
16
+ if (ref === undefined)
17
+ return;
18
+ dispatchRaw('create', { ref, cleanup });
19
+ };
20
+ const updateRef = (newRef) => {
21
+ ref = newRef;
22
+ dispatchCreateEvent();
23
+ };
24
+ onDestroy(() => {
25
+ // call every cleanup function
26
+ cleanupFunctions.forEach((cleanup) => cleanup());
27
+ });
28
+ return {
29
+ updateRef
30
+ };
31
+ };
@@ -6,6 +6,10 @@ import type { createRapierContext } from '../lib/createRapierContext';
6
6
  export type ColliderShapes = 'ball' | 'capsule' | 'segment' | 'triangle' | 'roundTriangle' | 'polyline' | 'trimesh' | 'cuboid' | 'roundCuboid' | 'heightfield' | 'cylinder' | 'roundCylinder' | 'cone' | 'roundCone' | 'convexHull' | 'convexMesh' | 'roundConvexHull' | 'roundConvexMesh';
7
7
  export type AutoCollidersShapes = 'cuboid' | 'ball' | 'trimesh' | 'convexHull' | 'capsule';
8
8
  export type ColliderEventMap = {
9
+ create: {
10
+ ref: Collider;
11
+ cleanup: (callback: () => void) => void;
12
+ };
9
13
  collisionenter: {
10
14
  targetCollider: Collider;
11
15
  targetRigidBody: RigidBody | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@threlte/rapier",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "author": "Grischa Erbe <hello@legrisch.com> (https://legrisch.com)",
5
5
  "license": "MIT",
6
6
  "devDependencies": {
@@ -27,7 +27,7 @@
27
27
  "type-fest": "^2.13.0",
28
28
  "typescript": "^5.0.0",
29
29
  "vite": "^4.3.6",
30
- "@threlte/core": "6.0.7"
30
+ "@threlte/core": "6.1.1"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "svelte": ">=4",