react-three-game 0.0.85 → 0.0.86

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 (38) hide show
  1. package/README.md +87 -35
  2. package/dist/index.d.ts +3 -7
  3. package/dist/index.js +1 -4
  4. package/dist/tools/prefabeditor/InstanceProvider.d.ts +0 -4
  5. package/dist/tools/prefabeditor/InstanceProvider.js +13 -44
  6. package/dist/tools/prefabeditor/PrefabEditor.d.ts +7 -2
  7. package/dist/tools/prefabeditor/PrefabEditor.js +13 -24
  8. package/dist/tools/prefabeditor/PrefabRoot.js +94 -44
  9. package/dist/tools/prefabeditor/{runtime.d.ts → assetRuntime.d.ts} +0 -25
  10. package/dist/tools/prefabeditor/assetRuntime.js +37 -0
  11. package/dist/tools/prefabeditor/components/BufferGeometryComponent.js +4 -2
  12. package/dist/tools/prefabeditor/components/CameraComponent.js +1 -1
  13. package/dist/tools/prefabeditor/components/ComponentRegistry.d.ts +0 -3
  14. package/dist/tools/prefabeditor/components/DataComponent.d.ts +3 -0
  15. package/dist/tools/prefabeditor/components/DataComponent.js +87 -0
  16. package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +1 -1
  17. package/dist/tools/prefabeditor/components/EnvironmentComponent.js +1 -1
  18. package/dist/tools/prefabeditor/components/GeometryComponent.js +4 -2
  19. package/dist/tools/prefabeditor/components/Input.d.ts +2 -13
  20. package/dist/tools/prefabeditor/components/Input.js +0 -55
  21. package/dist/tools/prefabeditor/components/MaterialComponent.js +1 -1
  22. package/dist/tools/prefabeditor/components/ModelComponent.js +3 -3
  23. package/dist/tools/prefabeditor/components/PhysicsComponent.d.ts +4 -0
  24. package/dist/tools/prefabeditor/components/PhysicsComponent.js +69 -132
  25. package/dist/tools/prefabeditor/components/PointLightComponent.js +1 -1
  26. package/dist/tools/prefabeditor/components/SoundComponent.js +17 -17
  27. package/dist/tools/prefabeditor/components/SpotLightComponent.js +1 -1
  28. package/dist/tools/prefabeditor/components/index.js +2 -2
  29. package/dist/tools/prefabeditor/types.d.ts +1 -0
  30. package/dist/tools/prefabeditor/types.js +18 -0
  31. package/dist/tools/prefabeditor/usePointerEvents.d.ts +27 -0
  32. package/dist/tools/prefabeditor/usePointerEvents.js +52 -0
  33. package/package.json +1 -1
  34. package/dist/tools/prefabeditor/GameEvents.d.ts +0 -128
  35. package/dist/tools/prefabeditor/GameEvents.js +0 -118
  36. package/dist/tools/prefabeditor/components/ClickComponent.d.ts +0 -3
  37. package/dist/tools/prefabeditor/components/ClickComponent.js +0 -52
  38. package/dist/tools/prefabeditor/runtime.js +0 -184
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ![Scene Editor](assets/editor.gif)
4
4
 
5
- JSON-first 3D game engine for React Three Fiber.
5
+ JSON-first scene mounting and authoring for React Three Fiber.
6
6
 
7
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).
8
8
 
@@ -10,7 +10,7 @@ Built on top of [three.js](https://github.com/mrdoob/three.js), [@react-three/fi
10
10
  * **🎬 Scene Editor** - Edit prefabs visually with hierarchy, inspector, transform gizmos, and play mode.
11
11
  * **⚛️ Physics** - Author rigid bodies directly in prefab data and run them through Rapier.
12
12
  * **🧩 Components** - Build scenes from reusable `GameObject` + component composition.
13
- * **🔧 Runtime Scene API** - Mutate the live world through `Scene`, `Entity`, and `EntityComponent` handles.
13
+ * **🔧 Direct Runtime Access** - Get native `Object3D`, Rapier rigid body, and prefab store access without a parallel engine API.
14
14
  * **⚡ R3F Native** - Use normal React Three Fiber components whenever runtime behavior is clearer in code.
15
15
 
16
16
  ## Documentation
@@ -98,12 +98,10 @@ Open the hosted editor here:
98
98
 
99
99
  * https://prnth.com/react-three-game/editor
100
100
 
101
- ## Prefabs And Scenes
101
+ ## Prefabs And Mounted Scenes
102
102
 
103
103
  `Prefab` is the serializable pure data format.
104
104
 
105
- `Scene` is the live runtime/editor world handle.
106
-
107
105
  That means a saved scene is just a prefab, and the same prefab can be:
108
106
 
109
107
  * edited directly in `PrefabEditor`
@@ -163,7 +161,7 @@ interface GameObject {
163
161
 
164
162
  ## Runtime Mutation
165
163
 
166
- Use the `Scene` API from `PrefabEditorRef` for authored state changes that should stay in prefab data.
164
+ Use native object access for Three.js behavior and the prefab store for authored data changes.
167
165
 
168
166
  ```tsx
169
167
  import { useEffect, useRef } from "react";
@@ -173,48 +171,105 @@ function RaiseBall() {
173
171
  const editorRef = useRef<PrefabEditorRef>(null);
174
172
 
175
173
  useEffect(() => {
176
- const transform = editorRef.current
177
- ?.scene
178
- .find("ball")
179
- ?.getComponent<{ position: [number, number, number] }>("Transform");
180
-
181
- transform?.set("position", [0, 8, 0]);
174
+ editorRef.current?.store.getState().updateNode("ball", (node) => ({
175
+ ...node,
176
+ components: {
177
+ ...node.components,
178
+ transform: {
179
+ type: "Transform",
180
+ properties: {
181
+ ...node.components?.transform?.properties,
182
+ position: [0, 8, 0],
183
+ },
184
+ },
185
+ },
186
+ }));
182
187
  }, []);
183
188
 
184
189
  return <PrefabEditor ref={editorRef} initialPrefab={prefab} />;
185
190
  }
186
191
  ```
187
192
 
188
- Batch related entity changes so they flush as one store revision:
193
+ For live Three.js access, use mounted objects directly:
189
194
 
190
195
  ```tsx
191
- scene.batch(() => {
192
- scene.find("orb1")?.getComponent("Transform")?.set("position", [1, 0, 0]);
193
- scene.find("orb2")?.getComponent("Transform")?.set("position", [-1, 0, 0]);
194
- });
196
+ const ball = editorRef.current?.getObject("ball");
197
+ ball?.rotateY(0.5);
195
198
  ```
196
199
 
197
- Define a component runtime factory with `create(ctx)` for hot runtime behavior that mutates the live Three.js object directly:
200
+ Mounted node metadata is mirrored onto the canonical Three.js wrapper object:
201
+
202
+ * `GameObject.id` -> `object.userData.prefabNodeId`
203
+ * `GameObject.name` -> `object.name` and `object.userData.prefabNodeName`
204
+ * `Data.properties.data` -> merged into `object.userData`
205
+
206
+ That gives you a stable authored id for traversal-based integrations, while still making Three.js name lookup convenient:
198
207
 
199
208
  ```tsx
200
- import type { Component } from "react-three-game";
201
-
202
- const Rotator: Component = {
203
- name: "Rotator",
204
- Editor: () => null,
205
- defaultProperties: { speed: 1 },
206
- create(ctx) {
207
- return {
208
- update(dt) {
209
- const speed = ctx.component.get<number>("speed") ?? 1;
210
- ctx.object.rotation.y += dt * speed;
209
+ const playerByName = editorRef.current?.root?.getObjectByName("Player");
210
+ const playerById = editorRef.current?.root?.getObjectByProperty("userData.prefabNodeId", "player");
211
+ ```
212
+
213
+ Treat names as a convenience surface, not the primary lookup key:
214
+
215
+ * 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
218
+
219
+ You can author extra `userData` from the editor with a `Data` component:
220
+
221
+ ```json
222
+ {
223
+ "data": {
224
+ "faction": "enemy",
225
+ "health": 100,
226
+ "loot": { "table": "crate" }
227
+ }
228
+ }
229
+ ```
230
+
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
+ },
211
249
  },
212
- };
250
+ }),
213
251
  },
214
- };
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
+ ]);
215
270
  ```
216
271
 
217
- `View` handles composition and rendering. `create(ctx)` handles imperative runtime behavior.
272
+ Custom component `View`s should use normal React and R3F behavior, such as `useFrame`, refs, and native Three.js APIs.
218
273
 
219
274
  ## Useful Exports
220
275
 
@@ -224,9 +279,6 @@ const Rotator: Component = {
224
279
  * `PrefabEditorMode`
225
280
  * `Prefab`
226
281
  * `GameObject`
227
- * `Scene`
228
- * `Entity`
229
- * `EntityComponent`
230
282
  * `registerComponent`
231
283
  * `createPrefabStore`
232
284
  * `usePrefabStoreApi`
package/dist/index.d.ts CHANGED
@@ -10,7 +10,6 @@ export { useEditorContext } from './tools/prefabeditor/PrefabEditor';
10
10
  export type { EditorContextType } from './tools/prefabeditor/PrefabEditor';
11
11
  export { createPrefabStore, prefabStoreToPrefab, usePrefabStoreApi } from './tools/prefabeditor/prefabStore';
12
12
  export type { PrefabStoreApi, PrefabStoreState } from './tools/prefabeditor/prefabStore';
13
- export { createScene } from './tools/prefabeditor/scene';
14
13
  export { denormalizePrefab } from './tools/prefabeditor/prefab';
15
14
  export { registerComponent } from './tools/prefabeditor/components/ComponentRegistry';
16
15
  export { FieldRenderer, FieldGroup, ListEditor, Label, Vector3Input, Vector3Field, NumberField, ColorInput, ColorField, StringInput, StringField, BooleanInput, BooleanField, SelectInput, SelectField, } from './tools/prefabeditor/components/Input';
@@ -18,11 +17,10 @@ export { loadJson, saveJson, exportGLB, exportGLBData, regenerateIds, computePar
18
17
  export type { ExportGLBOptions } from './tools/prefabeditor/utils';
19
18
  export { createModelNode, createImageNode, } from './tools/prefabeditor/prefab';
20
19
  export type { PrefabEditorProps, PrefabEditorRef, } from './tools/prefabeditor/PrefabEditor';
21
- export type { SpawnOptions, Scene, Entity, EntityComponent, EntityData, EntityUpdate, PropertyPath, SceneUpdates, } from './tools/prefabeditor/scene';
20
+ export type { SpawnOptions, } from './tools/prefabeditor/scene';
22
21
  export type { PrefabRootProps } from './tools/prefabeditor/PrefabRoot';
23
- export type { AssetRuntime, EntityRuntime, LiveObjectRef, LiveRigidBodyRef } from './tools/prefabeditor/runtime';
24
- export { useAssetRuntime, useEntityRuntime, useEntityObjectRef, useEntityRigidBodyRef } from './tools/prefabeditor/runtime';
25
- export type { ComponentInstance, ComponentRuntimeContext } from './tools/prefabeditor/runtime';
22
+ export type { AssetRuntime, EntityRuntime, LiveObjectRef, LiveRigidBodyRef } from './tools/prefabeditor/assetRuntime';
23
+ export { useAssetRuntime, useEntityRuntime, useEntityObjectRef, useEntityRigidBodyRef } from './tools/prefabeditor/assetRuntime';
26
24
  export type { Component, ComponentViewProps } from './tools/prefabeditor/components/ComponentRegistry';
27
25
  export type { FieldDefinition, FieldType } from './tools/prefabeditor/components/Input';
28
26
  export { MaterialOverridesProvider, useMaterialOverrides } from './tools/prefabeditor/components/MaterialComponent';
@@ -30,8 +28,6 @@ export type { MaterialOverrides } from './tools/prefabeditor/components/Material
30
28
  export type { Prefab, GameObject, ComponentData } from './tools/prefabeditor/types';
31
29
  export { findComponent, findComponentEntry, hasComponent } from './tools/prefabeditor/types';
32
30
  export { float, positionLocal, sin, time, uniform, vec3, } from 'three/tsl';
33
- export { gameEvents, useGameEvent, usePhysicsEvent, useClickEvent, getEntityIdFromRigidBody } from './tools/prefabeditor/GameEvents';
34
- export type { GameEventType, GameEventMap, GameEventPayload, PhysicsEventType, InteractionEventType, PhysicsEventPayload, ClickEventPayload } from './tools/prefabeditor/GameEvents';
35
31
  export { loadFiles } from './tools/dragdrop/DragDropLoader';
36
32
  export type { AssetLoadOptions } from './tools/dragdrop/DragDropLoader';
37
33
  export { loadModel, loadSound, loadTexture } from './tools/dragdrop/modelLoader';
package/dist/index.js CHANGED
@@ -10,7 +10,6 @@ export { default as PrefabRoot } from './tools/prefabeditor/PrefabRoot';
10
10
  export { useEditorContext } from './tools/prefabeditor/PrefabEditor';
11
11
  // Prefab Editor - Store & Scene API
12
12
  export { createPrefabStore, prefabStoreToPrefab, usePrefabStoreApi } from './tools/prefabeditor/prefabStore';
13
- export { createScene } from './tools/prefabeditor/scene';
14
13
  export { denormalizePrefab } from './tools/prefabeditor/prefab';
15
14
  // Prefab Editor - Component Registry
16
15
  export { registerComponent } from './tools/prefabeditor/components/ComponentRegistry';
@@ -19,12 +18,10 @@ export { FieldRenderer, FieldGroup, ListEditor, Label, Vector3Input, Vector3Fiel
19
18
  // Prefab Editor - Utils
20
19
  export { loadJson, saveJson, exportGLB, exportGLBData, regenerateIds, computeParentWorldMatrix, } from './tools/prefabeditor/utils';
21
20
  export { createModelNode, createImageNode, } from './tools/prefabeditor/prefab';
22
- export { useAssetRuntime, useEntityRuntime, useEntityObjectRef, useEntityRigidBodyRef } from './tools/prefabeditor/runtime';
21
+ export { useAssetRuntime, useEntityRuntime, useEntityObjectRef, useEntityRigidBodyRef } from './tools/prefabeditor/assetRuntime';
23
22
  export { MaterialOverridesProvider, useMaterialOverrides } from './tools/prefabeditor/components/MaterialComponent';
24
23
  export { findComponent, findComponentEntry, hasComponent } from './tools/prefabeditor/types';
25
24
  export { float, positionLocal, sin, time, uniform, vec3, } from 'three/tsl';
26
- // Game Events (physics + custom events)
27
- export { gameEvents, useGameEvent, usePhysicsEvent, useClickEvent, getEntityIdFromRigidBody } from './tools/prefabeditor/GameEvents';
28
25
  // Asset Loading
29
26
  export { loadFiles } from './tools/dragdrop/DragDropLoader';
30
27
  export { loadModel, loadSound, loadTexture } from './tools/dragdrop/modelLoader';
@@ -12,8 +12,6 @@ export declare function getRepeatAxesFromModelProperties(properties: Record<stri
12
12
  export type InstanceData = {
13
13
  id: string;
14
14
  sourceId: string;
15
- clickable?: boolean;
16
- clickEventName?: string;
17
15
  locked?: boolean;
18
16
  position: [number, number, number];
19
17
  rotation: [number, number, number];
@@ -35,8 +33,6 @@ export declare function useInstanceCheck(id: string): boolean;
35
33
  export declare const GameInstance: React.ForwardRefExoticComponent<{
36
34
  id: string;
37
35
  sourceId?: string;
38
- clickable?: boolean;
39
- clickEventName?: string;
40
36
  modelUrl: string;
41
37
  locked?: boolean;
42
38
  position: [number, number, number];
@@ -15,8 +15,7 @@ import { Merged, useHelper } from '@react-three/drei';
15
15
  import { InstancedRigidBodies } from "@react-three/rapier";
16
16
  import { ActiveCollisionTypes } from "@dimforge/rapier3d-compat";
17
17
  import { Mesh, Matrix4, Vector3, Quaternion, Euler, BoxHelper } from "three";
18
- import { gameEvents, getEntityIdFromRigidBody } from "./GameEvents";
19
- import { useClickValid } from "./useClickValid";
18
+ import { usePointerEvents } from "./usePointerEvents";
20
19
  export const DEFAULT_REPEAT_AXES = [{ axis: 'x', count: 1, offset: 1 }];
21
20
  export function normalizeRepeatAxes(value) {
22
21
  if (!Array.isArray(value)) {
@@ -103,32 +102,9 @@ function hasPhysics(instance) {
103
102
  function getColliderType(physics) {
104
103
  return physics.colliders || (physics.type === 'fixed' ? 'trimesh' : 'hull');
105
104
  }
106
- function emitPhysicsEvent(sourceId, eventName, payload) {
107
- if (!eventName)
108
- return;
109
- gameEvents.emit(eventName, {
110
- sourceEntityId: sourceId,
111
- targetEntityId: getEntityIdFromRigidBody(payload.other.rigidBody),
112
- targetRigidBody: payload.other.rigidBody,
113
- });
114
- }
115
- function emitClick(sourceId, instanceId, eventName, event) {
116
- gameEvents.emit(eventName, {
117
- sourceEntityId: sourceId,
118
- instanceEntityId: instanceId && instanceId !== sourceId ? instanceId : undefined,
119
- point: [event.point.x, event.point.y, event.point.z],
120
- button: event.button,
121
- altKey: event.nativeEvent.altKey,
122
- ctrlKey: event.nativeEvent.ctrlKey,
123
- metaKey: event.nativeEvent.metaKey,
124
- shiftKey: event.nativeEvent.shiftKey,
125
- });
126
- }
127
105
  function instanceEquals(a, b) {
128
106
  return a.id === b.id &&
129
107
  a.sourceId === b.sourceId &&
130
- a.clickable === b.clickable &&
131
- a.clickEventName === b.clickEventName &&
132
108
  a.locked === b.locked &&
133
109
  a.meshPath === b.meshPath &&
134
110
  arrayEquals(a.position, b.position) &&
@@ -245,7 +221,7 @@ function InstancedRigidGroup({ group, modelKey, partCount, flatMeshes, onSelect,
245
221
  const rigidBodiesRef = useRef(null);
246
222
  const instances = useMemo(() => group.instances.filter(hasPhysics).map(inst => {
247
223
  const _a = inst.physics, { activeCollisionTypes: _activeCollisionTypes, colliders: _colliders, userData } = _a, rigidBodyProps = __rest(_a, ["activeCollisionTypes", "colliders", "userData"]);
248
- return Object.assign(Object.assign({ key: inst.id, position: inst.position, rotation: inst.rotation, scale: inst.scale }, rigidBodyProps), { colliders: getColliderType(inst.physics), userData: Object.assign(Object.assign({}, userData), { entityId: inst.sourceId }), onIntersectionEnter: (payload) => emitPhysicsEvent(inst.sourceId, inst.physics.sensorEnterEventName, payload), onIntersectionExit: (payload) => emitPhysicsEvent(inst.sourceId, inst.physics.sensorExitEventName, payload), onCollisionEnter: (payload) => emitPhysicsEvent(inst.sourceId, inst.physics.collisionEnterEventName, payload), onCollisionExit: (payload) => emitPhysicsEvent(inst.sourceId, inst.physics.collisionExitEventName, payload) });
224
+ return Object.assign(Object.assign({ key: inst.id, position: inst.position, rotation: inst.rotation, scale: inst.scale }, rigidBodyProps), { colliders: getColliderType(inst.physics), userData: Object.assign(Object.assign({}, userData), { entityId: inst.sourceId }) });
249
225
  }), [group.instances]);
250
226
  // Apply scale to visual meshes (InstancedRigidBodies only scales colliders, not visuals)
251
227
  useEffect(() => {
@@ -317,12 +293,8 @@ function InstancedRigidGroup({ group, modelKey, partCount, flatMeshes, onSelect,
317
293
  onSelect(instance.sourceId);
318
294
  return;
319
295
  }
320
- if (!instance.clickable)
321
- return;
322
- e.stopPropagation();
323
- emitClick(instance.sourceId, instance.id, instance.clickEventName || 'click', e);
324
296
  };
325
- const shouldHandleClick = editMode || group.instances.some(inst => inst.clickable);
297
+ const shouldHandleClick = editMode;
326
298
  // Add key to force remount when instance count changes significantly (helps with cleanup)
327
299
  const rigidBodyKey = `rb_${modelKey}_${group.instances.map(inst => `${inst.id}:${getPhysicsSignature(inst.physics)}`).join('|')}`;
328
300
  return (_jsx(InstancedRigidBodies, { ref: rigidBodiesRef, instances: instances, children: Array.from({ length: partCount }).map((_, i) => {
@@ -344,14 +316,15 @@ function InstanceGroupItem({ instance, InstanceComponents, onSelect, registerRef
344
316
  const isLocked = Boolean(instance.locked);
345
317
  const isSelected = selectedId === instance.id || selectedId === instance.sourceId;
346
318
  const canSelect = editMode && !isLocked;
347
- const canClick = !editMode && Boolean(instance.clickable);
348
- const pointerHandlers = useClickValid(canSelect || canClick, (e) => {
349
- if (editMode) {
350
- onSelect === null || onSelect === void 0 ? void 0 : onSelect(instance.sourceId);
351
- }
352
- else if (instance.clickable) {
353
- emitClick(instance.sourceId, instance.id, instance.clickEventName || 'click', e);
354
- }
319
+ const canClick = false;
320
+ const pointerHandlers = usePointerEvents({
321
+ enabled: canSelect || canClick,
322
+ entity: instance,
323
+ onClick: () => {
324
+ if (editMode) {
325
+ onSelect === null || onSelect === void 0 ? void 0 : onSelect(instance.sourceId);
326
+ }
327
+ },
355
328
  });
356
329
  // Use BoxHelper when object is selected in edit mode
357
330
  useHelper(editMode && isSelected ? groupRef : null, BoxHelper, 'cyan');
@@ -370,7 +343,7 @@ export function useInstanceCheck(id) {
370
343
  return (_a = ctx === null || ctx === void 0 ? void 0 : ctx.hasInstance(id)) !== null && _a !== void 0 ? _a : false;
371
344
  }
372
345
  // GameInstance component: registers an instance for batch rendering (renders nothing itself)
373
- export const GameInstance = React.forwardRef(({ id, sourceId, clickable = false, clickEventName, modelUrl, locked = false, position, rotation, scale, physics = undefined, }, ref) => {
346
+ export const GameInstance = React.forwardRef(({ id, sourceId, modelUrl, locked = false, position, rotation, scale, physics = undefined, }, ref) => {
374
347
  const ctx = useContext(GameInstanceContext);
375
348
  const addInstance = ctx === null || ctx === void 0 ? void 0 : ctx.addInstance;
376
349
  const removeInstance = ctx === null || ctx === void 0 ? void 0 : ctx.removeInstance;
@@ -381,8 +354,6 @@ export const GameInstance = React.forwardRef(({ id, sourceId, clickable = false,
381
354
  const instance = useMemo(() => ({
382
355
  id,
383
356
  sourceId: sourceId !== null && sourceId !== void 0 ? sourceId : id,
384
- clickable,
385
- clickEventName,
386
357
  locked,
387
358
  meshPath: modelUrl,
388
359
  position,
@@ -392,8 +363,6 @@ export const GameInstance = React.forwardRef(({ id, sourceId, clickable = false,
392
363
  }), [
393
364
  id,
394
365
  sourceId,
395
- clickable,
396
- clickEventName,
397
366
  locked,
398
367
  modelUrl,
399
368
  positionX,
@@ -2,18 +2,23 @@ import GameCanvas from "../../shared/GameCanvas";
2
2
  import { Object3D, Texture } from "three";
3
3
  import { GameObject, Prefab } from "./types";
4
4
  import type { ExportGLBOptions } from "./utils";
5
- import { type Scene, type SpawnOptions } from "./scene";
5
+ import { type PrefabStoreApi } from "./prefabStore";
6
+ import type { SpawnOptions } from "./scene";
6
7
  export interface PrefabEditorRef {
8
+ root: Object3D | null;
9
+ store: PrefabStoreApi;
10
+ getObject: (nodeId: string) => Object3D | null;
11
+ getRigidBody: (nodeId: string) => any;
7
12
  screenshot: () => void;
8
13
  exportGLB: (options?: ExportGLBOptions) => Promise<ArrayBuffer | undefined>;
9
14
  exportGLBData: () => Promise<ArrayBuffer | undefined>;
10
15
  clearSelection: () => Promise<void>;
11
16
  save: () => Prefab;
12
- scene: Scene;
13
17
  load: (prefab: Prefab, options?: {
14
18
  resetHistory?: boolean;
15
19
  notifyChange?: boolean;
16
20
  }) => void;
21
+ addNode: (node: GameObject, options?: SpawnOptions) => GameObject;
17
22
  addModel: (path: string, model: Object3D, options?: SpawnOptions) => GameObject;
18
23
  addTexture: (path: string, texture: Texture, options?: SpawnOptions) => GameObject;
19
24
  }
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
11
11
  import { MapControls, TransformControls } from "@react-three/drei";
12
12
  import GameCanvas from "../../shared/GameCanvas";
13
- import { useCallback, useEffect, useMemo, useReducer, useRef, useState, forwardRef, useImperativeHandle, createContext, useContext } from "react";
13
+ import { useCallback, useEffect, useReducer, useRef, useState, forwardRef, useImperativeHandle, createContext, useContext } from "react";
14
14
  import { findComponentEntry } from "./types";
15
15
  import PrefabRoot from "./PrefabRoot";
16
16
  import { Physics } from "@react-three/rapier";
@@ -20,7 +20,6 @@ import { computeParentWorldMatrix, decompose, exportGLB as exportGLBFile, export
20
20
  import { loadFiles } from "../dragdrop";
21
21
  import { denormalizePrefab, createImageNode, createModelNode, createNode } from './prefab';
22
22
  import { createPrefabStore, PrefabStoreProvider } from "./prefabStore";
23
- import { createScene } from "./scene";
24
23
  function isObjectAttachedToRoot(root, object) {
25
24
  if (!root || !object)
26
25
  return false;
@@ -53,7 +52,6 @@ const DEFAULT_PREFAB = {
53
52
  root: createNode('Root', {}, { id: 'root' })
54
53
  };
55
54
  const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode: initialMode = PrefabEditorMode.Edit, onChange, showUI = true, enableWindowDrop = true, canvasProps, uiPlugins, children }, ref) => {
56
- var _a, _b;
57
55
  const [mode, setMode] = useState(initialMode);
58
56
  const [selectedId, setSelectedId] = useState(null);
59
57
  const [transformMode, setTransformMode] = useState("translate");
@@ -73,6 +71,7 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode
73
71
  const onChangeRef = useRef(onChange);
74
72
  const isEditMode = mode === PrefabEditorMode.Edit;
75
73
  const getPrefab = useCallback(() => denormalizePrefab(prefabStore.getState()), [prefabStore]);
74
+ const getRootObject = useCallback(() => { var _a, _b; return (_b = (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.root) !== null && _b !== void 0 ? _b : null; }, []);
76
75
  const getObject = useCallback((nodeId) => { var _a, _b; return (_b = (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.getObject(nodeId)) !== null && _b !== void 0 ? _b : null; }, []);
77
76
  const getRigidBody = useCallback((nodeId) => { var _a, _b; return (_b = (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.getRigidBody(nodeId)) !== null && _b !== void 0 ? _b : null; }, []);
78
77
  const handleObjectRefChange = useCallback((nodeId) => {
@@ -173,7 +172,7 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode
173
172
  return () => unsubscribe();
174
173
  }, [prefabStore, selectedId]);
175
174
  const selectedObject = selectedId ? getObject(selectedId) : null;
176
- const transformObject = isObjectAttachedToRoot((_b = (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.root) !== null && _b !== void 0 ? _b : null, selectedObject)
175
+ const transformObject = isObjectAttachedToRoot(getRootObject(), selectedObject)
177
176
  ? selectedObject
178
177
  : null;
179
178
  const addNode = useCallback((node, options) => {
@@ -251,21 +250,19 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode
251
250
  });
252
251
  }), [setSelection]);
253
252
  const handleExportGLB = useCallback((...args_1) => __awaiter(void 0, [...args_1], void 0, function* (options = {}) {
254
- var _a;
255
253
  yield clearSelection();
256
- const rootObject = (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.root;
254
+ const rootObject = getRootObject();
257
255
  if (!rootObject)
258
256
  return;
259
257
  return exportGLBFile(rootObject, Object.assign({ filename: `${prefabStore.getState().prefabName || 'prefab'}.glb` }, options));
260
- }), [clearSelection, prefabStore]);
258
+ }), [clearSelection, getRootObject, prefabStore]);
261
259
  const handleExportGLBData = useCallback(() => __awaiter(void 0, void 0, void 0, function* () {
262
- var _a;
263
260
  yield clearSelection();
264
- const rootObject = (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.root;
261
+ const rootObject = getRootObject();
265
262
  if (!rootObject)
266
263
  return;
267
264
  return exportGLBData(rootObject);
268
- }), [clearSelection]);
265
+ }), [clearSelection, getRootObject]);
269
266
  const handleFocusNode = useCallback((nodeId) => {
270
267
  const object = getObject(nodeId);
271
268
  const controls = controlsRef.current;
@@ -274,18 +271,6 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode
274
271
  return;
275
272
  focusCameraOnObject(object, camera, controls.target, () => { var _a; return (_a = controls.update) === null || _a === void 0 ? void 0 : _a.call(controls); });
276
273
  }, [getObject]);
277
- const scene = useMemo(() => createScene({
278
- getRootId: () => prefabStore.getState().rootId,
279
- getNode: (id) => { var _a; return (_a = prefabStore.getState().nodesById[id]) !== null && _a !== void 0 ? _a : null; },
280
- getChildIds: (id) => { var _a; return (_a = prefabStore.getState().childIdsById[id]) !== null && _a !== void 0 ? _a : []; },
281
- getParentId: (id) => { var _a; return (_a = prefabStore.getState().parentIdById[id]) !== null && _a !== void 0 ? _a : null; },
282
- updateNode: (id, update) => prefabStore.getState().updateNode(id, update),
283
- updateNodes: (updates) => prefabStore.getState().updateNodes(Object.entries(updates).map(([id, update]) => ({ id, update }))),
284
- addNode: (node, options) => addNode(node, options).id,
285
- removeNode: (id) => prefabStore.getState().deleteNode(id),
286
- getObject,
287
- getRigidBody,
288
- }), [addNode, getObject, getRigidBody, prefabStore]);
289
274
  const handleTransformChange = () => {
290
275
  if (!selectedId)
291
276
  return;
@@ -342,16 +327,20 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode
342
327
  };
343
328
  }, [addModel, addTexture, isEditMode, enableWindowDrop]);
344
329
  useImperativeHandle(ref, () => ({
330
+ root: getRootObject(),
331
+ store: prefabStore,
332
+ getObject,
333
+ getRigidBody,
345
334
  screenshot: handleScreenshot,
346
335
  exportGLB: handleExportGLB,
347
336
  exportGLBData: handleExportGLBData,
348
337
  clearSelection,
349
338
  save: getPrefab,
350
- scene,
351
339
  load: loadPrefab,
340
+ addNode,
352
341
  addModel,
353
342
  addTexture
354
- }), [addModel, addTexture, clearSelection, getPrefab, handleExportGLB, handleExportGLBData, handleScreenshot, loadPrefab, scene]);
343
+ }), [addModel, addNode, addTexture, clearSelection, getObject, getPrefab, getRigidBody, getRootObject, handleExportGLB, handleExportGLBData, handleScreenshot, loadPrefab, prefabStore]);
355
344
  const content = (_jsxs(_Fragment, { children: [isEditMode ? _jsx("gridHelper", { args: [10, 10], position: [0, -1, 0] }) : null, _jsx(PrefabRoot, { ref: prefabRootRef, store: prefabStore, editMode: isEditMode, selectedId: selectedId, onSelect: setSelection, onObjectRefChange: handleObjectRefChange, basePath: basePath }), children] }));
356
345
  const handleCanvasCreated = useCallback((state) => {
357
346
  var _a;