react-three-game 0.0.92 → 0.0.93

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 (33) hide show
  1. package/README.md +68 -33
  2. package/dist/helpers/index.d.ts +0 -3
  3. package/dist/helpers/index.js +1 -8
  4. package/dist/index.d.ts +5 -8
  5. package/dist/index.js +3 -4
  6. package/dist/tools/prefabeditor/EditorTree.js +2 -2
  7. package/dist/tools/prefabeditor/GameEvents.d.ts +6 -12
  8. package/dist/tools/prefabeditor/GameEvents.js +0 -8
  9. package/dist/tools/prefabeditor/InstanceProvider.d.ts +6 -4
  10. package/dist/tools/prefabeditor/InstanceProvider.js +84 -199
  11. package/dist/tools/prefabeditor/PrefabEditor.d.ts +18 -6
  12. package/dist/tools/prefabeditor/PrefabEditor.js +55 -39
  13. package/dist/tools/prefabeditor/PrefabRoot.d.ts +15 -8
  14. package/dist/tools/prefabeditor/PrefabRoot.js +141 -117
  15. package/dist/tools/prefabeditor/assetRuntime.d.ts +13 -11
  16. package/dist/tools/prefabeditor/assetRuntime.js +15 -15
  17. package/dist/tools/prefabeditor/components/BufferGeometryComponent.js +1 -1
  18. package/dist/tools/prefabeditor/components/CameraComponent.js +2 -2
  19. package/dist/tools/prefabeditor/components/ComponentRegistry.d.ts +3 -3
  20. package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +2 -2
  21. package/dist/tools/prefabeditor/components/ModelComponent.js +1 -1
  22. package/dist/tools/prefabeditor/components/PointLightComponent.js +2 -2
  23. package/dist/tools/prefabeditor/components/SoundComponent.js +2 -2
  24. package/dist/tools/prefabeditor/components/SpotLightComponent.js +2 -2
  25. package/dist/tools/prefabeditor/components/index.js +0 -2
  26. package/dist/tools/prefabeditor/types.d.ts +1 -0
  27. package/dist/tools/prefabeditor/usePointerEvents.d.ts +3 -3
  28. package/dist/tools/prefabeditor/usePointerEvents.js +5 -5
  29. package/package.json +1 -3
  30. package/dist/tools/prefabeditor/components/PhysicsComponent.d.ts +0 -26
  31. package/dist/tools/prefabeditor/components/PhysicsComponent.js +0 -302
  32. package/dist/tools/prefabeditor/scene.d.ts +0 -70
  33. package/dist/tools/prefabeditor/scene.js +0 -237
@@ -1,12 +1,13 @@
1
- import { Group, Matrix4, Object3D, Texture } from "three";
1
+ import { type ForwardRefExoticComponent, type RefAttributes } from "react";
2
+ import { Matrix4, Object3D, Texture } from "three";
2
3
  import { ThreeEvent } from "@react-three/fiber";
3
4
  import { GameObject as GameObjectType, Prefab } from "./types";
4
5
  import { LoadedModels } from "../dragdrop";
5
6
  import { PrefabStoreApi } from "./prefabStore";
6
7
  export interface PrefabRootRef {
7
- root: Group | null;
8
- getObject: (nodeId: string) => Object3D | null;
9
- getRigidBody: (nodeId: string) => any;
8
+ root: Object3D | null;
9
+ getNodeObject: (nodeId: string) => Object3D | null;
10
+ getNodeHandle: <T = unknown>(nodeId: string, kind: string) => T | null;
10
11
  addModel: (path: string, model: Object3D) => void;
11
12
  addTexture: (path: string, texture: Texture) => void;
12
13
  addSound: (path: string, sound: AudioBuffer) => void;
@@ -14,22 +15,28 @@ export interface PrefabRootRef {
14
15
  export interface PrefabRootProps {
15
16
  editMode?: boolean;
16
17
  data?: Prefab;
17
- store?: PrefabStoreApi;
18
18
  selectedId?: string | null;
19
19
  onSelect?: (id: string | null) => void;
20
- onClick?: (event: ThreeEvent<PointerEvent>, entity: GameObjectType) => void;
20
+ onClick?: (event: ThreeEvent<PointerEvent>, node: GameObjectType) => void;
21
+ onEditNodeClick?: (event: ThreeEvent<PointerEvent>, node: GameObjectType) => void;
21
22
  basePath?: string;
22
23
  }
23
- export declare const PrefabRoot: import("react").ForwardRefExoticComponent<PrefabRootProps & import("react").RefAttributes<PrefabRootRef>>;
24
+ interface PrefabRootInternalProps extends PrefabRootProps {
25
+ store?: PrefabStoreApi;
26
+ }
27
+ export declare const PrefabRootInternal: ForwardRefExoticComponent<PrefabRootInternalProps & RefAttributes<PrefabRootRef>>;
28
+ export declare const PrefabRoot: ForwardRefExoticComponent<PrefabRootProps & RefAttributes<PrefabRootRef>>;
24
29
  export declare function GameObjectRenderer(props: RendererProps): import("react/jsx-runtime").JSX.Element | null;
25
30
  interface RendererProps {
26
31
  nodeId: string;
27
32
  selectedId?: string | null;
28
33
  onSelect?: (id: string) => void;
29
- onClick?: (event: ThreeEvent<PointerEvent>, entity: GameObjectType) => void;
34
+ onClick?: (event: ThreeEvent<PointerEvent>, nodeId: string, object: Object3D | null) => void;
35
+ onEditNodeClick?: (event: ThreeEvent<PointerEvent>, node: GameObjectType) => void;
30
36
  registerRef: (id: string, obj: Object3D | null) => void;
31
37
  loadedModels: LoadedModels;
32
38
  editMode?: boolean;
33
39
  parentMatrix?: Matrix4;
40
+ isVisible?: boolean;
34
41
  }
35
42
  export default PrefabRoot;
@@ -20,9 +20,8 @@ import { builtinComponents } from "./components";
20
20
  import { loadModel, loadSound, loadTexture } from "../dragdrop";
21
21
  import { GameInstance, GameInstanceProvider, getRepeatAxesFromModelProperties, useInstanceCheck } from "./InstanceProvider";
22
22
  import { composeTransform, decompose } from "./utils";
23
- import { isPhysicsProps } from "./components/PhysicsComponent";
24
23
  import { createPrefabStore, PrefabStoreProvider, useOptionalPrefabStoreApi, usePrefabChildIds, usePrefabNode, usePrefabRootId } from "./prefabStore";
25
- import { AssetRuntimeContext, EntityRuntimeScope } from "./assetRuntime";
24
+ import { AssetRuntimeContext, CurrentNodeScope } from "./assetRuntime";
26
25
  import { gameEvents } from "./GameEvents";
27
26
  import { sound as soundManager } from "../../helpers/SoundManager";
28
27
  builtinComponents.forEach(registerComponent);
@@ -36,30 +35,13 @@ function resolveAssetPath(basePath, file) {
36
35
  return file;
37
36
  return file.startsWith("/") ? `${basePath}${file}` : `${basePath}/${file}`;
38
37
  }
39
- /** Check if all model assets required by a node are loaded. */
40
- function isNodeReady(node, loadedModels) {
38
+ /** Check if a model component's assets are loaded. */
39
+ function isNodeReady(model, loadedModels) {
41
40
  var _a;
42
- const model = findComponent(node, "Model");
43
41
  if (!((_a = model === null || model === void 0 ? void 0 : model.properties) === null || _a === void 0 ? void 0 : _a.filename))
44
42
  return true;
45
43
  return Boolean(loadedModels[model.properties.filename]);
46
44
  }
47
- function getNodeClickEventName(node) {
48
- var _a;
49
- const clickComponents = [
50
- findComponent(node, 'BufferGeometry'),
51
- findComponent(node, 'Geometry'),
52
- ];
53
- for (const component of clickComponents) {
54
- if (!((_a = component === null || component === void 0 ? void 0 : component.properties) === null || _a === void 0 ? void 0 : _a.emitClickEvent))
55
- continue;
56
- const eventName = component.properties.clickEventName;
57
- if (typeof eventName === 'string' && eventName.trim()) {
58
- return eventName.trim();
59
- }
60
- }
61
- return null;
62
- }
63
45
  function getNodeMetadataProps(node) {
64
46
  var _a, _b;
65
47
  const nodeName = (_b = (_a = node.name) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : '';
@@ -68,7 +50,7 @@ function getNodeMetadataProps(node) {
68
50
  userData: Object.assign(Object.assign({ prefabNodeId: node.id }, (nodeName ? { prefabNodeName: nodeName } : {})), getNodeUserData(node)),
69
51
  };
70
52
  }
71
- export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSelect, onClick, basePath = "" }, ref) => {
53
+ export const PrefabRootInternal = forwardRef(({ editMode, data, store, selectedId, onSelect, onClick, onEditNodeClick, basePath = "" }, ref) => {
72
54
  var _a;
73
55
  const [models, setModels] = useState({});
74
56
  const [textures, setTextures] = useState({});
@@ -81,8 +63,7 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
81
63
  const failedTextures = useRef(new Set());
82
64
  const failedSounds = useRef(new Set());
83
65
  const objectRefs = useRef({});
84
- const rigidBodyRefs = useRef(new Map());
85
- const rootRef = useRef(null);
66
+ const nodeHandles = useRef(new Map());
86
67
  const parentStore = useOptionalPrefabStoreApi();
87
68
  const [ownedStore] = useState(() => {
88
69
  if (data)
@@ -94,35 +75,53 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
94
75
  const resolvedStore = (_a = store !== null && store !== void 0 ? store : parentStore) !== null && _a !== void 0 ? _a : ownedStore;
95
76
  const usesOwnedStore = resolvedStore === ownedStore;
96
77
  const shouldProvideStoreContext = !parentStore || parentStore !== resolvedStore;
78
+ const rootId = useStore(resolvedStore, state => state.rootId);
97
79
  const assetManifestKey = useStore(resolvedStore, state => state.assetManifestKey);
98
80
  const availableModels = useMemo(() => (Object.assign(Object.assign({}, models), injectedModels)), [models, injectedModels]);
99
81
  const availableTextures = useMemo(() => (Object.assign(Object.assign({}, textures), injectedTextures)), [textures, injectedTextures]);
100
82
  const availableSounds = useMemo(() => (Object.assign(Object.assign({}, sounds), injectedSounds)), [sounds, injectedSounds]);
101
- const getObject = useCallback((id) => {
83
+ const getNodeObject = useCallback((id) => {
102
84
  var _a;
103
85
  return (_a = objectRefs.current[id]) !== null && _a !== void 0 ? _a : null;
104
86
  }, []);
105
- const getRigidBody = useCallback((id) => {
106
- var _a;
107
- return (_a = rigidBodyRefs.current.get(id)) !== null && _a !== void 0 ? _a : null;
87
+ const getNodeHandle = useCallback((id, kind) => {
88
+ var _a, _b;
89
+ return (_b = (_a = nodeHandles.current.get(id)) === null || _a === void 0 ? void 0 : _a.get(kind)) !== null && _b !== void 0 ? _b : null;
90
+ }, []);
91
+ const registerNodeHandle = useCallback((id, kind, handle) => {
92
+ const current = nodeHandles.current.get(id);
93
+ if (handle == null) {
94
+ if (!current)
95
+ return;
96
+ current.delete(kind);
97
+ if (current.size === 0) {
98
+ nodeHandles.current.delete(id);
99
+ }
100
+ return;
101
+ }
102
+ if (current) {
103
+ current.set(kind, handle);
104
+ return;
105
+ }
106
+ nodeHandles.current.set(id, new Map([[kind, handle]]));
108
107
  }, []);
109
108
  useImperativeHandle(ref, () => ({
110
- root: rootRef.current,
111
- getObject,
112
- getRigidBody: (nodeId) => { var _a; return (_a = rigidBodyRefs.current.get(nodeId)) !== null && _a !== void 0 ? _a : null; },
109
+ get root() {
110
+ var _a;
111
+ return (_a = objectRefs.current[rootId]) !== null && _a !== void 0 ? _a : null;
112
+ },
113
+ getNodeObject,
114
+ getNodeHandle,
113
115
  addModel: (path, model) => setInjectedModels(prev => (Object.assign(Object.assign({}, prev), { [path]: model }))),
114
116
  addTexture: (path, texture) => setInjectedTextures(prev => (Object.assign(Object.assign({}, prev), { [path]: texture }))),
115
117
  addSound: (path, sound) => {
116
118
  soundManager.setBuffer(path, sound);
117
119
  setInjectedSounds(prev => (Object.assign(Object.assign({}, prev), { [path]: sound })));
118
120
  },
119
- }), [getObject]);
121
+ }), [getNodeHandle, getNodeObject, rootId]);
120
122
  const registerRef = useCallback((id, obj) => {
121
123
  objectRefs.current[id] = obj;
122
124
  }, []);
123
- const registerRigidBodyRef = useCallback((id, rb) => {
124
- rigidBodyRefs.current.set(id, rb);
125
- }, []);
126
125
  useEffect(() => {
127
126
  if (usesOwnedStore && data) {
128
127
  resolvedStore.getState().replacePrefab(data);
@@ -186,25 +185,87 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
186
185
  syncAssets();
187
186
  }, [resolvedStore, assetManifestKey, basePath, injectedModels, injectedSounds, injectedTextures, models, sounds, textures]);
188
187
  const assetRuntime = useMemo(() => ({
189
- getObject,
190
- getRigidBody,
191
- registerRigidBodyRef,
188
+ registerNodeHandle,
189
+ getNodeHandle,
190
+ getNodeObject,
192
191
  getModel: (path) => { var _a; return (_a = availableModels[path]) !== null && _a !== void 0 ? _a : null; },
193
192
  getTexture: (path) => { var _a; return (_a = availableTextures[path]) !== null && _a !== void 0 ? _a : null; },
194
193
  getSound: (path) => { var _a; return (_a = availableSounds[path]) !== null && _a !== void 0 ? _a : null; },
195
194
  getAssetRevision: () => `${Object.keys(availableTextures).sort().join('|')}::${Object.keys(availableModels).sort().join('|')}`,
196
- }), [getObject, getRigidBody, registerRigidBodyRef, availableModels, availableTextures, availableSounds]);
197
- const content = (_jsx("group", { ref: rootRef, children: _jsx(GameInstanceProvider, { models: availableModels, selectedId: selectedId, editMode: editMode, onSelect: editMode ? onSelect : undefined, registerRef: registerRef, children: _jsx(StoreRootNode, { selectedId: selectedId, onSelect: editMode ? onSelect : undefined, onClick: onClick, registerRef: registerRef, loadedModels: availableModels, editMode: editMode, parentMatrix: IDENTITY }) }) }));
195
+ }), [registerNodeHandle, getNodeHandle, getNodeObject, availableModels, availableTextures, availableSounds]);
196
+ const handleNodeClick = useCallback((event, nodeId, fallbackObject) => {
197
+ const node = resolvedStore.getState().nodesById[nodeId];
198
+ if (!node)
199
+ return;
200
+ const { clickEventName } = analyzeNodeComponents(node);
201
+ emitNodePointerEvent(clickEventName, event, nodeId, node, fallbackObject);
202
+ onClick === null || onClick === void 0 ? void 0 : onClick(event, node);
203
+ }, [onClick, resolvedStore]);
204
+ const content = (_jsx(GameInstanceProvider, { models: availableModels, selectedId: selectedId, editMode: editMode, onSelect: editMode ? onSelect : undefined, onClick: editMode ? undefined : handleNodeClick, registerRef: registerRef, children: _jsx(StoreRootNode, { selectedId: selectedId, onSelect: editMode ? onSelect : undefined, onClick: editMode ? undefined : handleNodeClick, onEditNodeClick: editMode ? onEditNodeClick : undefined, registerRef: registerRef, loadedModels: availableModels, editMode: editMode, parentMatrix: IDENTITY }) }));
198
205
  const runtimeContent = _jsx(AssetRuntimeContext.Provider, { value: assetRuntime, children: content });
199
206
  if (!shouldProvideStoreContext) {
200
207
  return runtimeContent;
201
208
  }
202
209
  return _jsx(PrefabStoreProvider, { store: resolvedStore, children: runtimeContent });
203
210
  });
211
+ export const PrefabRoot = PrefabRootInternal;
204
212
  function StoreRootNode(props) {
205
213
  const rootId = usePrefabRootId();
206
214
  return _jsx(GameObjectRenderer, Object.assign({}, props, { nodeId: rootId }));
207
215
  }
216
+ function getClickEventName(component) {
217
+ var _a;
218
+ if (!((_a = component === null || component === void 0 ? void 0 : component.properties) === null || _a === void 0 ? void 0 : _a.emitClickEvent))
219
+ return null;
220
+ const eventName = component.properties.clickEventName;
221
+ return typeof eventName === 'string' && eventName.trim() ? eventName.trim() : null;
222
+ }
223
+ function analyzeNodeComponents(node) {
224
+ var _a, _b, _c;
225
+ let bufferGeometry;
226
+ let geometry;
227
+ let material;
228
+ let model;
229
+ const composition = [];
230
+ for (const [key, component] of Object.entries((_a = node.components) !== null && _a !== void 0 ? _a : {})) {
231
+ if (!(component === null || component === void 0 ? void 0 : component.type))
232
+ continue;
233
+ switch (component.type) {
234
+ case "Transform":
235
+ break;
236
+ case "BufferGeometry":
237
+ bufferGeometry = component;
238
+ break;
239
+ case "Geometry":
240
+ geometry = component;
241
+ break;
242
+ case "Material":
243
+ material = component;
244
+ break;
245
+ case "Model":
246
+ model = component;
247
+ break;
248
+ default: {
249
+ const def = getComponentDef(component.type);
250
+ if (!(def === null || def === void 0 ? void 0 : def.View))
251
+ break;
252
+ composition.push({
253
+ key,
254
+ View: def.View,
255
+ properties: component.properties,
256
+ });
257
+ break;
258
+ }
259
+ }
260
+ }
261
+ return {
262
+ geometry: bufferGeometry !== null && bufferGeometry !== void 0 ? bufferGeometry : geometry,
263
+ material,
264
+ model,
265
+ clickEventName: (_c = (_b = getClickEventName(bufferGeometry)) !== null && _b !== void 0 ? _b : getClickEventName(geometry)) !== null && _c !== void 0 ? _c : getClickEventName(model),
266
+ composition,
267
+ };
268
+ }
208
269
  function emitNodePointerEvent(eventName, event, nodeId, node, fallbackObject) {
209
270
  var _a;
210
271
  const trimmedEventName = eventName === null || eventName === void 0 ? void 0 : eventName.trim();
@@ -247,21 +308,19 @@ export function GameObjectRenderer(props) {
247
308
  ? _jsx(InstancedNode, Object.assign({}, props), key)
248
309
  : _jsx(StandardNode, Object.assign({}, props), key);
249
310
  }
250
- function InstancedNode({ nodeId, parentMatrix = IDENTITY, editMode, registerRef, onSelect, onClick }) {
311
+ function InstancedNode({ nodeId, parentMatrix = IDENTITY, editMode, registerRef, onSelect, onEditNodeClick, onClick, isVisible = true }) {
251
312
  var _a, _b;
252
313
  const gameObject = usePrefabNode(nodeId);
253
314
  if (!gameObject)
254
315
  return null;
316
+ const analyzedComponents = useMemo(() => analyzeNodeComponents(gameObject), [gameObject]);
255
317
  const localTransform = getNodeTransformProps(gameObject);
256
318
  const isLocked = Boolean(gameObject.locked);
319
+ const nodeVisible = isVisible && !gameObject.hidden;
257
320
  const metadataProps = getNodeMetadataProps(gameObject);
258
- const groupProps = Object.assign(Object.assign({}, metadataProps), { position: localTransform.position, rotation: localTransform.rotation, scale: localTransform.scale });
259
- const physicsData = findComponent(gameObject, "Physics");
260
- const physicsProps = isPhysicsProps(physicsData === null || physicsData === void 0 ? void 0 : physicsData.properties)
261
- ? physicsData === null || physicsData === void 0 ? void 0 : physicsData.properties
262
- : undefined;
263
- const modelUrl = (_b = (_a = findComponent(gameObject, "Model")) === null || _a === void 0 ? void 0 : _a.properties) === null || _b === void 0 ? void 0 : _b.filename;
264
- const instances = useMemo(() => buildRepeatedInstances(gameObject, parentMatrix, modelUrl, physicsProps), [gameObject, modelUrl, parentMatrix, physicsProps]);
321
+ const groupProps = Object.assign(Object.assign({}, metadataProps), { visible: nodeVisible, position: localTransform.position, rotation: localTransform.rotation, scale: localTransform.scale });
322
+ const modelUrl = (_b = (_a = analyzedComponents.model) === null || _a === void 0 ? void 0 : _a.properties) === null || _b === void 0 ? void 0 : _b.filename;
323
+ const instances = useMemo(() => buildRepeatedInstances(gameObject, parentMatrix, modelUrl), [gameObject, modelUrl, parentMatrix]);
265
324
  const groupRef = useRef(null);
266
325
  const handleGroupRef = useCallback((object) => {
267
326
  groupRef.current = object;
@@ -271,24 +330,24 @@ function InstancedNode({ nodeId, parentMatrix = IDENTITY, editMode, registerRef,
271
330
  }, [editMode, nodeId, registerRef]);
272
331
  const editClickHandlers = useClickValid(!!editMode && !isLocked, (event) => {
273
332
  onSelect === null || onSelect === void 0 ? void 0 : onSelect(nodeId);
274
- onClick === null || onClick === void 0 ? void 0 : onClick(event, gameObject);
333
+ onEditNodeClick === null || onEditNodeClick === void 0 ? void 0 : onEditNodeClick(event, gameObject);
275
334
  });
276
- const renderedInstances = instances.map(instance => (_jsx(GameInstance, { id: instance.id, sourceId: gameObject.id, modelUrl: instance.modelUrl, position: instance.position, rotation: instance.rotation, scale: instance.scale, locked: isLocked, physics: instance.physics }, instance.id)));
335
+ const renderedInstances = instances.map(instance => (_jsx(GameInstance, { id: instance.id, sourceId: gameObject.id, modelUrl: instance.modelUrl, position: instance.position, rotation: instance.rotation, scale: instance.scale, visible: nodeVisible, locked: isLocked, onClick: onClick }, instance.id)));
277
336
  if (editMode) {
278
337
  return (_jsxs(_Fragment, { children: [_jsx("group", Object.assign({ ref: handleGroupRef }, groupProps, editClickHandlers, { children: _jsx("mesh", { visible: false, children: _jsx("boxGeometry", { args: [0.01, 0.01, 0.01] }) }) })), renderedInstances] }));
279
338
  }
280
339
  return _jsx(_Fragment, { children: renderedInstances });
281
340
  }
282
- function StandardNode({ nodeId, selectedId, onSelect, onClick, registerRef, loadedModels, editMode, parentMatrix = IDENTITY, }) {
283
- var _a, _b;
341
+ function StandardNode({ nodeId, selectedId, onSelect, onClick, onEditNodeClick, registerRef, loadedModels, editMode, parentMatrix = IDENTITY, isVisible = true, }) {
284
342
  const gameObject = usePrefabNode(nodeId);
285
343
  const childIds = usePrefabChildIds(nodeId);
286
344
  if (!gameObject)
287
345
  return null;
346
+ const analyzedComponents = useMemo(() => analyzeNodeComponents(gameObject), [gameObject]);
288
347
  const isSelected = selectedId === nodeId;
289
348
  const isLocked = Boolean(gameObject.locked);
349
+ const nodeVisible = isVisible && !gameObject.hidden;
290
350
  const stillInstanced = useInstanceCheck(nodeId);
291
- const clickEventName = getNodeClickEventName(gameObject);
292
351
  const metadataProps = getNodeMetadataProps(gameObject);
293
352
  const groupRef = useRef(null);
294
353
  const handleGroupRef = useCallback((object) => {
@@ -297,21 +356,18 @@ function StandardNode({ nodeId, selectedId, onSelect, onClick, registerRef, load
297
356
  }, [nodeId, registerRef]);
298
357
  const editClickHandlers = useClickValid(!!editMode && !isLocked, (event) => {
299
358
  onSelect === null || onSelect === void 0 ? void 0 : onSelect(nodeId);
300
- onClick === null || onClick === void 0 ? void 0 : onClick(event, gameObject);
359
+ onEditNodeClick === null || onEditNodeClick === void 0 ? void 0 : onEditNodeClick(event, gameObject);
301
360
  });
302
- const primaryClickHandlers = !editMode && (clickEventName || onClick)
361
+ const primaryClickHandlers = !editMode && onClick
303
362
  ? {
304
363
  onClick: (event) => {
305
364
  event.stopPropagation();
306
- emitNodePointerEvent(clickEventName, event, nodeId, gameObject, groupRef.current);
307
- onClick === null || onClick === void 0 ? void 0 : onClick(event, gameObject);
365
+ onClick(event, nodeId, groupRef.current);
308
366
  },
309
367
  }
310
368
  : undefined;
311
369
  const world = parentMatrix.clone().multiply(compose(gameObject));
312
- const physics = findComponent(gameObject, "Physics");
313
- const ready = isNodeReady(gameObject, loadedModels);
314
- const hasPhysics = physics && ready && !stillInstanced;
370
+ const ready = isNodeReady(analyzedComponents.model, loadedModels);
315
371
  const transform = getNodeTransformProps(gameObject);
316
372
  const transformProps = {
317
373
  position: transform.position,
@@ -319,40 +375,11 @@ function StandardNode({ nodeId, selectedId, onSelect, onClick, registerRef, load
319
375
  scale: transform.scale,
320
376
  };
321
377
  const groupProps = Object.assign(Object.assign({}, metadataProps), transformProps);
322
- const physicsDef = hasPhysics ? getComponentDef(physics.type) : null;
323
- const isInstanced = (_b = (_a = findComponent(gameObject, "Model")) === null || _a === void 0 ? void 0 : _a.properties) === null || _b === void 0 ? void 0 : _b.instanced;
324
- const physicsKey = `physics_${nodeId}_${isInstanced ? 'instanced' : 'standard'}`;
325
- const renderCtx = { loadedModels, editMode, registerRef };
326
- const childNodes = _jsx(ChildNodes, { childIds: childIds, parentMatrix: world, selectedId: selectedId, onSelect: onSelect, onClick: onClick, registerRef: registerRef, loadedModels: loadedModels, editMode: editMode });
327
- const inner = renderCompositionNode(gameObject, renderCtx, primaryClickHandlers, childNodes);
378
+ const childNodes = _jsx(ChildNodes, { childIds: childIds, parentMatrix: world, selectedId: selectedId, onSelect: onSelect, onClick: onClick, onEditNodeClick: onEditNodeClick, registerRef: registerRef, loadedModels: loadedModels, editMode: editMode, isVisible: nodeVisible });
379
+ const inner = renderNodeContent(analyzedComponents, loadedModels, primaryClickHandlers, childNodes);
328
380
  const editAnchor = editMode ? (_jsx("mesh", { visible: false, children: _jsx("boxGeometry", { args: [0.01, 0.01, 0.01] }) })) : null;
329
- const standardNode = (_jsxs("group", Object.assign({ ref: handleGroupRef }, groupProps, (editMode ? editClickHandlers : undefined), { children: [editAnchor, inner] })));
330
- const physicsNode = hasPhysics && (physicsDef === null || physicsDef === void 0 ? void 0 : physicsDef.View) ? (_jsx(physicsDef.View, Object.assign({ properties: physics.properties }, transformProps, { children: _jsxs("group", Object.assign({ ref: handleGroupRef }, metadataProps, (editMode ? editClickHandlers : undefined), { children: [editAnchor, inner] })) }), physicsKey)) : null;
331
- return (_jsx(EntityRuntimeScope, { nodeId: nodeId, editMode: editMode, isSelected: isSelected, children: physicsNode !== null && physicsNode !== void 0 ? physicsNode : standardNode }));
332
- }
333
- function isRendererHandledComponent(componentType) {
334
- return componentType === "Transform"
335
- || componentType === "BufferGeometry"
336
- || componentType === "Geometry"
337
- || componentType === "Material"
338
- || componentType === "Physics"
339
- || componentType === "Model";
340
- }
341
- function getCompositionComponents(gameObject) {
342
- var _a;
343
- return Object.entries((_a = gameObject.components) !== null && _a !== void 0 ? _a : {}).reduce((result, [key, comp]) => {
344
- if (!(comp === null || comp === void 0 ? void 0 : comp.type) || isRendererHandledComponent(comp.type))
345
- return result;
346
- const def = getComponentDef(comp.type);
347
- if (!(def === null || def === void 0 ? void 0 : def.View))
348
- return result;
349
- result.push({
350
- key,
351
- View: def.View,
352
- properties: comp.properties,
353
- });
354
- return result;
355
- }, []);
381
+ const standardNode = (_jsxs("group", Object.assign({ ref: handleGroupRef }, groupProps, { visible: nodeVisible }, (editMode ? editClickHandlers : undefined), { children: [editAnchor, inner] })));
382
+ return (_jsx(CurrentNodeScope, { nodeId: nodeId, editMode: editMode, isSelected: isSelected, children: standardNode }));
356
383
  }
357
384
  function ChildNodes(_a) {
358
385
  var { childIds, parentMatrix } = _a, props = __rest(_a, ["childIds", "parentMatrix"]);
@@ -370,7 +397,7 @@ function getModelRepeatSettings(node) {
370
397
  repeatAxes: getRepeatAxesFromModelProperties(properties),
371
398
  };
372
399
  }
373
- function buildRepeatedInstances(gameObject, parentMatrix, modelUrl, physics) {
400
+ function buildRepeatedInstances(gameObject, parentMatrix, modelUrl) {
374
401
  if (!modelUrl)
375
402
  return [];
376
403
  const transform = getNodeTransformProps(gameObject);
@@ -407,7 +434,6 @@ function buildRepeatedInstances(gameObject, parentMatrix, modelUrl, physics) {
407
434
  position,
408
435
  rotation,
409
436
  scale,
410
- physics,
411
437
  });
412
438
  }
413
439
  }
@@ -423,32 +449,30 @@ function getNodeTransformProps(node) {
423
449
  scale: (_d = t === null || t === void 0 ? void 0 : t.scale) !== null && _d !== void 0 ? _d : [1, 1, 1],
424
450
  };
425
451
  }
426
- function renderCompositionNode(gameObject, ctx, primaryClickHandlers, childNodes) {
427
- const primaryContent = renderNodePrimaryContent(gameObject, ctx, primaryClickHandlers);
428
- return applyNodeComposition(gameObject, _jsxs(_Fragment, { children: [primaryContent, childNodes] }));
429
- }
430
- function renderNodePrimaryContent(gameObject, ctx, primaryClickHandlers) {
431
- var _a, _b, _c;
432
- const geometry = (_a = findComponent(gameObject, "BufferGeometry")) !== null && _a !== void 0 ? _a : findComponent(gameObject, "Geometry");
433
- const material = findComponent(gameObject, "Material");
434
- const model = findComponent(gameObject, "Model");
435
- const geometryDef = geometry && getComponentDef(geometry.type);
436
- const materialDef = material && getComponentDef(material.type);
437
- const modelDef = model && getComponentDef(model.type);
438
- const geometryProperties = (_b = geometry === null || geometry === void 0 ? void 0 : geometry.properties) !== null && _b !== void 0 ? _b : {};
452
+ function renderNodeContent(analyzedComponents, loadedModels, primaryClickHandlers, childNodes) {
453
+ var _a, _b, _c, _d;
454
+ const geometry = analyzedComponents.geometry;
455
+ const geometryDef = analyzedComponents.geometry && getComponentDef(analyzedComponents.geometry.type);
456
+ const materialDef = analyzedComponents.material && getComponentDef(analyzedComponents.material.type);
457
+ const modelDef = analyzedComponents.model && getComponentDef(analyzedComponents.model.type);
458
+ const geometryProperties = (_a = geometry === null || geometry === void 0 ? void 0 : geometry.properties) !== null && _a !== void 0 ? _a : {};
439
459
  const meshVisible = geometryProperties.visible !== false;
440
460
  const meshCastShadow = meshVisible && geometryProperties.castShadow !== false;
441
461
  const meshReceiveShadow = meshVisible && geometryProperties.receiveShadow !== false;
442
- if ((geometry === null || geometry === void 0 ? void 0 : geometry.type) && (geometryDef === null || geometryDef === void 0 ? void 0 : geometryDef.View)) {
443
- return (_jsxs("mesh", Object.assign({ visible: meshVisible, castShadow: meshCastShadow, receiveShadow: meshReceiveShadow }, primaryClickHandlers, { children: [_jsx(geometryDef.View, { properties: geometry.properties }), material && (materialDef === null || materialDef === void 0 ? void 0 : materialDef.View) && (_jsx(materialDef.View, { properties: material.properties }, "material"))] })));
462
+ let primaryContent = null;
463
+ if (((_b = analyzedComponents.geometry) === null || _b === void 0 ? void 0 : _b.type) && (geometryDef === null || geometryDef === void 0 ? void 0 : geometryDef.View)) {
464
+ primaryContent = (_jsxs("mesh", Object.assign({ visible: meshVisible, castShadow: meshCastShadow, receiveShadow: meshReceiveShadow }, primaryClickHandlers, { children: [_jsx(geometryDef.View, { properties: analyzedComponents.geometry.properties }), analyzedComponents.material && (materialDef === null || materialDef === void 0 ? void 0 : materialDef.View) && (_jsx(materialDef.View, { properties: analyzedComponents.material.properties }, "material"))] })));
444
465
  }
445
- if ((model === null || model === void 0 ? void 0 : model.type) && (modelDef === null || modelDef === void 0 ? void 0 : modelDef.View) && !((_c = model.properties) === null || _c === void 0 ? void 0 : _c.instanced) && isNodeReady(gameObject, ctx.loadedModels)) {
446
- return _jsx(modelDef.View, { properties: model.properties });
466
+ else if (((_c = analyzedComponents.model) === null || _c === void 0 ? void 0 : _c.type)
467
+ && (modelDef === null || modelDef === void 0 ? void 0 : modelDef.View)
468
+ && !((_d = analyzedComponents.model.properties) === null || _d === void 0 ? void 0 : _d.instanced)
469
+ && isNodeReady(analyzedComponents.model, loadedModels)) {
470
+ primaryContent = primaryClickHandlers ? (_jsx("group", Object.assign({}, primaryClickHandlers, { children: _jsx(modelDef.View, { properties: analyzedComponents.model.properties }) }))) : (_jsx(modelDef.View, { properties: analyzedComponents.model.properties }));
447
471
  }
448
- return null;
449
- }
450
- function applyNodeComposition(gameObject, subtree) {
451
- const components = getCompositionComponents(gameObject);
452
- return components.reduce((acc, { key, View, properties }) => (_jsx(View, { properties: properties, children: acc }, key)), subtree);
472
+ let content = _jsxs(_Fragment, { children: [primaryContent, childNodes] });
473
+ for (const { key, View, properties } of analyzedComponents.composition) {
474
+ content = (_jsx(View, { properties: properties, children: content }, key));
475
+ }
476
+ return content;
453
477
  }
454
478
  export default PrefabRoot;
@@ -1,34 +1,36 @@
1
1
  import { type ReactNode } from "react";
2
2
  import type { Object3D, Texture } from "three";
3
3
  export interface AssetRuntime {
4
- registerRigidBodyRef: (id: string, rb: any) => void;
4
+ registerNodeHandle: (id: string, kind: string, handle: unknown) => void;
5
+ getNodeHandle: <T = unknown>(id: string, kind: string) => T | null;
5
6
  getModel: (path: string) => Object3D | null;
6
7
  getTexture: (path: string) => Texture | null;
7
8
  getSound: (path: string) => AudioBuffer | null;
8
9
  getAssetRevision: () => string;
9
- getObject: (id: string) => Object3D | null;
10
- getRigidBody: (id: string) => any;
10
+ getNodeObject: (id: string) => Object3D | null;
11
11
  }
12
12
  export interface AssetRuntimeContextValue extends AssetRuntime {
13
13
  }
14
- export interface EntityRuntime {
14
+ export interface CurrentNodeRuntime {
15
15
  nodeId: string;
16
16
  editMode?: boolean;
17
17
  isSelected?: boolean;
18
- getObject: <T extends Object3D = Object3D>() => T | null;
19
- getRigidBody: <T = any>() => T | null;
18
+ getCurrentNodeObject: <T extends Object3D = Object3D>() => T | null;
19
+ getCurrentNodeHandle: <T = unknown>(kind: string) => T | null;
20
20
  }
21
21
  export interface LiveRef<T> {
22
22
  readonly current: T | null;
23
23
  }
24
24
  export type LiveObjectRef<T extends Object3D = Object3D> = LiveRef<T>;
25
- export type LiveRigidBodyRef<T = any> = LiveRef<T>;
25
+ export type LiveHandleRef<T = unknown> = LiveRef<T>;
26
+ export type CurrentNodeObjectRef<T extends Object3D = Object3D> = LiveObjectRef<T>;
27
+ export type CurrentNodeHandleRef<T = unknown> = LiveHandleRef<T>;
26
28
  export declare const AssetRuntimeContext: import("react").Context<AssetRuntimeContextValue | null>;
27
29
  export declare function useAssetRuntime(): AssetRuntime;
28
- export declare function useEntityRuntime(): EntityRuntime;
29
- export declare function useEntityObjectRef<T extends Object3D = Object3D>(): LiveRef<T>;
30
- export declare function useEntityRigidBodyRef<T = any>(): LiveRef<T>;
31
- export declare function EntityRuntimeScope({ nodeId, editMode, isSelected, children, }: {
30
+ export declare function useCurrentNode(): CurrentNodeRuntime;
31
+ export declare function useCurrentNodeObject<T extends Object3D = Object3D>(): LiveRef<T>;
32
+ export declare function useCurrentNodeHandle<T = unknown>(kind: string): LiveRef<T>;
33
+ export declare function CurrentNodeScope({ nodeId, editMode, isSelected, children, }: {
32
34
  nodeId: string;
33
35
  editMode?: boolean;
34
36
  isSelected?: boolean;
@@ -1,37 +1,37 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { createContext, useContext, useMemo } from "react";
3
3
  export const AssetRuntimeContext = createContext(null);
4
- const EntityRuntimeContext = createContext(null);
4
+ const CurrentNodeRuntimeContext = createContext(null);
5
5
  export function useAssetRuntime() {
6
6
  const ctx = useContext(AssetRuntimeContext);
7
7
  if (!ctx)
8
8
  throw new Error("useAssetRuntime must be used inside <PrefabRoot>");
9
9
  return ctx;
10
10
  }
11
- export function useEntityRuntime() {
12
- const ctx = useContext(EntityRuntimeContext);
11
+ export function useCurrentNode() {
12
+ const ctx = useContext(CurrentNodeRuntimeContext);
13
13
  if (!ctx)
14
- throw new Error("useEntityRuntime must be used inside a component View rendered by <PrefabRoot>");
14
+ throw new Error("useCurrentNode must be used inside a component View rendered by <PrefabRoot>");
15
15
  return ctx;
16
16
  }
17
- export function useEntityObjectRef() {
18
- const { getObject } = useEntityRuntime();
19
- return useMemo(() => ({ get current() { return getObject(); } }), [getObject]);
17
+ export function useCurrentNodeObject() {
18
+ const { getCurrentNodeObject } = useCurrentNode();
19
+ return useMemo(() => ({ get current() { return getCurrentNodeObject(); } }), [getCurrentNodeObject]);
20
20
  }
21
- export function useEntityRigidBodyRef() {
22
- const { getRigidBody } = useEntityRuntime();
23
- return useMemo(() => ({ get current() { return getRigidBody(); } }), [getRigidBody]);
21
+ export function useCurrentNodeHandle(kind) {
22
+ const { getCurrentNodeHandle } = useCurrentNode();
23
+ return useMemo(() => ({ get current() { return getCurrentNodeHandle(kind); } }), [getCurrentNodeHandle, kind]);
24
24
  }
25
- export function EntityRuntimeScope({ nodeId, editMode, isSelected, children, }) {
25
+ export function CurrentNodeScope({ nodeId, editMode, isSelected, children, }) {
26
26
  const asset = useContext(AssetRuntimeContext);
27
27
  if (!asset)
28
- throw new Error("EntityRuntimeScope must be used inside <PrefabRoot>");
28
+ throw new Error("CurrentNodeScope must be used inside <PrefabRoot>");
29
29
  const value = useMemo(() => ({
30
30
  nodeId,
31
31
  editMode,
32
32
  isSelected,
33
- getObject: () => asset.getObject(nodeId),
34
- getRigidBody: () => asset.getRigidBody(nodeId),
33
+ getCurrentNodeObject: () => asset.getNodeObject(nodeId),
34
+ getCurrentNodeHandle: (kind) => asset.getNodeHandle(nodeId, kind),
35
35
  }), [asset, editMode, isSelected, nodeId]);
36
- return _jsx(EntityRuntimeContext.Provider, { value: value, children: children });
36
+ return _jsx(CurrentNodeRuntimeContext.Provider, { value: value, children: children });
37
37
  }
@@ -52,7 +52,7 @@ function BufferArrayField({ label, value, fallback, onChange, rows = 4, }) {
52
52
  function BufferGeometryComponentEditor({ component, onUpdate, }) {
53
53
  var _a;
54
54
  const properties = (_a = component.properties) !== null && _a !== void 0 ? _a : {};
55
- return (_jsxs(FieldGroup, { children: [_jsx(BufferArrayField, { label: "Positions", value: properties.positions, fallback: DEFAULT_TRIANGLE_POSITIONS, rows: 5, onChange: (positions) => onUpdate({ positions }) }), _jsx(BufferArrayField, { label: "Indices", value: properties.indices, fallback: DEFAULT_TRIANGLE_INDICES, onChange: (indices) => onUpdate({ indices }) }), _jsx(BufferArrayField, { label: "Normals", value: properties.normals, fallback: [], onChange: (normals) => onUpdate({ normals }) }), _jsx(BufferArrayField, { label: "UVs", value: properties.uvs, fallback: DEFAULT_TRIANGLE_UVS, onChange: (uvs) => onUpdate({ uvs }) }), _jsx(BooleanField, { name: "computeVertexNormals", label: "Compute Normals", values: properties, onChange: onUpdate, fallback: true }), _jsx(BooleanField, { name: "visible", label: "Visible", values: properties, onChange: onUpdate, fallback: true }), _jsx(BooleanField, { name: "castShadow", label: "Cast Shadow", values: properties, onChange: onUpdate, fallback: true }), _jsx(BooleanField, { name: "receiveShadow", label: "Receive Shadow", values: properties, onChange: onUpdate, fallback: true }), _jsx(BooleanField, { name: "emitClickEvent", label: "Emit Click Event", values: properties, onChange: onUpdate, fallback: false }), properties.emitClickEvent ? (_jsx(StringField, { name: "clickEventName", label: "Click Event Name", values: properties, onChange: onUpdate, placeholder: "entity:click" })) : null] }));
55
+ return (_jsxs(FieldGroup, { children: [_jsx(BufferArrayField, { label: "Positions", value: properties.positions, fallback: DEFAULT_TRIANGLE_POSITIONS, rows: 5, onChange: (positions) => onUpdate({ positions }) }), _jsx(BufferArrayField, { label: "Indices", value: properties.indices, fallback: DEFAULT_TRIANGLE_INDICES, onChange: (indices) => onUpdate({ indices }) }), _jsx(BufferArrayField, { label: "Normals", value: properties.normals, fallback: [], onChange: (normals) => onUpdate({ normals }) }), _jsx(BufferArrayField, { label: "UVs", value: properties.uvs, fallback: DEFAULT_TRIANGLE_UVS, onChange: (uvs) => onUpdate({ uvs }) }), _jsx(BooleanField, { name: "computeVertexNormals", label: "Compute Normals", values: properties, onChange: onUpdate, fallback: true }), _jsx(BooleanField, { name: "visible", label: "Visible", values: properties, onChange: onUpdate, fallback: true }), _jsx(BooleanField, { name: "castShadow", label: "Cast Shadow", values: properties, onChange: onUpdate, fallback: true }), _jsx(BooleanField, { name: "receiveShadow", label: "Receive Shadow", values: properties, onChange: onUpdate, fallback: true }), _jsx(BooleanField, { name: "emitClickEvent", label: "Emit Click Event", values: properties, onChange: onUpdate, fallback: false }), properties.emitClickEvent ? (_jsx(StringField, { name: "clickEventName", label: "Click Event Name", values: properties, onChange: onUpdate, placeholder: "node:click" })) : null] }));
56
56
  }
57
57
  function BufferGeometryComponentView({ properties }) {
58
58
  const positions = normalizeNumberArray(properties.positions, DEFAULT_TRIANGLE_POSITIONS);
@@ -3,7 +3,7 @@ import { OrthographicCamera, PerspectiveCamera, useHelper } from '@react-three/d
3
3
  import { useRef } from 'react';
4
4
  import { CameraHelper } from 'three';
5
5
  import { useFrame, useThree } from '@react-three/fiber';
6
- import { useEntityRuntime } from '../assetRuntime';
6
+ import { useCurrentNode } from '../assetRuntime';
7
7
  import { FieldGroup, NumberField, SelectField } from './Input';
8
8
  const CAMERA_PROJECTION_OPTIONS = [
9
9
  { value: 'perspective', label: 'Perspective' },
@@ -25,7 +25,7 @@ function CameraComponentEditor({ component, onUpdate }) {
25
25
  }
26
26
  function CameraComponentView({ properties, children }) {
27
27
  var _a;
28
- const { editMode, isSelected } = useEntityRuntime();
28
+ const { editMode, isSelected } = useCurrentNode();
29
29
  const { size } = useThree();
30
30
  const merged = Object.assign(Object.assign({}, cameraDefaults), properties);
31
31
  const projection = (_a = merged.projection) !== null && _a !== void 0 ? _a : cameraDefaults.projection;
@@ -10,11 +10,11 @@ export interface ComponentViewProps<P = Record<string, any>> {
10
10
  properties: P;
11
11
  /** Children to render for components that wrap the current subtree. */
12
12
  children?: React.ReactNode;
13
- /** Entity local position (passed to wrapper components like Physics). */
13
+ /** Current node local position for wrapper components. */
14
14
  position?: [number, number, number];
15
- /** Entity local rotation in radians (passed to wrapper components like Physics). */
15
+ /** Current node local rotation in radians for wrapper components. */
16
16
  rotation?: [number, number, number];
17
- /** Entity local scale (passed to wrapper components like Physics). */
17
+ /** Current node local scale for wrapper components. */
18
18
  scale?: [number, number, number];
19
19
  }
20
20
  export interface Component {