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
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-three-rapier-unified",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Unified package combining @react-three/rapier and @react-three/rapier-addons",
|
|
5
|
+
"main": "dist/index.cjs.js",
|
|
6
|
+
"module": "dist/index.esm.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"source": "src/index.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"src"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc && vite build",
|
|
15
|
+
"dev": "vite build --watch",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"@react-three/fiber": "^9.0.4",
|
|
20
|
+
"react": "^19",
|
|
21
|
+
"three": ">=0.159.0"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@dimforge/rapier3d-compat": "0.19.3",
|
|
25
|
+
"suspend-react": "^0.1.3",
|
|
26
|
+
"three-stdlib": "^2.35.12"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@react-three/fiber": "9.0.4",
|
|
30
|
+
"@types/react": "19.0.8",
|
|
31
|
+
"@types/three": "0.172.0",
|
|
32
|
+
"react": "19.0.0",
|
|
33
|
+
"three": "0.172.0",
|
|
34
|
+
"typescript": "5.7.3",
|
|
35
|
+
"vite": "^6.0.0",
|
|
36
|
+
"vite-plugin-dts": "^4.0.0"
|
|
37
|
+
},
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/MMORPG-Lordson/react-three-rapier-unified.git"
|
|
41
|
+
},
|
|
42
|
+
"keywords": [
|
|
43
|
+
"react",
|
|
44
|
+
"three",
|
|
45
|
+
"threejs",
|
|
46
|
+
"physics",
|
|
47
|
+
"rapier",
|
|
48
|
+
"react-three-fiber",
|
|
49
|
+
"r3f"
|
|
50
|
+
],
|
|
51
|
+
"author": "",
|
|
52
|
+
"license": "MIT"
|
|
53
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { InteractionGroups, RigidBody } from "@dimforge/rapier3d-compat";
|
|
3
|
+
import { useBeforePhysicsStep, useRapier } from "../../hooks/hooks";
|
|
4
|
+
import { FC, memo, useRef } from "react";
|
|
5
|
+
import { Object3D, Vector3 } from "three";
|
|
6
|
+
import { ThreeElements } from "@react-three/fiber";
|
|
7
|
+
import { AttractorDebugHelper } from "./AttractorDebugHelper";
|
|
8
|
+
|
|
9
|
+
type Object3DProps = ThreeElements["object3D"];
|
|
10
|
+
|
|
11
|
+
export type AttractorGravityType = "static" | "linear" | "newtonian";
|
|
12
|
+
|
|
13
|
+
export interface AttractorProps {
|
|
14
|
+
/**
|
|
15
|
+
* The relative position of this attractor
|
|
16
|
+
*/
|
|
17
|
+
position?: Object3DProps["position"];
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The strength of the attractor.
|
|
21
|
+
* Positive values attract, negative values repel.
|
|
22
|
+
*
|
|
23
|
+
* @defaultValue 1
|
|
24
|
+
*/
|
|
25
|
+
strength?: number;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The range of the attractor. Will not affect objects outside of this range.
|
|
29
|
+
*
|
|
30
|
+
* @defaultValue 10
|
|
31
|
+
* @min 0
|
|
32
|
+
*/
|
|
33
|
+
range?: number;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The type of gravity to use.
|
|
37
|
+
* - static: The gravity is constant and does not change over time.
|
|
38
|
+
* - linear: The gravity is linearly interpolated the closer the object is to the attractor.
|
|
39
|
+
* - newtonian: The gravity is calculated using the newtonian gravity formula.
|
|
40
|
+
* @defaultValue "static"
|
|
41
|
+
*/
|
|
42
|
+
type?: AttractorGravityType;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The mass of the attractor. Used when type is `newtonian`.
|
|
46
|
+
* @defaultValue 6.673e-11
|
|
47
|
+
*/
|
|
48
|
+
gravitationalConstant?: number;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The collision groups that this attractor will apply effects to. If a RigidBody contains one or more colliders that are in one of the mask group, it will be affected by this attractor.
|
|
52
|
+
* If not specified, the attractor will apply effects to all RigidBodies.
|
|
53
|
+
*/
|
|
54
|
+
collisionGroups?: InteractionGroups;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface AttractorState
|
|
58
|
+
extends Required<Omit<AttractorProps, "position" | "collisionGroups">> {
|
|
59
|
+
collisionGroups?: InteractionGroups;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const calcForceByType = {
|
|
63
|
+
static: (s: number, m2: number, r: number, d: number, G: number) => s,
|
|
64
|
+
linear: (s: number, m2: number, r: number, d: number, G: number) =>
|
|
65
|
+
s * (d / r),
|
|
66
|
+
newtonian: (s: number, m2: number, r: number, d: number, G: number) =>
|
|
67
|
+
(G * s * m2) / Math.pow(d, 2)
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const _position = new Vector3();
|
|
71
|
+
const _vector3 = new Vector3();
|
|
72
|
+
|
|
73
|
+
export const applyAttractorForceOnRigidBody = (
|
|
74
|
+
rigidBody: RigidBody,
|
|
75
|
+
{
|
|
76
|
+
object,
|
|
77
|
+
strength,
|
|
78
|
+
range,
|
|
79
|
+
gravitationalConstant,
|
|
80
|
+
collisionGroups,
|
|
81
|
+
type
|
|
82
|
+
}: AttractorState & {
|
|
83
|
+
object: Object3D;
|
|
84
|
+
}
|
|
85
|
+
) => {
|
|
86
|
+
const rbPosition = rigidBody.translation();
|
|
87
|
+
_position.set(rbPosition.x, rbPosition.y, rbPosition.z);
|
|
88
|
+
|
|
89
|
+
const worldPosition = object.getWorldPosition(new Vector3());
|
|
90
|
+
|
|
91
|
+
const distance: number = worldPosition.distanceTo(_position);
|
|
92
|
+
|
|
93
|
+
if (distance < range) {
|
|
94
|
+
let force = calcForceByType[type](
|
|
95
|
+
strength,
|
|
96
|
+
rigidBody.mass(),
|
|
97
|
+
range,
|
|
98
|
+
distance,
|
|
99
|
+
gravitationalConstant
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// Prevent wild forces when Attractors collide
|
|
103
|
+
force = force === Infinity ? strength : force;
|
|
104
|
+
|
|
105
|
+
// Naively test if the rigidBody contains a collider in one of the collision groups
|
|
106
|
+
let isRigidBodyInCollisionGroup =
|
|
107
|
+
collisionGroups === undefined ? true : false;
|
|
108
|
+
if (collisionGroups !== undefined) {
|
|
109
|
+
for (let i = 0; i < rigidBody.numColliders(); i++) {
|
|
110
|
+
const collider = rigidBody.collider(i);
|
|
111
|
+
const colliderCollisionGroups = collider.collisionGroups();
|
|
112
|
+
if (
|
|
113
|
+
((collisionGroups >> 16) & colliderCollisionGroups) != 0 &&
|
|
114
|
+
((colliderCollisionGroups >> 16) & collisionGroups) != 0
|
|
115
|
+
) {
|
|
116
|
+
isRigidBodyInCollisionGroup = true;
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (isRigidBodyInCollisionGroup) {
|
|
123
|
+
_vector3
|
|
124
|
+
.set(0, 0, 0)
|
|
125
|
+
.subVectors(worldPosition, _position)
|
|
126
|
+
.normalize()
|
|
127
|
+
.multiplyScalar(force);
|
|
128
|
+
|
|
129
|
+
rigidBody.applyImpulse(_vector3, true);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export const Attractor: FC<AttractorProps> = memo((props) => {
|
|
135
|
+
const {
|
|
136
|
+
position = [0, 0, 0],
|
|
137
|
+
strength = 1,
|
|
138
|
+
range = 10,
|
|
139
|
+
type = "static",
|
|
140
|
+
gravitationalConstant = 6.673e-11,
|
|
141
|
+
collisionGroups
|
|
142
|
+
} = props;
|
|
143
|
+
const object = useRef<Object3D>(null!);
|
|
144
|
+
const { isDebug } = useRapier();
|
|
145
|
+
|
|
146
|
+
useBeforePhysicsStep((world) => {
|
|
147
|
+
if (object.current) {
|
|
148
|
+
world.bodies.forEach((body) => {
|
|
149
|
+
if (body.isDynamic()) {
|
|
150
|
+
applyAttractorForceOnRigidBody(body, {
|
|
151
|
+
object: object.current!,
|
|
152
|
+
strength,
|
|
153
|
+
range,
|
|
154
|
+
type,
|
|
155
|
+
gravitationalConstant,
|
|
156
|
+
collisionGroups
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
return (
|
|
164
|
+
<>
|
|
165
|
+
<object3D ref={object} position={position} />
|
|
166
|
+
{isDebug && (
|
|
167
|
+
<AttractorDebugHelper
|
|
168
|
+
strength={strength}
|
|
169
|
+
gravitationalConstant={gravitationalConstant}
|
|
170
|
+
range={range}
|
|
171
|
+
type={type}
|
|
172
|
+
collisionGroups={collisionGroups}
|
|
173
|
+
object={object}
|
|
174
|
+
/>
|
|
175
|
+
)}
|
|
176
|
+
</>
|
|
177
|
+
);
|
|
178
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import React, { RefObject, useEffect, useRef } from "react";
|
|
2
|
+
import { useThree, useFrame } from "@react-three/fiber";
|
|
3
|
+
import {
|
|
4
|
+
Mesh,
|
|
5
|
+
MeshBasicMaterial,
|
|
6
|
+
Object3D,
|
|
7
|
+
SphereGeometry,
|
|
8
|
+
Vector3
|
|
9
|
+
} from "three";
|
|
10
|
+
import { VertexNormalsHelper } from "three-stdlib";
|
|
11
|
+
import { AttractorState } from "./Attractor";
|
|
12
|
+
|
|
13
|
+
const _v3 = new Vector3();
|
|
14
|
+
|
|
15
|
+
export const AttractorDebugHelper = (
|
|
16
|
+
props: AttractorState & {
|
|
17
|
+
object: RefObject<Object3D>;
|
|
18
|
+
}
|
|
19
|
+
) => {
|
|
20
|
+
const { scene } = useThree();
|
|
21
|
+
const ref = useRef<Mesh>(null!);
|
|
22
|
+
const normalsHelper = useRef<VertexNormalsHelper>(null!);
|
|
23
|
+
const color = props.strength > 0 ? 0x0000ff : 0xff0000;
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
ref.current = new Mesh(
|
|
27
|
+
new SphereGeometry(0.2, 6, 6),
|
|
28
|
+
new MeshBasicMaterial({ color, wireframe: true })
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
normalsHelper.current = new VertexNormalsHelper(
|
|
32
|
+
ref.current,
|
|
33
|
+
props.range,
|
|
34
|
+
color
|
|
35
|
+
);
|
|
36
|
+
normalsHelper.current.frustumCulled = false;
|
|
37
|
+
|
|
38
|
+
scene.add(ref.current);
|
|
39
|
+
scene.add(normalsHelper.current);
|
|
40
|
+
|
|
41
|
+
return () => {
|
|
42
|
+
if (normalsHelper.current && ref.current) {
|
|
43
|
+
scene.remove(normalsHelper.current);
|
|
44
|
+
scene.remove(ref.current);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}, [props, color]);
|
|
48
|
+
|
|
49
|
+
useFrame(() => {
|
|
50
|
+
if (ref.current && props.object.current) {
|
|
51
|
+
const worldPosition = props.object.current.getWorldPosition(_v3);
|
|
52
|
+
|
|
53
|
+
ref.current.position.copy(worldPosition);
|
|
54
|
+
normalsHelper.current?.update();
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return null;
|
|
59
|
+
};
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { Collider } from "@dimforge/rapier3d-compat";
|
|
2
|
+
import React, {
|
|
3
|
+
ForwardedRef,
|
|
4
|
+
memo,
|
|
5
|
+
ReactNode,
|
|
6
|
+
Ref,
|
|
7
|
+
useEffect,
|
|
8
|
+
useMemo,
|
|
9
|
+
useRef
|
|
10
|
+
} from "react";
|
|
11
|
+
import { Object3D } from "three";
|
|
12
|
+
import { useRapier } from "../hooks/hooks";
|
|
13
|
+
import { useForwardedRef } from "../hooks/use-forwarded-ref";
|
|
14
|
+
import { useImperativeInstance } from "../hooks/use-imperative-instance";
|
|
15
|
+
import {
|
|
16
|
+
BallArgs,
|
|
17
|
+
CapsuleArgs,
|
|
18
|
+
ColliderOptions,
|
|
19
|
+
ConeArgs,
|
|
20
|
+
ConvexHullArgs,
|
|
21
|
+
CuboidArgs,
|
|
22
|
+
CylinderArgs,
|
|
23
|
+
HeightfieldArgs,
|
|
24
|
+
RoundConeArgs,
|
|
25
|
+
RoundCuboidArgs,
|
|
26
|
+
RoundCylinderArgs,
|
|
27
|
+
TrimeshArgs
|
|
28
|
+
} from "../types";
|
|
29
|
+
import { vec3 } from "../utils/three-object-helpers";
|
|
30
|
+
import {
|
|
31
|
+
cleanRigidBodyPropsForCollider,
|
|
32
|
+
createColliderFromOptions,
|
|
33
|
+
createColliderState,
|
|
34
|
+
getActiveCollisionEventsFromProps,
|
|
35
|
+
immutableColliderOptions,
|
|
36
|
+
useColliderEvents,
|
|
37
|
+
useUpdateColliderOptions
|
|
38
|
+
} from "../utils/utils-collider";
|
|
39
|
+
import { useRigidBodyContext } from "./RigidBody";
|
|
40
|
+
|
|
41
|
+
export interface ColliderProps extends ColliderOptions<any> {
|
|
42
|
+
children?: ReactNode;
|
|
43
|
+
ref?: Ref<Collider>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* A collider is a shape that can be attached to a rigid body to define its physical properties.
|
|
48
|
+
* @internal
|
|
49
|
+
*/
|
|
50
|
+
export const AnyCollider = memo((props: ColliderProps) => {
|
|
51
|
+
const { children, position, rotation, quaternion, scale, name } = props;
|
|
52
|
+
const { world, colliderEvents, colliderStates } = useRapier();
|
|
53
|
+
const rigidBodyContext = useRigidBodyContext();
|
|
54
|
+
const colliderRef = useForwardedRef(props.ref);
|
|
55
|
+
const objectRef = useRef<Object3D>(null);
|
|
56
|
+
|
|
57
|
+
// We spread the props out here to make sure that the ref is updated when the props change.
|
|
58
|
+
const immutablePropArray = immutableColliderOptions.flatMap((key) =>
|
|
59
|
+
// Array.isArray(props[key]) ? [...props[key]] : props[key]
|
|
60
|
+
Array.isArray(props[key]) ? props[key] : [props[key]]
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const getInstance = useImperativeInstance(
|
|
64
|
+
() => {
|
|
65
|
+
const worldScale = objectRef.current!.getWorldScale(vec3());
|
|
66
|
+
|
|
67
|
+
const collider = createColliderFromOptions(
|
|
68
|
+
props,
|
|
69
|
+
world,
|
|
70
|
+
worldScale,
|
|
71
|
+
rigidBodyContext?.getRigidBody
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (typeof props.ref == "function") {
|
|
75
|
+
props.ref(collider);
|
|
76
|
+
}
|
|
77
|
+
colliderRef.current = collider;
|
|
78
|
+
|
|
79
|
+
return collider;
|
|
80
|
+
},
|
|
81
|
+
(collider) => {
|
|
82
|
+
if (world.getCollider(collider.handle)) {
|
|
83
|
+
world.removeCollider(collider, true);
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
[...immutablePropArray, rigidBodyContext]
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
const collider = getInstance();
|
|
91
|
+
|
|
92
|
+
colliderStates.set(
|
|
93
|
+
collider.handle,
|
|
94
|
+
createColliderState(
|
|
95
|
+
collider,
|
|
96
|
+
objectRef.current!,
|
|
97
|
+
rigidBodyContext?.ref.current
|
|
98
|
+
)
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
return () => {
|
|
102
|
+
colliderStates.delete(collider.handle);
|
|
103
|
+
};
|
|
104
|
+
}, [getInstance]);
|
|
105
|
+
|
|
106
|
+
const mergedProps = useMemo(() => {
|
|
107
|
+
return {
|
|
108
|
+
...cleanRigidBodyPropsForCollider(rigidBodyContext?.options),
|
|
109
|
+
...props
|
|
110
|
+
};
|
|
111
|
+
}, [props, rigidBodyContext?.options]);
|
|
112
|
+
|
|
113
|
+
useUpdateColliderOptions(getInstance, mergedProps, colliderStates);
|
|
114
|
+
useColliderEvents(
|
|
115
|
+
getInstance,
|
|
116
|
+
mergedProps,
|
|
117
|
+
colliderEvents,
|
|
118
|
+
getActiveCollisionEventsFromProps(rigidBodyContext?.options)
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<object3D
|
|
123
|
+
position={position}
|
|
124
|
+
rotation={rotation}
|
|
125
|
+
quaternion={quaternion}
|
|
126
|
+
scale={scale}
|
|
127
|
+
ref={objectRef}
|
|
128
|
+
name={name}
|
|
129
|
+
>
|
|
130
|
+
{children}
|
|
131
|
+
</object3D>
|
|
132
|
+
);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
export type ColliderOptionsRequiredArgs<T extends unknown[]> = Omit<
|
|
136
|
+
ColliderOptions<T>,
|
|
137
|
+
"args"
|
|
138
|
+
> & {
|
|
139
|
+
args: T;
|
|
140
|
+
children?: ReactNode;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export type CuboidColliderProps = ColliderOptionsRequiredArgs<CuboidArgs>;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* A cuboid collider shape
|
|
147
|
+
* @category Colliders
|
|
148
|
+
*/
|
|
149
|
+
export const CuboidCollider = React.forwardRef(
|
|
150
|
+
(props: CuboidColliderProps, ref: ForwardedRef<Collider>) => {
|
|
151
|
+
return <AnyCollider {...props} shape="cuboid" ref={ref} />;
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
CuboidCollider.displayName = "CuboidCollider";
|
|
155
|
+
|
|
156
|
+
export type RoundCuboidColliderProps =
|
|
157
|
+
ColliderOptionsRequiredArgs<RoundCuboidArgs>;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* A round cuboid collider shape
|
|
161
|
+
* @category Colliders
|
|
162
|
+
*/
|
|
163
|
+
export const RoundCuboidCollider = React.forwardRef(
|
|
164
|
+
(props: RoundCuboidColliderProps, ref: ForwardedRef<Collider>) => (
|
|
165
|
+
<AnyCollider {...props} shape="roundCuboid" ref={ref} />
|
|
166
|
+
)
|
|
167
|
+
);
|
|
168
|
+
RoundCuboidCollider.displayName = "RoundCuboidCollider";
|
|
169
|
+
|
|
170
|
+
export type BallColliderProps = ColliderOptionsRequiredArgs<BallArgs>;
|
|
171
|
+
/**
|
|
172
|
+
* A ball collider shape
|
|
173
|
+
* @category Colliders
|
|
174
|
+
*/
|
|
175
|
+
export const BallCollider = React.forwardRef(
|
|
176
|
+
(props: BallColliderProps, ref: ForwardedRef<Collider>) => (
|
|
177
|
+
<AnyCollider {...props} shape="ball" ref={ref} />
|
|
178
|
+
)
|
|
179
|
+
);
|
|
180
|
+
BallCollider.displayName = "BallCollider";
|
|
181
|
+
|
|
182
|
+
export type CapsuleColliderProps = ColliderOptionsRequiredArgs<CapsuleArgs>;
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* A capsule collider shape
|
|
186
|
+
* @category Colliders
|
|
187
|
+
*/
|
|
188
|
+
export const CapsuleCollider = React.forwardRef(
|
|
189
|
+
(props: CapsuleColliderProps, ref: ForwardedRef<Collider>) => (
|
|
190
|
+
<AnyCollider {...props} shape="capsule" ref={ref} />
|
|
191
|
+
)
|
|
192
|
+
);
|
|
193
|
+
CapsuleCollider.displayName = "CapsuleCollider";
|
|
194
|
+
|
|
195
|
+
export type HeightfieldColliderProps =
|
|
196
|
+
ColliderOptionsRequiredArgs<HeightfieldArgs>;
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* A heightfield collider shape
|
|
200
|
+
* @category Colliders
|
|
201
|
+
*/
|
|
202
|
+
export const HeightfieldCollider = React.forwardRef(
|
|
203
|
+
(props: HeightfieldColliderProps, ref: ForwardedRef<Collider>) => (
|
|
204
|
+
<AnyCollider {...props} shape="heightfield" ref={ref} />
|
|
205
|
+
)
|
|
206
|
+
);
|
|
207
|
+
HeightfieldCollider.displayName = "HeightfieldCollider";
|
|
208
|
+
|
|
209
|
+
export type TrimeshColliderProps = ColliderOptionsRequiredArgs<TrimeshArgs>;
|
|
210
|
+
/**
|
|
211
|
+
* A trimesh collider shape
|
|
212
|
+
* @category Colliders
|
|
213
|
+
*/
|
|
214
|
+
export const TrimeshCollider = React.forwardRef(
|
|
215
|
+
(props: TrimeshColliderProps, ref: ForwardedRef<Collider>) => (
|
|
216
|
+
<AnyCollider {...props} shape="trimesh" ref={ref} />
|
|
217
|
+
)
|
|
218
|
+
);
|
|
219
|
+
TrimeshCollider.displayName = "TrimeshCollider";
|
|
220
|
+
|
|
221
|
+
export type ConeColliderProps = ColliderOptionsRequiredArgs<ConeArgs>;
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* A cone collider shape
|
|
225
|
+
* @category Colliders
|
|
226
|
+
*/
|
|
227
|
+
export const ConeCollider = React.forwardRef(
|
|
228
|
+
(props: ConeColliderProps, ref: ForwardedRef<Collider>) => (
|
|
229
|
+
<AnyCollider {...props} shape="cone" ref={ref} />
|
|
230
|
+
)
|
|
231
|
+
);
|
|
232
|
+
ConeCollider.displayName = "ConeCollider";
|
|
233
|
+
|
|
234
|
+
export type RoundConeColliderProps = ColliderOptionsRequiredArgs<RoundConeArgs>;
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* A round cylinder collider shape
|
|
238
|
+
* @category Colliders
|
|
239
|
+
*/
|
|
240
|
+
export const RoundConeCollider = React.forwardRef(
|
|
241
|
+
(props: RoundConeColliderProps, ref: ForwardedRef<Collider>) => (
|
|
242
|
+
<AnyCollider {...props} shape="roundCone" ref={ref} />
|
|
243
|
+
)
|
|
244
|
+
);
|
|
245
|
+
RoundConeCollider.displayName = "RoundConeCollider";
|
|
246
|
+
|
|
247
|
+
export type CylinderColliderProps = ColliderOptionsRequiredArgs<CylinderArgs>;
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* A cylinder collider shape
|
|
251
|
+
* @category Colliders
|
|
252
|
+
*/
|
|
253
|
+
export const CylinderCollider = React.forwardRef(
|
|
254
|
+
(props: CylinderColliderProps, ref: ForwardedRef<Collider>) => (
|
|
255
|
+
<AnyCollider {...props} shape="cylinder" ref={ref} />
|
|
256
|
+
)
|
|
257
|
+
);
|
|
258
|
+
CylinderCollider.displayName = "CylinderCollider";
|
|
259
|
+
|
|
260
|
+
export type RoundCylinderColliderProps =
|
|
261
|
+
ColliderOptionsRequiredArgs<RoundCylinderArgs>;
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* A round cylinder collider shape
|
|
265
|
+
* @category Colliders
|
|
266
|
+
*/
|
|
267
|
+
export const RoundCylinderCollider = React.forwardRef(
|
|
268
|
+
(props: RoundConeColliderProps, ref: ForwardedRef<Collider>) => (
|
|
269
|
+
<AnyCollider {...props} shape="roundCylinder" ref={ref} />
|
|
270
|
+
)
|
|
271
|
+
);
|
|
272
|
+
CylinderCollider.displayName = "RoundCylinderCollider";
|
|
273
|
+
|
|
274
|
+
export type ConvexHullColliderProps =
|
|
275
|
+
ColliderOptionsRequiredArgs<ConvexHullArgs>;
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* A convex hull collider shape
|
|
279
|
+
* @category Colliders
|
|
280
|
+
*/
|
|
281
|
+
export const ConvexHullCollider = React.forwardRef(
|
|
282
|
+
(props: ConvexHullColliderProps, ref: ForwardedRef<Collider>) => (
|
|
283
|
+
<AnyCollider {...props} shape="convexHull" ref={ref} />
|
|
284
|
+
)
|
|
285
|
+
);
|
|
286
|
+
ConvexHullCollider.displayName = "ConvexHullCollider";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React, { memo, useRef } from "react";
|
|
2
|
+
import { useFrame } from "@react-three/fiber";
|
|
3
|
+
import { BufferAttribute, BufferGeometry, LineSegments } from "three";
|
|
4
|
+
import { useRapier } from "../hooks/hooks";
|
|
5
|
+
import { _vector3 } from "../utils/shared-objects";
|
|
6
|
+
|
|
7
|
+
export const Debug = memo(() => {
|
|
8
|
+
const { world } = useRapier();
|
|
9
|
+
const ref = useRef<LineSegments>(null);
|
|
10
|
+
|
|
11
|
+
useFrame(() => {
|
|
12
|
+
const mesh = ref.current;
|
|
13
|
+
if (!mesh) return;
|
|
14
|
+
|
|
15
|
+
const buffers = world.debugRender();
|
|
16
|
+
|
|
17
|
+
const geometry = new BufferGeometry();
|
|
18
|
+
geometry.setAttribute("position", new BufferAttribute(buffers.vertices, 3));
|
|
19
|
+
geometry.setAttribute("color", new BufferAttribute(buffers.colors, 4));
|
|
20
|
+
|
|
21
|
+
mesh.geometry.dispose();
|
|
22
|
+
mesh.geometry = geometry;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<group>
|
|
27
|
+
<lineSegments ref={ref} frustumCulled={false}>
|
|
28
|
+
<lineBasicMaterial color={0xffffff} vertexColors />
|
|
29
|
+
<bufferGeometry />
|
|
30
|
+
</lineSegments>
|
|
31
|
+
</group>
|
|
32
|
+
);
|
|
33
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useFrame } from "@react-three/fiber";
|
|
2
|
+
import React, { memo } from "react";
|
|
3
|
+
import { useRaf } from "../utils/utils-physics";
|
|
4
|
+
import { PhysicsProps } from "./Physics";
|
|
5
|
+
|
|
6
|
+
interface FrameStepperProps {
|
|
7
|
+
type?: PhysicsProps["updateLoop"];
|
|
8
|
+
onStep: (dt: number) => void;
|
|
9
|
+
updatePriority?: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const UseFrameStepper = ({ onStep, updatePriority }: FrameStepperProps) => {
|
|
13
|
+
useFrame((_, dt) => {
|
|
14
|
+
onStep(dt);
|
|
15
|
+
}, updatePriority);
|
|
16
|
+
|
|
17
|
+
return null;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const RafStepper = ({ onStep }: FrameStepperProps) => {
|
|
21
|
+
useRaf((dt) => {
|
|
22
|
+
onStep(dt);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return null;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const FrameStepper = ({ onStep, type, updatePriority }: FrameStepperProps) => {
|
|
29
|
+
return type === "independent" ? (
|
|
30
|
+
<RafStepper onStep={onStep} />
|
|
31
|
+
) : (
|
|
32
|
+
<UseFrameStepper onStep={onStep} updatePriority={updatePriority} />
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default memo(FrameStepper);
|