react-three-rapier-unified 1.0.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.
@@ -0,0 +1,231 @@
1
+ import { RigidBody, RigidBodyDesc } from "@dimforge/rapier3d-compat";
2
+ import { useEffect, useMemo } from "react";
3
+ import { Matrix4, Object3D, Vector3 } from "three";
4
+ import { Boolean3Tuple, RigidBodyProps, Vector3Tuple } from "..";
5
+ import {
6
+ EventMap,
7
+ RigidBodyState,
8
+ RigidBodyStateMap
9
+ } from "../components/Physics";
10
+ import {
11
+ _matrix4,
12
+ _position,
13
+ _rotation,
14
+ _scale,
15
+ _vector3
16
+ } from "./shared-objects";
17
+ import { rigidBodyTypeFromString, vectorToTuple } from "./utils";
18
+
19
+ export const rigidBodyDescFromOptions = (options: RigidBodyProps) => {
20
+ const type = rigidBodyTypeFromString(options?.type || "dynamic");
21
+
22
+ const desc = new RigidBodyDesc(type);
23
+
24
+ // Apply immutable options
25
+ desc.canSleep = options?.canSleep ?? true;
26
+
27
+ return desc;
28
+ };
29
+
30
+ interface CreateRigidBodyStateOptions {
31
+ object: Object3D;
32
+ rigidBody: RigidBody;
33
+ setMatrix?: (matrix: Matrix4) => void;
34
+ getMatrix?: (matrix: Matrix4) => Matrix4;
35
+ worldScale?: Vector3;
36
+ meshType?: RigidBodyState["meshType"];
37
+ }
38
+
39
+ export const createRigidBodyState = ({
40
+ rigidBody,
41
+ object,
42
+ setMatrix,
43
+ getMatrix,
44
+ worldScale,
45
+ meshType = "mesh"
46
+ }: CreateRigidBodyStateOptions): RigidBodyState => {
47
+ object.updateWorldMatrix(true, false);
48
+ const invertedWorldMatrix = object.parent!.matrixWorld.clone().invert();
49
+
50
+ return {
51
+ object,
52
+ rigidBody,
53
+ invertedWorldMatrix,
54
+ setMatrix: setMatrix
55
+ ? setMatrix
56
+ : (matrix: Matrix4) => {
57
+ object.matrix.copy(matrix);
58
+ },
59
+ getMatrix: getMatrix
60
+ ? getMatrix
61
+ : (matrix: Matrix4) => matrix.copy(object.matrix),
62
+ scale: worldScale || object.getWorldScale(_scale).clone(),
63
+ isSleeping: false,
64
+ meshType
65
+ };
66
+ };
67
+
68
+ type ImmutableRigidBodyOptions = (keyof RigidBodyProps)[];
69
+
70
+ export const immutableRigidBodyOptions: ImmutableRigidBodyOptions = [
71
+ "args",
72
+ "colliders",
73
+ "canSleep"
74
+ ];
75
+
76
+ type MutableRigidBodyOptions = {
77
+ [Prop in keyof RigidBodyProps]: (rb: RigidBody, value: any) => void;
78
+ };
79
+
80
+ export const mutableRigidBodyOptions: MutableRigidBodyOptions = {
81
+ gravityScale: (rb: RigidBody, value: number) => {
82
+ rb.setGravityScale(value, true);
83
+ },
84
+ additionalSolverIterations(rb: RigidBody, value: number) {
85
+ rb.setAdditionalSolverIterations(value);
86
+ },
87
+ linearDamping: (rb: RigidBody, value: number) => {
88
+ rb.setLinearDamping(value);
89
+ },
90
+ angularDamping: (rb: RigidBody, value: number) => {
91
+ rb.setAngularDamping(value);
92
+ },
93
+ dominanceGroup: (rb: RigidBody, value: number) => {
94
+ rb.setDominanceGroup(value);
95
+ },
96
+ enabledRotations: (rb: RigidBody, [x, y, z]: Boolean3Tuple) => {
97
+ rb.setEnabledRotations(x, y, z, true);
98
+ },
99
+ enabledTranslations: (rb: RigidBody, [x, y, z]: Boolean3Tuple) => {
100
+ rb.setEnabledTranslations(x, y, z, true);
101
+ },
102
+ lockRotations: (rb: RigidBody, value: boolean) => {
103
+ rb.lockRotations(value, true);
104
+ },
105
+ lockTranslations: (rb: RigidBody, value: boolean) => {
106
+ rb.lockTranslations(value, true);
107
+ },
108
+ angularVelocity: (rb: RigidBody, [x, y, z]: Vector3Tuple) => {
109
+ rb.setAngvel({ x, y, z }, true);
110
+ },
111
+ linearVelocity: (rb: RigidBody, [x, y, z]: Vector3Tuple) => {
112
+ rb.setLinvel({ x, y, z }, true);
113
+ },
114
+ ccd: (rb: RigidBody, value: boolean) => {
115
+ rb.enableCcd(value);
116
+ },
117
+ softCcdPrediction: (rb: RigidBody, value: number) => {
118
+ rb.setSoftCcdPrediction(value);
119
+ },
120
+ userData: (rb: RigidBody, value: { [key: string]: any }) => {
121
+ rb.userData = value;
122
+ },
123
+ type(rb, value) {
124
+ rb.setBodyType(rigidBodyTypeFromString(value), true);
125
+ },
126
+ position: () => {},
127
+ rotation: () => {},
128
+ quaternion: () => {},
129
+ scale: () => {}
130
+ };
131
+
132
+ const mutableRigidBodyOptionKeys = Object.keys(mutableRigidBodyOptions);
133
+
134
+ export const setRigidBodyOptions = (
135
+ rigidBody: RigidBody,
136
+ options: RigidBodyProps,
137
+ states: RigidBodyStateMap,
138
+ updateTranslations: boolean = true
139
+ ) => {
140
+ if (!rigidBody) {
141
+ return;
142
+ }
143
+
144
+ const state = states.get(rigidBody.handle);
145
+
146
+ if (state) {
147
+ if (updateTranslations) {
148
+ state.object.updateWorldMatrix(true, false);
149
+
150
+ _matrix4
151
+ .copy(state.object.matrixWorld)
152
+ .decompose(_position, _rotation, _scale);
153
+
154
+ rigidBody.setTranslation(_position, false);
155
+ rigidBody.setRotation(_rotation, false);
156
+ }
157
+
158
+ mutableRigidBodyOptionKeys.forEach((key) => {
159
+ if (key in options) {
160
+ mutableRigidBodyOptions[key as keyof RigidBodyProps]!(
161
+ rigidBody,
162
+ options[key as keyof RigidBodyProps]
163
+ );
164
+ }
165
+ });
166
+ }
167
+ };
168
+
169
+ export const useUpdateRigidBodyOptions = (
170
+ getRigidBody: () => RigidBody,
171
+ props: RigidBodyProps,
172
+ states: RigidBodyStateMap,
173
+ updateTranslations: boolean = true
174
+ ) => {
175
+ // TODO: Improve this, split each prop into its own effect
176
+ const mutablePropsAsFlatArray = useMemo(
177
+ () =>
178
+ mutableRigidBodyOptionKeys.flatMap((key) => {
179
+ return vectorToTuple(props[key as keyof RigidBodyProps]);
180
+ }),
181
+ [props]
182
+ );
183
+
184
+ useEffect(() => {
185
+ const rigidBody = getRigidBody();
186
+ setRigidBodyOptions(rigidBody, props, states, updateTranslations);
187
+ }, mutablePropsAsFlatArray);
188
+ };
189
+
190
+ export const useRigidBodyEvents = (
191
+ getRigidBody: () => RigidBody,
192
+ props: RigidBodyProps,
193
+ events: EventMap
194
+ ) => {
195
+ const {
196
+ onWake,
197
+ onSleep,
198
+ onCollisionEnter,
199
+ onCollisionExit,
200
+ onIntersectionEnter,
201
+ onIntersectionExit,
202
+ onContactForce
203
+ } = props;
204
+
205
+ const eventHandlers = {
206
+ onWake,
207
+ onSleep,
208
+ onCollisionEnter,
209
+ onCollisionExit,
210
+ onIntersectionEnter,
211
+ onIntersectionExit,
212
+ onContactForce
213
+ };
214
+
215
+ useEffect(() => {
216
+ const rigidBody = getRigidBody();
217
+ events.set(rigidBody.handle, eventHandlers);
218
+
219
+ return () => {
220
+ events.delete(rigidBody.handle);
221
+ };
222
+ }, [
223
+ onWake,
224
+ onSleep,
225
+ onCollisionEnter,
226
+ onCollisionExit,
227
+ onIntersectionEnter,
228
+ onIntersectionExit,
229
+ onContactForce
230
+ ]);
231
+ };
@@ -0,0 +1,105 @@
1
+ import { useRef } from "react";
2
+ import {
3
+ Quaternion as RapierQuaternion,
4
+ Vector3 as RapierVector3
5
+ } from "@dimforge/rapier3d-compat";
6
+ import { Euler, Quaternion, Vector3 } from "three";
7
+ import { _euler, _quaternion, _vector3 } from "./shared-objects";
8
+ import { RigidBodyTypeString, Vector3Tuple } from "../types";
9
+ import {
10
+ Vector3 as Vector3Like,
11
+ Quaternion as QuaternionLike
12
+ } from "@react-three/fiber";
13
+
14
+ export const vectorArrayToVector3 = (arr: Vector3Tuple) => {
15
+ const [x, y, z] = arr;
16
+ return new Vector3(x, y, z);
17
+ };
18
+
19
+ export const vector3ToQuaternion = (v: Vector3) => {
20
+ return _quaternion.setFromEuler(_euler.setFromVector3(v));
21
+ };
22
+
23
+ export const rapierVector3ToVector3 = ({ x, y, z }: RapierVector3) =>
24
+ _vector3.set(x, y, z);
25
+
26
+ export const rapierQuaternionToQuaternion = ({
27
+ x,
28
+ y,
29
+ z,
30
+ w
31
+ }: RapierQuaternion) => _quaternion.set(x, y, z, w);
32
+
33
+ export const vector3ToRapierVector = (v: Vector3Like) => {
34
+ if (Array.isArray(v)) {
35
+ return new RapierVector3(v[0], v[1], v[2]);
36
+ } else if (typeof v === "number") {
37
+ return new RapierVector3(v, v, v);
38
+ } else {
39
+ const threeVector3 = v as Vector3;
40
+ return new RapierVector3(threeVector3.x, threeVector3.y, threeVector3.z);
41
+ }
42
+ };
43
+
44
+ export const quaternionToRapierQuaternion = (v: QuaternionLike) => {
45
+ if (Array.isArray(v)) {
46
+ return new RapierQuaternion(v[0], v[1], v[2], v[3]);
47
+ } else {
48
+ return new RapierQuaternion(v.x, v.y, v.z, v.w);
49
+ }
50
+ };
51
+
52
+ const rigidBodyTypeMap = {
53
+ fixed: 1,
54
+ dynamic: 0,
55
+ kinematicPosition: 2,
56
+ kinematicVelocity: 3
57
+ } as const;
58
+
59
+ export const rigidBodyTypeFromString = (type: RigidBodyTypeString) =>
60
+ rigidBodyTypeMap[type];
61
+
62
+ export const scaleVertices = (vertices: ArrayLike<number>, scale: Vector3) => {
63
+ const scaledVerts = Array.from(vertices);
64
+
65
+ for (let i = 0; i < vertices.length / 3; i++) {
66
+ scaledVerts[i * 3] *= scale.x;
67
+ scaledVerts[i * 3 + 1] *= scale.y;
68
+ scaledVerts[i * 3 + 2] *= scale.z;
69
+ }
70
+
71
+ return scaledVerts;
72
+ };
73
+
74
+ export const vectorToTuple = (
75
+ v: Vector3 | Quaternion | any[] | undefined | number | Euler
76
+ ) => {
77
+ if (!v) return [0];
78
+
79
+ if (v instanceof Quaternion) {
80
+ return [v.x, v.y, v.z, v.w];
81
+ }
82
+
83
+ if (v instanceof Vector3 || v instanceof Euler) {
84
+ return [v.x, v.y, v.z];
85
+ }
86
+
87
+ if (Array.isArray(v)) {
88
+ return v;
89
+ }
90
+
91
+ return [v];
92
+ };
93
+
94
+ export function useConst<T>(initialValue: T | (() => T)): T {
95
+ const ref = useRef<{ value: T }>(undefined);
96
+ if (ref.current === undefined) {
97
+ ref.current = {
98
+ value:
99
+ typeof initialValue === "function"
100
+ ? (initialValue as Function)()
101
+ : initialValue
102
+ };
103
+ }
104
+ return ref.current.value;
105
+ }