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.
Files changed (39) hide show
  1. package/README.md +81 -88
  2. package/dist/helpers/index.d.ts +0 -3
  3. package/dist/helpers/index.js +1 -8
  4. package/dist/index.d.ts +10 -10
  5. package/dist/index.js +7 -6
  6. package/dist/tools/prefabeditor/EditorTree.js +10 -14
  7. package/dist/tools/prefabeditor/EditorUI.js +4 -4
  8. package/dist/tools/prefabeditor/GameEvents.d.ts +6 -12
  9. package/dist/tools/prefabeditor/GameEvents.js +0 -8
  10. package/dist/tools/prefabeditor/InstanceProvider.d.ts +6 -4
  11. package/dist/tools/prefabeditor/InstanceProvider.js +84 -199
  12. package/dist/tools/prefabeditor/PrefabEditor.d.ts +12 -21
  13. package/dist/tools/prefabeditor/PrefabEditor.js +138 -146
  14. package/dist/tools/prefabeditor/PrefabRoot.d.ts +30 -11
  15. package/dist/tools/prefabeditor/PrefabRoot.js +182 -139
  16. package/dist/tools/prefabeditor/assetRuntime.d.ts +9 -13
  17. package/dist/tools/prefabeditor/assetRuntime.js +13 -13
  18. package/dist/tools/prefabeditor/components/BufferGeometryComponent.js +1 -1
  19. package/dist/tools/prefabeditor/components/CameraComponent.js +2 -2
  20. package/dist/tools/prefabeditor/components/ComponentRegistry.d.ts +3 -3
  21. package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +2 -2
  22. package/dist/tools/prefabeditor/components/Input.js +5 -9
  23. package/dist/tools/prefabeditor/components/ModelComponent.js +4 -6
  24. package/dist/tools/prefabeditor/components/PointLightComponent.js +2 -2
  25. package/dist/tools/prefabeditor/components/SoundComponent.js +2 -2
  26. package/dist/tools/prefabeditor/components/SpotLightComponent.js +2 -2
  27. package/dist/tools/prefabeditor/components/index.js +0 -2
  28. package/dist/tools/prefabeditor/prefab.d.ts +1 -2
  29. package/dist/tools/prefabeditor/prefab.js +2 -3
  30. package/dist/tools/prefabeditor/prefabStore.d.ts +0 -6
  31. package/dist/tools/prefabeditor/prefabStore.js +1 -33
  32. package/dist/tools/prefabeditor/types.d.ts +1 -0
  33. package/dist/tools/prefabeditor/usePointerEvents.d.ts +3 -3
  34. package/dist/tools/prefabeditor/usePointerEvents.js +5 -5
  35. package/package.json +49 -51
  36. package/dist/tools/prefabeditor/components/PhysicsComponent.d.ts +0 -26
  37. package/dist/tools/prefabeditor/components/PhysicsComponent.js +0 -302
  38. package/dist/tools/prefabeditor/scene.d.ts +0 -70
  39. package/dist/tools/prefabeditor/scene.js +0 -237
package/README.md CHANGED
@@ -1,16 +1,15 @@
1
1
  # react-three-game
2
2
 
3
- ![Scene Editor](assets/editor.gif)
3
+ ![Prefab Editor](assets/editor.gif)
4
4
 
5
- JSON-first scene mounting and authoring for React Three Fiber.
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/rapier](https://github.com/pmndrs/react-three-rapier).
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 scenes as serializable JSON and load them on their own or inside other scenes.
10
- * **🎬 Scene Editor** - Edit prefabs visually with hierarchy, inspector, transform gizmos, and play mode.
11
- * **⚛️ Physics** - Author rigid bodies directly in prefab data and run them through Rapier.
12
- * **🧩 Components** - Build scenes from reusable `GameObject` + component composition.
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 @react-three/rapier three
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 Scene",
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
- <Physics>
75
- <ambientLight intensity={0.8} />
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 falling sphere above a ground plane.
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 Scenes
93
+ ## Prefabs And Mounted Objects
102
94
 
103
95
  `Prefab` is the serializable pure data format.
104
96
 
105
- That means a saved scene is just a prefab, and the same prefab can be:
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 native object access for Three.js behavior and the prefab store for authored data changes.
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?.store.getState().updateNode("ball", (node) => ({
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, not the primary lookup key:
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
- * `getObject(id)` is still the stable authored-node lookup
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
- For batched authored updates, write through the store once:
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
- * `PrefabRoot`
278
- * `PrefabEditor`
279
- * `PrefabEditorMode`
280
- * `Prefab`
281
- * `GameObject`
282
- * `registerComponent`
283
- * `createPrefabStore`
284
- * `usePrefabStoreApi`
285
- * `useAssetRuntime()` / `useEntityRuntime()`
286
- * `useEntityObjectRef()` / `useEntityRigidBodyRef()`
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
 
@@ -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;
@@ -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], physicsType = "fixed", disabled = false, } = options;
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/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 { createPrefabStore, prefabStoreToPrefab, usePrefabStoreApi } from './tools/prefabeditor/prefabStore';
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, getEntityIdFromRigidBody, useClickEvent, useGameEvent, usePhysicsEvent } from './tools/prefabeditor/GameEvents';
15
- export type { ClickEventPayload, GameEventHandler, GameEventMap, PhysicsEventPayload } from './tools/prefabeditor/GameEvents';
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, EntityRuntime, LiveObjectRef, LiveRigidBodyRef } from './tools/prefabeditor/assetRuntime';
25
- export { useAssetRuntime, useEntityRuntime, useEntityObjectRef, useEntityRigidBodyRef } from './tools/prefabeditor/assetRuntime';
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/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
- // Prefab Editor - Store & Scene API
12
- export { createPrefabStore, prefabStoreToPrefab, usePrefabStoreApi } from './tools/prefabeditor/prefabStore';
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, getEntityIdFromRigidBody, useClickEvent, useGameEvent, usePhysicsEvent } from './tools/prefabeditor/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, useEntityRuntime, useEntityObjectRef, useEntityRigidBodyRef } from './tools/prefabeditor/assetRuntime';
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
- addChild(parentId, newNode);
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 = duplicateNode(nodeId);
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
- deleteNode(nodeId);
43
+ editor.remove(nodeId);
48
44
  if (selectedId === nodeId)
49
45
  setSelectedId(null);
50
46
  };
51
47
  const handleToggleDisabled = (nodeId) => {
52
- toggleNodeFlag(nodeId, 'disabled');
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
- toggleNodeFlag(nodeId, 'locked');
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
- moveNode(draggedId, targetId, getDropPosition(e, isRoot));
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: "Scene" })] }), !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) => {
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: "Scene Actions", style: tree.iconButton, onClick: (e) => {
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 updateNode = usePrefabStore(state => state.updateNode);
25
- const deleteNode = usePrefabStore(state => state.deleteNode);
25
+ const editor = useEditorRef();
26
26
  const updateNodeHandler = (update) => {
27
27
  if (!selectedId)
28
28
  return;
29
- updateNode(selectedId, update);
29
+ editor.update(selectedId, update);
30
30
  };
31
31
  const deleteNodeHandler = () => {
32
32
  if (!selectedId || selectedId === rootId)
33
33
  return;
34
- deleteNode(selectedId);
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 PhysicsEventPayload = {
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
- targetRigidBody?: unknown;
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': PhysicsEventPayload;
30
- 'sensor:exit': PhysicsEventPayload;
31
- 'collision:enter': PhysicsEventPayload;
32
- 'collision:exit': PhysicsEventPayload;
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
- physics?: PhysicsProps | undefined;
42
+ visible?: boolean;
43
+ onClick?: (event: ThreeEvent<PointerEvent>, nodeId: string, object: Object3D | null) => void;
42
44
  } & React.RefAttributes<Group<import("three").Object3DEventMap>>>;