react-three-game 0.0.92 → 0.0.94
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/README.md +81 -88
- package/dist/helpers/index.d.ts +0 -3
- package/dist/helpers/index.js +1 -8
- package/dist/index.d.ts +10 -10
- package/dist/index.js +7 -6
- package/dist/tools/prefabeditor/EditorTree.js +10 -14
- package/dist/tools/prefabeditor/EditorUI.js +4 -4
- package/dist/tools/prefabeditor/GameEvents.d.ts +6 -12
- package/dist/tools/prefabeditor/GameEvents.js +0 -8
- package/dist/tools/prefabeditor/InstanceProvider.d.ts +6 -4
- package/dist/tools/prefabeditor/InstanceProvider.js +84 -199
- package/dist/tools/prefabeditor/PrefabEditor.d.ts +12 -21
- package/dist/tools/prefabeditor/PrefabEditor.js +138 -146
- package/dist/tools/prefabeditor/PrefabRoot.d.ts +30 -11
- package/dist/tools/prefabeditor/PrefabRoot.js +182 -139
- package/dist/tools/prefabeditor/assetRuntime.d.ts +9 -13
- package/dist/tools/prefabeditor/assetRuntime.js +13 -13
- package/dist/tools/prefabeditor/components/BufferGeometryComponent.js +1 -1
- package/dist/tools/prefabeditor/components/CameraComponent.js +2 -2
- package/dist/tools/prefabeditor/components/ComponentRegistry.d.ts +3 -3
- package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +2 -2
- package/dist/tools/prefabeditor/components/Input.js +5 -9
- package/dist/tools/prefabeditor/components/ModelComponent.js +4 -6
- package/dist/tools/prefabeditor/components/PointLightComponent.js +2 -2
- package/dist/tools/prefabeditor/components/SoundComponent.js +2 -2
- package/dist/tools/prefabeditor/components/SpotLightComponent.js +2 -2
- package/dist/tools/prefabeditor/components/index.js +0 -2
- package/dist/tools/prefabeditor/prefab.d.ts +1 -2
- package/dist/tools/prefabeditor/prefab.js +2 -3
- package/dist/tools/prefabeditor/prefabStore.d.ts +0 -6
- package/dist/tools/prefabeditor/prefabStore.js +1 -33
- package/dist/tools/prefabeditor/types.d.ts +1 -0
- package/dist/tools/prefabeditor/usePointerEvents.d.ts +3 -3
- package/dist/tools/prefabeditor/usePointerEvents.js +5 -5
- package/package.json +49 -51
- package/dist/tools/prefabeditor/components/PhysicsComponent.d.ts +0 -26
- package/dist/tools/prefabeditor/components/PhysicsComponent.js +0 -302
- package/dist/tools/prefabeditor/scene.d.ts +0 -70
- package/dist/tools/prefabeditor/scene.js +0 -237
package/README.md
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
# react-three-game
|
|
2
2
|
|
|
3
|
-

|
|
4
4
|
|
|
5
|
-
JSON-first
|
|
5
|
+
JSON-first prefab mounting and authoring for React Three Fiber.
|
|
6
6
|
|
|
7
|
-
Built on top of [three.js](https://github.com/mrdoob/three.js), [@react-three/fiber](https://github.com/pmndrs/react-three-fiber), and [@react-three/
|
|
7
|
+
Built on top of [three.js](https://github.com/mrdoob/three.js), [@react-three/fiber](https://github.com/pmndrs/react-three-fiber), and [@react-three/drei](https://github.com/pmndrs/drei).
|
|
8
8
|
|
|
9
|
-
* **🧱 Prefabs** - Save
|
|
10
|
-
* **🎬
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* **🔧 Direct Runtime Access** - Get native `Object3D`, Rapier rigid body, and prefab store access without a parallel engine API.
|
|
9
|
+
* **🧱 Prefabs** - Save prefabs as serializable JSON and load them on their own or inside larger app worlds.
|
|
10
|
+
* **🎬 Prefab Editor** - Edit prefabs visually with hierarchy, inspector, transform gizmos, and play mode.
|
|
11
|
+
* **🧩 Components** - Build prefabs from reusable `GameObject` + component composition.
|
|
12
|
+
* **🔧 Direct Runtime Access** - Get native `Object3D`, runtime handles, and authored prefab mutation APIs without a parallel engine API.
|
|
14
13
|
* **⚡ R3F Native** - Use normal React Three Fiber components whenever runtime behavior is clearer in code.
|
|
15
14
|
|
|
16
15
|
## Documentation
|
|
@@ -21,7 +20,7 @@ Built on top of [three.js](https://github.com/mrdoob/three.js), [@react-three/fi
|
|
|
21
20
|
## Install
|
|
22
21
|
|
|
23
22
|
```bash
|
|
24
|
-
npm install react-three-game @react-three/drei @react-three/fiber
|
|
23
|
+
npm install react-three-game @react-three/drei @react-three/fiber three
|
|
25
24
|
```
|
|
26
25
|
|
|
27
26
|
## Usage
|
|
@@ -29,12 +28,11 @@ npm install react-three-game @react-three/drei @react-three/fiber @react-three/r
|
|
|
29
28
|
Here is a minimal example that renders a prefab inside a normal R3F app:
|
|
30
29
|
|
|
31
30
|
```tsx
|
|
32
|
-
import { Physics } from "@react-three/rapier";
|
|
33
31
|
import { GameCanvas, PrefabRoot, ground } from "react-three-game";
|
|
34
32
|
|
|
35
33
|
const prefab = {
|
|
36
34
|
id: "starter-scene",
|
|
37
|
-
name: "Starter
|
|
35
|
+
name: "Starter Prefab",
|
|
38
36
|
root: {
|
|
39
37
|
id: "root",
|
|
40
38
|
children: [
|
|
@@ -58,10 +56,6 @@ const prefab = {
|
|
|
58
56
|
type: "Material",
|
|
59
57
|
properties: { color: "#f66" },
|
|
60
58
|
},
|
|
61
|
-
physics: {
|
|
62
|
-
type: "Physics",
|
|
63
|
-
properties: { type: "dynamic" },
|
|
64
|
-
},
|
|
65
59
|
},
|
|
66
60
|
},
|
|
67
61
|
],
|
|
@@ -71,16 +65,14 @@ const prefab = {
|
|
|
71
65
|
export default function App() {
|
|
72
66
|
return (
|
|
73
67
|
<GameCanvas>
|
|
74
|
-
<
|
|
75
|
-
|
|
76
|
-
<PrefabRoot data={prefab} />
|
|
77
|
-
</Physics>
|
|
68
|
+
<ambientLight intensity={0.8} />
|
|
69
|
+
<PrefabRoot data={prefab} />
|
|
78
70
|
</GameCanvas>
|
|
79
71
|
);
|
|
80
72
|
}
|
|
81
73
|
```
|
|
82
74
|
|
|
83
|
-
This example renders a
|
|
75
|
+
This example renders a simple authored prefab with a ground plane and mesh content.
|
|
84
76
|
|
|
85
77
|
## Prefab Editor
|
|
86
78
|
|
|
@@ -98,22 +90,21 @@ Open the hosted editor here:
|
|
|
98
90
|
|
|
99
91
|
* https://prnth.com/react-three-game/editor
|
|
100
92
|
|
|
101
|
-
## Prefabs And Mounted
|
|
93
|
+
## Prefabs And Mounted Objects
|
|
102
94
|
|
|
103
95
|
`Prefab` is the serializable pure data format.
|
|
104
96
|
|
|
105
|
-
That means
|
|
97
|
+
That means authored content stays as a prefab, and the same prefab can be:
|
|
106
98
|
|
|
107
99
|
* edited directly in `PrefabEditor`
|
|
108
100
|
* rendered directly with `PrefabRoot`
|
|
109
|
-
* loaded inside another scene as reusable content
|
|
101
|
+
* loaded inside another prefab or app scene as reusable content
|
|
110
102
|
|
|
111
103
|
`PrefabRoot` keeps the rendering model narrow and compositional:
|
|
112
104
|
|
|
113
105
|
* `Transform` is the renderer-owned outer transform
|
|
114
106
|
* `Geometry` or `BufferGeometry` + `Material` become the primary mesh content
|
|
115
107
|
* non-instanced `Model` becomes the node's primary content
|
|
116
|
-
* `Physics` is a renderer-owned outer wrapper
|
|
117
108
|
* every other component `View` wraps the current subtree
|
|
118
109
|
|
|
119
110
|
Custom component `View`s use normal React Three Fiber composition with `children`.
|
|
@@ -161,7 +152,7 @@ interface GameObject {
|
|
|
161
152
|
|
|
162
153
|
## Runtime Mutation
|
|
163
154
|
|
|
164
|
-
Use
|
|
155
|
+
Use the editor or root ref for scene-native object access, and the `Scene` mutation methods for authored data changes.
|
|
165
156
|
|
|
166
157
|
```tsx
|
|
167
158
|
import { useEffect, useRef } from "react";
|
|
@@ -171,7 +162,7 @@ function RaiseBall() {
|
|
|
171
162
|
const editorRef = useRef<PrefabEditorRef>(null);
|
|
172
163
|
|
|
173
164
|
useEffect(() => {
|
|
174
|
-
editorRef.current?.
|
|
165
|
+
editorRef.current?.update("ball", (node) => ({
|
|
175
166
|
...node,
|
|
176
167
|
components: {
|
|
177
168
|
...node.components,
|
|
@@ -197,6 +188,55 @@ const ball = editorRef.current?.getObject("ball");
|
|
|
197
188
|
ball?.rotateY(0.5);
|
|
198
189
|
```
|
|
199
190
|
|
|
191
|
+
For runtime integrations that need to react to authored scene changes, subscribe through the prefab store:
|
|
192
|
+
|
|
193
|
+
```tsx
|
|
194
|
+
import { usePrefabStoreApi } from "react-three-game";
|
|
195
|
+
|
|
196
|
+
const store = usePrefabStoreApi();
|
|
197
|
+
const stop = store.subscribe(
|
|
198
|
+
(s) => s.nodesById,
|
|
199
|
+
(next, prev) => console.log("scene changed", next, prev),
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
stop();
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
For runtime-owned imperative state, register node-local handles instead of reaching for ad hoc globals:
|
|
206
|
+
|
|
207
|
+
```tsx
|
|
208
|
+
import { useEffect } from "react";
|
|
209
|
+
import { useAssetRuntime, useNode, useNodeHandle } from "react-three-game";
|
|
210
|
+
|
|
211
|
+
function SpinnerView({ children }: { children?: React.ReactNode }) {
|
|
212
|
+
const { nodeId } = useNode();
|
|
213
|
+
const { registerHandle } = useAssetRuntime();
|
|
214
|
+
|
|
215
|
+
useEffect(() => {
|
|
216
|
+
const handle = {
|
|
217
|
+
setSpeed(next: number) {
|
|
218
|
+
console.log("speed", next);
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
registerHandle(nodeId, "spinner", handle);
|
|
223
|
+
return () => registerHandle(nodeId, "spinner", null);
|
|
224
|
+
}, [nodeId, registerHandle]);
|
|
225
|
+
|
|
226
|
+
return <>{children}</>;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function SpinnerStatus() {
|
|
230
|
+
const spinnerRef = useNodeHandle<{ setSpeed: (next: number) => void }>("spinner");
|
|
231
|
+
|
|
232
|
+
useEffect(() => {
|
|
233
|
+
spinnerRef.current?.setSpeed(2);
|
|
234
|
+
}, [spinnerRef]);
|
|
235
|
+
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
200
240
|
Mounted node metadata is mirrored onto the canonical Three.js wrapper object:
|
|
201
241
|
|
|
202
242
|
* `GameObject.id` -> `object.userData.prefabNodeId`
|
|
@@ -210,11 +250,11 @@ const playerByName = editorRef.current?.root?.getObjectByName("Player");
|
|
|
210
250
|
const playerById = editorRef.current?.root?.getObjectByProperty("userData.prefabNodeId", "player");
|
|
211
251
|
```
|
|
212
252
|
|
|
213
|
-
Treat names as a convenience surface,
|
|
253
|
+
Treat names as a convenience surface, with stable ids as the primary lookup key:
|
|
214
254
|
|
|
255
|
+
* `editorRef.current?.getObject(id)` is the clearest stable authored-node lookup
|
|
215
256
|
* names are not guaranteed unique
|
|
216
|
-
*
|
|
217
|
-
* traversal metadata is applied to the prefab node transform object, not necessarily the inner mesh or model child
|
|
257
|
+
* traversal metadata is applied to the prefab node transform object — the inner mesh or model child is one level deeper
|
|
218
258
|
|
|
219
259
|
You can author extra `userData` from the editor with a `Data` component:
|
|
220
260
|
|
|
@@ -228,68 +268,21 @@ You can author extra `userData` from the editor with a `Data` component:
|
|
|
228
268
|
}
|
|
229
269
|
```
|
|
230
270
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
```tsx
|
|
234
|
-
editorRef.current?.store.getState().updateNodes([
|
|
235
|
-
{
|
|
236
|
-
id: "orb1",
|
|
237
|
-
update: (node) => ({
|
|
238
|
-
...node,
|
|
239
|
-
components: {
|
|
240
|
-
...node.components,
|
|
241
|
-
transform: {
|
|
242
|
-
...node.components?.transform,
|
|
243
|
-
type: "Transform",
|
|
244
|
-
properties: {
|
|
245
|
-
...node.components?.transform?.properties,
|
|
246
|
-
position: [1, 0, 0],
|
|
247
|
-
},
|
|
248
|
-
},
|
|
249
|
-
},
|
|
250
|
-
}),
|
|
251
|
-
},
|
|
252
|
-
{
|
|
253
|
-
id: "orb2",
|
|
254
|
-
update: (node) => ({
|
|
255
|
-
...node,
|
|
256
|
-
components: {
|
|
257
|
-
...node.components,
|
|
258
|
-
transform: {
|
|
259
|
-
...node.components?.transform,
|
|
260
|
-
type: "Transform",
|
|
261
|
-
properties: {
|
|
262
|
-
...node.components?.transform?.properties,
|
|
263
|
-
position: [-1, 0, 0],
|
|
264
|
-
},
|
|
265
|
-
},
|
|
266
|
-
},
|
|
267
|
-
}),
|
|
268
|
-
},
|
|
269
|
-
]);
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
Custom component `View`s should use normal React and R3F behavior, such as `useFrame`, refs, and native Three.js APIs.
|
|
271
|
+
Custom component `View`s use normal React and R3F behavior — `useFrame`, refs, and native Three.js APIs.
|
|
273
272
|
|
|
274
273
|
## Useful Exports
|
|
275
274
|
|
|
276
|
-
* `GameCanvas`
|
|
277
|
-
* `
|
|
278
|
-
* `
|
|
279
|
-
* `
|
|
280
|
-
* `
|
|
281
|
-
* `
|
|
282
|
-
* `
|
|
283
|
-
* `
|
|
284
|
-
* `
|
|
285
|
-
* `
|
|
286
|
-
* `
|
|
287
|
-
* `ground(...)`
|
|
288
|
-
* `loadJson()` / `saveJson()`
|
|
289
|
-
* `loadModel()` / `loadTexture()`
|
|
290
|
-
* `loadSound()` / `loadFiles()`
|
|
291
|
-
* `exportGLB()`
|
|
292
|
-
* `computeParentWorldMatrix()`
|
|
275
|
+
* `GameCanvas`, `PrefabRoot`, `PrefabEditor`, `PrefabEditorMode`
|
|
276
|
+
* `Prefab`, `GameObject`, `ComponentData`, `PrefabNode`, `PrefabEditorRef`, `Scene`
|
|
277
|
+
* `registerComponent`, `Component`, `ComponentViewProps`, `FieldDefinition`
|
|
278
|
+
* `useScene`, `useEditorRef`, `useEditorContext`
|
|
279
|
+
* `useNode`, `useNodeObject`, `useNodeHandle`, `useAssetRuntime`
|
|
280
|
+
* `usePrefabStore`, `usePrefabStoreApi`
|
|
281
|
+
* `gameEvents`, `useGameEvent`, `useClickEvent`
|
|
282
|
+
* `loadJson`, `saveJson`, `loadFiles`, `loadModel`, `loadTexture`, `loadSound`
|
|
283
|
+
* `exportGLB`, `exportGLBData`, `regenerateIds`, `computeParentWorldMatrix`
|
|
284
|
+
* `ground`, `soundManager`
|
|
285
|
+
* `FieldRenderer`, `Vector3Field`, `NumberField`, `StringField`, `BooleanField`, `SelectField`, `ColorField`
|
|
293
286
|
|
|
294
287
|
## Development
|
|
295
288
|
|
package/dist/helpers/index.d.ts
CHANGED
|
@@ -16,8 +16,6 @@ export interface GroundOptions {
|
|
|
16
16
|
repeat?: boolean;
|
|
17
17
|
/** Texture repeat counts when repeat=true. Defaults to [25,25]. */
|
|
18
18
|
repeatCount?: [number, number];
|
|
19
|
-
/** Physics body type. Defaults to "fixed". */
|
|
20
|
-
physicsType?: "fixed" | "dynamic" | "kinematic";
|
|
21
19
|
/** Set true to disable the node. */
|
|
22
20
|
disabled?: boolean;
|
|
23
21
|
}
|
|
@@ -28,6 +26,5 @@ export interface GroundOptions {
|
|
|
28
26
|
* - Transform (rotated to lie flat)
|
|
29
27
|
* - Geometry (plane)
|
|
30
28
|
* - Material (optional texture + repeat)
|
|
31
|
-
* - Physics (fixed by default)
|
|
32
29
|
*/
|
|
33
30
|
export declare function ground(options?: GroundOptions): GameObject;
|
package/dist/helpers/index.js
CHANGED
|
@@ -5,10 +5,9 @@
|
|
|
5
5
|
* - Transform (rotated to lie flat)
|
|
6
6
|
* - Geometry (plane)
|
|
7
7
|
* - Material (optional texture + repeat)
|
|
8
|
-
* - Physics (fixed by default)
|
|
9
8
|
*/
|
|
10
9
|
export function ground(options = {}) {
|
|
11
|
-
const { id = "ground", size = 50, position = [0, 0, 0], rotation = [-Math.PI / 2, 0, 0], scale = [1, 1, 1], color = "#eeeeee", texture, repeat = texture ? true : false, repeatCount = [25, 25],
|
|
10
|
+
const { id = "ground", size = 50, position = [0, 0, 0], rotation = [-Math.PI / 2, 0, 0], scale = [1, 1, 1], color = "#eeeeee", texture, repeat = texture ? true : false, repeatCount = [25, 25], disabled = false, } = options;
|
|
12
11
|
return {
|
|
13
12
|
id,
|
|
14
13
|
disabled,
|
|
@@ -32,12 +31,6 @@ export function ground(options = {}) {
|
|
|
32
31
|
type: "Material",
|
|
33
32
|
properties: Object.assign(Object.assign({ color }, (texture ? { texture } : {})), (repeat ? { repeat: true, repeatCount } : {})),
|
|
34
33
|
},
|
|
35
|
-
physics: {
|
|
36
|
-
type: "Physics",
|
|
37
|
-
properties: {
|
|
38
|
-
type: physicsType,
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
34
|
},
|
|
42
35
|
};
|
|
43
36
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,25 +4,26 @@ export { ground } from './helpers';
|
|
|
4
4
|
export type { GroundOptions, Vec3 } from './helpers';
|
|
5
5
|
export { sound as soundManager } from './helpers/SoundManager';
|
|
6
6
|
export { default as PrefabEditor } from './tools/prefabeditor/PrefabEditor';
|
|
7
|
-
export { PrefabEditorMode } from './tools/prefabeditor/
|
|
7
|
+
export { PrefabEditorMode } from './tools/prefabeditor/PrefabRoot';
|
|
8
8
|
export { default as PrefabRoot } from './tools/prefabeditor/PrefabRoot';
|
|
9
|
-
export { useEditorContext } from './tools/prefabeditor/PrefabEditor';
|
|
9
|
+
export { useEditorContext, useEditorRef } from './tools/prefabeditor/PrefabEditor';
|
|
10
10
|
export type { EditorContextType } from './tools/prefabeditor/PrefabEditor';
|
|
11
|
-
export {
|
|
11
|
+
export { usePrefabStore, usePrefabStoreApi } from './tools/prefabeditor/prefabStore';
|
|
12
12
|
export type { PrefabStoreApi, PrefabStoreState } from './tools/prefabeditor/prefabStore';
|
|
13
13
|
export { denormalizePrefab } from './tools/prefabeditor/prefab';
|
|
14
|
-
export { gameEvents,
|
|
15
|
-
export type { ClickEventPayload, GameEventHandler, GameEventMap
|
|
14
|
+
export { gameEvents, useClickEvent, useGameEvent } from './tools/prefabeditor/GameEvents';
|
|
15
|
+
export type { ClickEventPayload, ContactEventPayload, GameEventHandler, GameEventMap } from './tools/prefabeditor/GameEvents';
|
|
16
16
|
export { registerComponent } from './tools/prefabeditor/components/ComponentRegistry';
|
|
17
17
|
export { FieldRenderer, FieldGroup, ListEditor, Label, Vector3Input, Vector3Field, NumberField, ColorInput, ColorField, StringInput, StringField, BooleanInput, BooleanField, SelectInput, SelectField, } from './tools/prefabeditor/components/Input';
|
|
18
18
|
export { loadJson, saveJson, exportGLB, exportGLBData, regenerateIds, computeParentWorldMatrix, } from './tools/prefabeditor/utils';
|
|
19
19
|
export type { ExportGLBOptions } from './tools/prefabeditor/utils';
|
|
20
20
|
export { createModelNode, createImageNode, } from './tools/prefabeditor/prefab';
|
|
21
|
-
export type { PrefabEditorProps, PrefabEditorRef, } from './tools/prefabeditor/PrefabEditor';
|
|
22
|
-
export type { Entity, EntityComponent, EntityData, EntityUpdate, PropertyPath, Scene, SceneUpdates, SpawnOptions, } from './tools/prefabeditor/scene';
|
|
21
|
+
export type { PrefabEditorProps, PrefabNode, PrefabEditorRef, } from './tools/prefabeditor/PrefabEditor';
|
|
23
22
|
export type { PrefabRootProps } from './tools/prefabeditor/PrefabRoot';
|
|
24
|
-
export type { AssetRuntime,
|
|
25
|
-
export { useAssetRuntime,
|
|
23
|
+
export type { AssetRuntime, NodeApi, LiveRef } from './tools/prefabeditor/assetRuntime';
|
|
24
|
+
export { useAssetRuntime, useNode, useNodeHandle, useNodeObject } from './tools/prefabeditor/assetRuntime';
|
|
25
|
+
export type { Scene } from './tools/prefabeditor/PrefabRoot';
|
|
26
|
+
export { useScene } from './tools/prefabeditor/PrefabRoot';
|
|
26
27
|
export type { Component, ComponentViewProps } from './tools/prefabeditor/components/ComponentRegistry';
|
|
27
28
|
export type { FieldDefinition, FieldType } from './tools/prefabeditor/components/Input';
|
|
28
29
|
export { MaterialOverridesProvider, useMaterialOverrides } from './tools/prefabeditor/components/MaterialComponent';
|
|
@@ -35,4 +36,3 @@ export type { AssetLoadOptions } from './tools/dragdrop/DragDropLoader';
|
|
|
35
36
|
export { loadModel, loadSound, loadTexture } from './tools/dragdrop/modelLoader';
|
|
36
37
|
export type { LoadedModel, LoadedModels, ModelLoadResult, LoadedSound, LoadedSounds, SoundLoadResult, LoadedTexture, LoadedTextures, TextureLoadResult, ProgressCallback, } from './tools/dragdrop/modelLoader';
|
|
37
38
|
export { ModelListViewer, SoundListViewer, ModelPicker, SoundPicker, TextureListViewer, TexturePicker, SingleModelViewer, SingleSoundViewer, SingleTextureViewer, SharedCanvas, } from './tools/assetviewer/page';
|
|
38
|
-
export type { PrefabRootRef } from './tools/prefabeditor/PrefabRoot';
|
package/dist/index.js
CHANGED
|
@@ -5,13 +5,13 @@ export { ground } from './helpers';
|
|
|
5
5
|
export { sound as soundManager } from './helpers/SoundManager';
|
|
6
6
|
// Prefab Editor
|
|
7
7
|
export { default as PrefabEditor } from './tools/prefabeditor/PrefabEditor';
|
|
8
|
-
export { PrefabEditorMode } from './tools/prefabeditor/
|
|
8
|
+
export { PrefabEditorMode } from './tools/prefabeditor/PrefabRoot';
|
|
9
9
|
export { default as PrefabRoot } from './tools/prefabeditor/PrefabRoot';
|
|
10
|
-
export { useEditorContext } from './tools/prefabeditor/PrefabEditor';
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
export { useEditorContext, useEditorRef } from './tools/prefabeditor/PrefabEditor';
|
|
11
|
+
export { usePrefabStore, usePrefabStoreApi } from './tools/prefabeditor/prefabStore';
|
|
12
|
+
// Prefab Editor - Data API
|
|
13
13
|
export { denormalizePrefab } from './tools/prefabeditor/prefab';
|
|
14
|
-
export { gameEvents,
|
|
14
|
+
export { gameEvents, useClickEvent, useGameEvent } from './tools/prefabeditor/GameEvents';
|
|
15
15
|
// Prefab Editor - Component Registry
|
|
16
16
|
export { registerComponent } from './tools/prefabeditor/components/ComponentRegistry';
|
|
17
17
|
// Prefab Editor - Input Components
|
|
@@ -19,7 +19,8 @@ export { FieldRenderer, FieldGroup, ListEditor, Label, Vector3Input, Vector3Fiel
|
|
|
19
19
|
// Prefab Editor - Utils
|
|
20
20
|
export { loadJson, saveJson, exportGLB, exportGLBData, regenerateIds, computeParentWorldMatrix, } from './tools/prefabeditor/utils';
|
|
21
21
|
export { createModelNode, createImageNode, } from './tools/prefabeditor/prefab';
|
|
22
|
-
export { useAssetRuntime,
|
|
22
|
+
export { useAssetRuntime, useNode, useNodeHandle, useNodeObject } from './tools/prefabeditor/assetRuntime';
|
|
23
|
+
export { useScene } from './tools/prefabeditor/PrefabRoot';
|
|
23
24
|
export { MaterialOverridesProvider, useMaterialOverrides } from './tools/prefabeditor/components/MaterialComponent';
|
|
24
25
|
export { findComponent, findComponentEntry, hasComponent } from './tools/prefabeditor/types';
|
|
25
26
|
export { float, positionLocal, sin, time, uniform, vec3, } from 'three/tsl';
|
|
@@ -1,20 +1,16 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { memo, useCallback, useState } from 'react';
|
|
3
3
|
import { base, colors, tree } from './styles';
|
|
4
|
-
import { useEditorContext } from './PrefabEditor';
|
|
4
|
+
import { useEditorContext, useEditorRef } from './PrefabEditor';
|
|
5
5
|
import { Dropdown } from './Dropdown';
|
|
6
6
|
import { FileMenu, TreeContextMenu, TreeNodeMenu } from './EditorTreeMenus';
|
|
7
7
|
import { createEmptyNode } from './prefab';
|
|
8
8
|
import { usePrefabChildIds, usePrefabNode, usePrefabRootId, usePrefabStore, usePrefabStoreApi } from './prefabStore';
|
|
9
9
|
export default function EditorTree({ selectedId, setSelectedId, getPrefab, onReplacePrefab, onImportPrefab, onUndo, onRedo, canUndo, canRedo }) {
|
|
10
10
|
const { onFocusNode } = useEditorContext();
|
|
11
|
+
const editor = useEditorRef();
|
|
11
12
|
const rootId = usePrefabRootId();
|
|
12
13
|
const store = usePrefabStoreApi();
|
|
13
|
-
const addChild = usePrefabStore(state => state.addChild);
|
|
14
|
-
const duplicateNode = usePrefabStore(state => state.duplicateNode);
|
|
15
|
-
const deleteNode = usePrefabStore(state => state.deleteNode);
|
|
16
|
-
const toggleNodeFlag = usePrefabStore(state => state.toggleNodeFlag);
|
|
17
|
-
const moveNode = usePrefabStore(state => state.moveNode);
|
|
18
14
|
const [draggedId, setDraggedId] = useState(null);
|
|
19
15
|
const [dropTarget, setDropTarget] = useState(null);
|
|
20
16
|
const [collapsedIds, setCollapsedIds] = useState(new Set());
|
|
@@ -31,30 +27,30 @@ export default function EditorTree({ selectedId, setSelectedId, getPrefab, onRep
|
|
|
31
27
|
};
|
|
32
28
|
const handleAddChild = (parentId) => {
|
|
33
29
|
const newNode = createEmptyNode();
|
|
34
|
-
|
|
30
|
+
editor.add(newNode, parentId);
|
|
35
31
|
setSelectedId(newNode.id);
|
|
36
32
|
};
|
|
37
33
|
const handleDuplicate = (nodeId) => {
|
|
38
34
|
if (nodeId === rootId)
|
|
39
35
|
return;
|
|
40
|
-
const duplicatedId =
|
|
36
|
+
const duplicatedId = editor.duplicate(nodeId);
|
|
41
37
|
if (duplicatedId)
|
|
42
38
|
setSelectedId(duplicatedId);
|
|
43
39
|
};
|
|
44
40
|
const handleDelete = (nodeId) => {
|
|
45
41
|
if (nodeId === rootId)
|
|
46
42
|
return;
|
|
47
|
-
|
|
43
|
+
editor.remove(nodeId);
|
|
48
44
|
if (selectedId === nodeId)
|
|
49
45
|
setSelectedId(null);
|
|
50
46
|
};
|
|
51
47
|
const handleToggleDisabled = (nodeId) => {
|
|
52
|
-
|
|
48
|
+
editor.update(nodeId, n => (Object.assign(Object.assign({}, n), { disabled: !n.disabled })));
|
|
53
49
|
};
|
|
54
50
|
const handleToggleLocked = (nodeId) => {
|
|
55
51
|
var _a;
|
|
56
52
|
const willLock = !((_a = store.getState().nodesById[nodeId]) === null || _a === void 0 ? void 0 : _a.locked);
|
|
57
|
-
|
|
53
|
+
editor.update(nodeId, n => (Object.assign(Object.assign({}, n), { locked: !n.locked })));
|
|
58
54
|
if (willLock && selectedId === nodeId)
|
|
59
55
|
setSelectedId(null);
|
|
60
56
|
};
|
|
@@ -99,12 +95,12 @@ export default function EditorTree({ selectedId, setSelectedId, getPrefab, onRep
|
|
|
99
95
|
if (!draggedId || draggedId === targetId)
|
|
100
96
|
return;
|
|
101
97
|
e.preventDefault();
|
|
102
|
-
|
|
98
|
+
editor.move(draggedId, targetId, getDropPosition(e, isRoot));
|
|
103
99
|
setDraggedId(null);
|
|
104
100
|
setDropTarget(null);
|
|
105
101
|
};
|
|
106
102
|
const visibleIds = usePrefabStore(useCallback(state => searchQuery ? buildVisibleIds(state, rootId, searchQuery) : null, [rootId, searchQuery]));
|
|
107
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { style: Object.assign(Object.assign({}, tree.panel), { width: collapsed ? 'auto' : 224 }), children: [_jsxs("div", { style: base.header, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 6, cursor: 'pointer' }, onClick: () => setCollapsed(!collapsed), children: [_jsx("span", { children: collapsed ? '▶' : '▼' }), _jsx("span", { children: "
|
|
103
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { style: Object.assign(Object.assign({}, tree.panel), { width: collapsed ? 'auto' : 224 }), children: [_jsxs("div", { style: base.header, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 6, cursor: 'pointer' }, onClick: () => setCollapsed(!collapsed), children: [_jsx("span", { children: collapsed ? '▶' : '▼' }), _jsx("span", { children: "Prefab" })] }), !collapsed && (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 4 }, children: [_jsx("button", { style: Object.assign(Object.assign({}, base.btn), { padding: '2px 6px', fontSize: 10, opacity: canUndo ? 1 : 0.4 }), onClick: (e) => { e.stopPropagation(); onUndo === null || onUndo === void 0 ? void 0 : onUndo(); }, disabled: !canUndo, title: "Undo", children: "\u21B6" }), _jsx("button", { style: Object.assign(Object.assign({}, base.btn), { padding: '2px 6px', fontSize: 10, opacity: canRedo ? 1 : 0.4 }), onClick: (e) => { e.stopPropagation(); onRedo === null || onRedo === void 0 ? void 0 : onRedo(); }, disabled: !canRedo, title: "Redo", children: "\u21B7" }), _jsx(Dropdown, { placement: "bottom-end", trigger: ({ ref, toggle }) => (_jsx("button", { ref: ref, title: "Menu", style: Object.assign(Object.assign({}, base.btn), { padding: '2px 6px', fontSize: 10 }), onClick: (e) => {
|
|
108
104
|
e.stopPropagation();
|
|
109
105
|
toggle();
|
|
110
106
|
}, children: "\u22EE" })), children: (close) => (_jsx(FileMenu, { getPrefab: getPrefab, onReplacePrefab: onReplacePrefab, onImportPrefab: onImportPrefab, onClose: close })) })] }))] }), !collapsed && (_jsxs(_Fragment, { children: [_jsx("div", { style: { padding: '4px 4px', borderBottom: `1px solid ${colors.borderLight}` }, children: _jsx("input", { type: "text", placeholder: "Search nodes...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), onClick: (e) => e.stopPropagation(), style: Object.assign(Object.assign({}, base.input), { padding: '4px 8px' }) }) }), _jsx("div", { style: tree.scroll, children: _jsx(TreeNode, { nodeId: rootId, depth: 0, rootId: rootId, visibleIds: visibleIds, collapsedIds: collapsedIds, dropTarget: dropTarget, selectedNodeId: selectedId, onToggleCollapse: toggleCollapse, onOpenContextMenu: openContextMenu, onDragStart: handleDragStart, onDragOver: handleDragOver, onDragLeave: handleDragLeave, onDrop: handleDrop, onDragEnd: () => { setDraggedId(null); setDropTarget(null); }, renderTreeNodeMenu: renderTreeNodeMenu, onToggleDisabled: handleToggleDisabled, setSelectedId: setSelectedId }) })] }))] }), _jsx(TreeContextMenu, { contextMenu: contextMenu, onClose: closeContextMenu, children: (nodeId, close) => renderTreeNodeMenu(nodeId, nodeId === rootId, close) })] }));
|
|
@@ -138,7 +134,7 @@ const TreeNode = memo(function TreeNode({ nodeId, depth, rootId, visibleIds, col
|
|
|
138
134
|
}, children: "\u22EF" })), children: (close) => renderTreeNodeMenu(nodeId, false, close) }), _jsx("button", { style: Object.assign(Object.assign({}, tree.iconButton), { opacity: node.disabled ? 0.5 : 0.7 }), onClick: (e) => {
|
|
139
135
|
e.stopPropagation();
|
|
140
136
|
onToggleDisabled(nodeId);
|
|
141
|
-
}, title: node.disabled ? 'Enable' : 'Disable', children: node.disabled ? '◎' : '◉' })] })), isRoot && (_jsx(Dropdown, { placement: "bottom-end", trigger: ({ ref, toggle }) => (_jsx("button", { ref: ref, title: "
|
|
137
|
+
}, title: node.disabled ? 'Enable' : 'Disable', children: node.disabled ? '◎' : '◉' })] })), isRoot && (_jsx(Dropdown, { placement: "bottom-end", trigger: ({ ref, toggle }) => (_jsx("button", { ref: ref, title: "Prefab Actions", style: tree.iconButton, onClick: (e) => {
|
|
142
138
|
e.stopPropagation();
|
|
143
139
|
toggle();
|
|
144
140
|
}, children: "\u22EF" })), children: (close) => renderTreeNodeMenu(nodeId, true, close) }))] }), !isCollapsed && childIds.map(childId => (_jsx(TreeNode, { nodeId: childId, depth: depth + 1, rootId: rootId, visibleIds: visibleIds, collapsedIds: collapsedIds, dropTarget: dropTarget, selectedNodeId: selectedNodeId, onToggleCollapse: onToggleCollapse, onOpenContextMenu: onOpenContextMenu, onDragStart: onDragStart, onDragOver: onDragOver, onDragLeave: onDragLeave, onDrop: onDrop, onDragEnd: onDragEnd, renderTreeNodeMenu: renderTreeNodeMenu, onToggleDisabled: onToggleDisabled, setSelectedId: setSelectedId }, childId)))] }));
|
|
@@ -15,23 +15,23 @@ import { hasComponent } from "./types";
|
|
|
15
15
|
import EditorTree from './EditorTree';
|
|
16
16
|
import { getAllComponentDefs } from './components/ComponentRegistry';
|
|
17
17
|
import { createComponentData } from './prefab';
|
|
18
|
+
import { useEditorRef } from './PrefabEditor';
|
|
18
19
|
import { base, colors, inspector, componentCard } from './styles';
|
|
19
20
|
import { usePrefabStore } from './prefabStore';
|
|
20
21
|
function EditorUI({ selectedId, setSelectedId, getPrefab, onReplacePrefab, onImportPrefab, basePath, onUndo, onRedo, canUndo, canRedo }) {
|
|
21
22
|
const [collapsed, setCollapsed] = useState(false);
|
|
22
23
|
const rootId = usePrefabStore(state => state.rootId);
|
|
23
24
|
const selectedNode = usePrefabStore(state => { var _a; return selectedId ? (_a = state.nodesById[selectedId]) !== null && _a !== void 0 ? _a : null : null; });
|
|
24
|
-
const
|
|
25
|
-
const deleteNode = usePrefabStore(state => state.deleteNode);
|
|
25
|
+
const editor = useEditorRef();
|
|
26
26
|
const updateNodeHandler = (update) => {
|
|
27
27
|
if (!selectedId)
|
|
28
28
|
return;
|
|
29
|
-
|
|
29
|
+
editor.update(selectedId, update);
|
|
30
30
|
};
|
|
31
31
|
const deleteNodeHandler = () => {
|
|
32
32
|
if (!selectedId || selectedId === rootId)
|
|
33
33
|
return;
|
|
34
|
-
|
|
34
|
+
editor.remove(selectedId);
|
|
35
35
|
setSelectedId(null);
|
|
36
36
|
};
|
|
37
37
|
return _jsxs(_Fragment, { children: [_jsxs("div", { style: inspector.panel, children: [_jsxs("div", { style: base.header, onClick: () => setCollapsed(!collapsed), children: [_jsx("span", { children: "Inspector" }), _jsx("span", { children: collapsed ? '◀' : '▼' })] }), !collapsed && selectedNode && (_jsx(NodeInspector, { node: selectedNode, updateNode: updateNodeHandler, deleteNode: deleteNodeHandler, basePath: basePath }))] }), _jsx("div", { style: { position: 'absolute', top: 8, left: 8, zIndex: 20 }, children: _jsx(EditorTree, { selectedId: selectedId, setSelectedId: setSelectedId, getPrefab: getPrefab, onReplacePrefab: onReplacePrefab, onImportPrefab: onImportPrefab, onUndo: onUndo, onRedo: onRedo, canUndo: canUndo, canRedo: canRedo }) })] });
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
export type GameEventHandler<TPayload = unknown> = (payload: TPayload) => void;
|
|
2
|
-
export type
|
|
2
|
+
export type ContactEventPayload = {
|
|
3
3
|
sourceEntityId?: string;
|
|
4
4
|
sourceNodeId?: string;
|
|
5
5
|
sourceObject?: unknown;
|
|
6
|
-
sourceRigidBody?: unknown;
|
|
7
6
|
targetEntityId?: string | null;
|
|
8
7
|
targetNodeId?: string | null;
|
|
9
8
|
targetObject?: unknown;
|
|
10
|
-
|
|
11
|
-
rapierEvent?: unknown;
|
|
9
|
+
event?: unknown;
|
|
12
10
|
};
|
|
13
11
|
export type ClickEventPayload = {
|
|
14
12
|
sourceEntityId?: string;
|
|
@@ -26,10 +24,10 @@ export type ClickEventPayload = {
|
|
|
26
24
|
r3fEvent?: unknown;
|
|
27
25
|
};
|
|
28
26
|
export interface GameEventMap {
|
|
29
|
-
'sensor:enter':
|
|
30
|
-
'sensor:exit':
|
|
31
|
-
'collision:enter':
|
|
32
|
-
'collision:exit':
|
|
27
|
+
'sensor:enter': ContactEventPayload;
|
|
28
|
+
'sensor:exit': ContactEventPayload;
|
|
29
|
+
'collision:enter': ContactEventPayload;
|
|
30
|
+
'collision:exit': ContactEventPayload;
|
|
33
31
|
click: ClickEventPayload;
|
|
34
32
|
[eventType: string]: unknown;
|
|
35
33
|
}
|
|
@@ -40,8 +38,4 @@ export declare const gameEvents: {
|
|
|
40
38
|
hasListeners(type: string): boolean;
|
|
41
39
|
};
|
|
42
40
|
export declare function useGameEvent<TType extends string>(type: TType, handler: GameEventHandler<TType extends keyof GameEventMap ? GameEventMap[TType] : unknown>, deps?: React.DependencyList): void;
|
|
43
|
-
export declare function usePhysicsEvent<TType extends string>(type: TType, handler: GameEventHandler<TType extends keyof GameEventMap ? GameEventMap[TType] : unknown>, deps?: React.DependencyList): void;
|
|
44
41
|
export declare function useClickEvent<TType extends string>(type: TType, handler: GameEventHandler<TType extends keyof GameEventMap ? GameEventMap[TType] : unknown>, deps?: React.DependencyList): void;
|
|
45
|
-
export declare function getEntityIdFromRigidBody(rigidBody: {
|
|
46
|
-
userData?: unknown;
|
|
47
|
-
} | null | undefined): string | null;
|
|
@@ -53,14 +53,6 @@ export function useGameEvent(type, handler, deps = []) {
|
|
|
53
53
|
return gameEvents.on(type, stableHandler);
|
|
54
54
|
}, [type, stableHandler]);
|
|
55
55
|
}
|
|
56
|
-
export function usePhysicsEvent(type, handler, deps = []) {
|
|
57
|
-
useGameEvent(type, handler, deps);
|
|
58
|
-
}
|
|
59
56
|
export function useClickEvent(type, handler, deps = []) {
|
|
60
57
|
useGameEvent(type, handler, deps);
|
|
61
58
|
}
|
|
62
|
-
export function getEntityIdFromRigidBody(rigidBody) {
|
|
63
|
-
var _a;
|
|
64
|
-
const entityId = (_a = rigidBody === null || rigidBody === void 0 ? void 0 : rigidBody.userData) === null || _a === void 0 ? void 0 : _a.entityId;
|
|
65
|
-
return typeof entityId === 'string' ? entityId : null;
|
|
66
|
-
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import { ThreeEvent } from '@react-three/fiber';
|
|
2
3
|
import { Object3D, Group } from "three";
|
|
3
|
-
import { PhysicsProps } from "./components/PhysicsComponent";
|
|
4
4
|
export type RepeatAxisConfig = {
|
|
5
5
|
axis: 'x' | 'y' | 'z';
|
|
6
6
|
count: number;
|
|
@@ -13,18 +13,19 @@ export type InstanceData = {
|
|
|
13
13
|
id: string;
|
|
14
14
|
sourceId: string;
|
|
15
15
|
locked?: boolean;
|
|
16
|
+
visible?: boolean;
|
|
16
17
|
position: [number, number, number];
|
|
17
18
|
rotation: [number, number, number];
|
|
18
19
|
scale: [number, number, number];
|
|
19
20
|
meshPath: string;
|
|
20
|
-
physics?: PhysicsProps | undefined;
|
|
21
21
|
};
|
|
22
|
-
export declare function GameInstanceProvider({ children, models, onSelect, registerRef, selectedId, editMode }: {
|
|
22
|
+
export declare function GameInstanceProvider({ children, models, onSelect, onClick, registerRef, selectedId, editMode }: {
|
|
23
23
|
children: React.ReactNode;
|
|
24
24
|
models: {
|
|
25
25
|
[filename: string]: Object3D;
|
|
26
26
|
};
|
|
27
27
|
onSelect?: (id: string | null) => void;
|
|
28
|
+
onClick?: (event: ThreeEvent<PointerEvent>, nodeId: string, object: Object3D | null) => void;
|
|
28
29
|
registerRef?: (id: string, obj: Object3D | null) => void;
|
|
29
30
|
selectedId?: string | null;
|
|
30
31
|
editMode?: boolean;
|
|
@@ -38,5 +39,6 @@ export declare const GameInstance: React.ForwardRefExoticComponent<{
|
|
|
38
39
|
position: [number, number, number];
|
|
39
40
|
rotation: [number, number, number];
|
|
40
41
|
scale: [number, number, number];
|
|
41
|
-
|
|
42
|
+
visible?: boolean;
|
|
43
|
+
onClick?: (event: ThreeEvent<PointerEvent>, nodeId: string, object: Object3D | null) => void;
|
|
42
44
|
} & React.RefAttributes<Group<import("three").Object3DEventMap>>>;
|