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.
- package/LICENSE +9 -0
- package/README.md +166 -0
- package/dist/index.cjs.js +31 -0
- package/dist/index.d.ts +1191 -0
- package/dist/index.esm.js +1667 -0
- package/package.json +53 -0
- package/src/addons/attractor/Attractor.tsx +178 -0
- package/src/addons/attractor/AttractorDebugHelper.tsx +59 -0
- package/src/components/AnyCollider.tsx +286 -0
- package/src/components/Debug.tsx +33 -0
- package/src/components/FrameStepper.tsx +36 -0
- package/src/components/InstancedRigidBodies.tsx +135 -0
- package/src/components/MeshCollider.tsx +53 -0
- package/src/components/Physics.tsx +894 -0
- package/src/components/RigidBody.tsx +153 -0
- package/src/hooks/hooks.ts +211 -0
- package/src/hooks/joints.ts +221 -0
- package/src/hooks/use-forwarded-ref.ts +19 -0
- package/src/hooks/use-imperative-instance.ts +33 -0
- package/src/index.ts +55 -0
- package/src/types.ts +541 -0
- package/src/utils/interaction-groups.ts +43 -0
- package/src/utils/shared-objects.ts +10 -0
- package/src/utils/singleton-proxy.ts +51 -0
- package/src/utils/three-object-helpers.ts +25 -0
- package/src/utils/utils-collider.ts +491 -0
- package/src/utils/utils-physics.ts +26 -0
- package/src/utils/utils-rigidbody.ts +231 -0
- package/src/utils/utils.ts +105 -0
|
@@ -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
|
+
}
|