react-three-game 0.0.91 → 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 (34) 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/assetviewer/page.js +38 -10
  7. package/dist/tools/prefabeditor/EditorTree.js +2 -2
  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 +18 -6
  13. package/dist/tools/prefabeditor/PrefabEditor.js +67 -30
  14. package/dist/tools/prefabeditor/PrefabRoot.d.ts +15 -9
  15. package/dist/tools/prefabeditor/PrefabRoot.js +142 -129
  16. package/dist/tools/prefabeditor/assetRuntime.d.ts +13 -11
  17. package/dist/tools/prefabeditor/assetRuntime.js +15 -15
  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 +3 -3
  22. package/dist/tools/prefabeditor/components/ModelComponent.js +1 -1
  23. package/dist/tools/prefabeditor/components/PointLightComponent.js +2 -2
  24. package/dist/tools/prefabeditor/components/SoundComponent.js +2 -2
  25. package/dist/tools/prefabeditor/components/SpotLightComponent.js +2 -2
  26. package/dist/tools/prefabeditor/components/index.js +0 -2
  27. package/dist/tools/prefabeditor/types.d.ts +1 -0
  28. package/dist/tools/prefabeditor/usePointerEvents.d.ts +3 -3
  29. package/dist/tools/prefabeditor/usePointerEvents.js +5 -5
  30. package/package.json +1 -3
  31. package/dist/tools/prefabeditor/components/PhysicsComponent.d.ts +0 -26
  32. package/dist/tools/prefabeditor/components/PhysicsComponent.js +0 -287
  33. package/dist/tools/prefabeditor/scene.d.ts +0 -70
  34. 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,23 +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;
21
- onObjectRefChange?: (id: string, object: Object3D | null) => void;
20
+ onClick?: (event: ThreeEvent<PointerEvent>, node: GameObjectType) => void;
21
+ onEditNodeClick?: (event: ThreeEvent<PointerEvent>, node: GameObjectType) => void;
22
22
  basePath?: string;
23
23
  }
24
- 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>>;
25
29
  export declare function GameObjectRenderer(props: RendererProps): import("react/jsx-runtime").JSX.Element | null;
26
30
  interface RendererProps {
27
31
  nodeId: string;
28
32
  selectedId?: string | null;
29
33
  onSelect?: (id: string) => void;
30
- 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;
31
36
  registerRef: (id: string, obj: Object3D | null) => void;
32
37
  loadedModels: LoadedModels;
33
38
  editMode?: boolean;
34
39
  parentMatrix?: Matrix4;
40
+ isVisible?: boolean;
35
41
  }
36
42
  export default PrefabRoot;
@@ -10,9 +10,8 @@ var __rest = (this && this.__rest) || function (s, e) {
10
10
  return t;
11
11
  };
12
12
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
13
- import { useHelper } from "@react-three/drei";
14
13
  import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
15
- import { BoxHelper, Euler, Matrix4, } from "three";
14
+ import { Euler, Matrix4, } from "three";
16
15
  import { useStore } from "zustand";
17
16
  import { useClickValid } from "./useClickValid";
18
17
  import { findComponent, getNodeUserData } from "./types";
@@ -21,9 +20,8 @@ import { builtinComponents } from "./components";
21
20
  import { loadModel, loadSound, loadTexture } from "../dragdrop";
22
21
  import { GameInstance, GameInstanceProvider, getRepeatAxesFromModelProperties, useInstanceCheck } from "./InstanceProvider";
23
22
  import { composeTransform, decompose } from "./utils";
24
- import { isPhysicsProps } from "./components/PhysicsComponent";
25
23
  import { createPrefabStore, PrefabStoreProvider, useOptionalPrefabStoreApi, usePrefabChildIds, usePrefabNode, usePrefabRootId } from "./prefabStore";
26
- import { AssetRuntimeContext, EntityRuntimeScope } from "./assetRuntime";
24
+ import { AssetRuntimeContext, CurrentNodeScope } from "./assetRuntime";
27
25
  import { gameEvents } from "./GameEvents";
28
26
  import { sound as soundManager } from "../../helpers/SoundManager";
29
27
  builtinComponents.forEach(registerComponent);
@@ -37,30 +35,13 @@ function resolveAssetPath(basePath, file) {
37
35
  return file;
38
36
  return file.startsWith("/") ? `${basePath}${file}` : `${basePath}/${file}`;
39
37
  }
40
- /** Check if all model assets required by a node are loaded. */
41
- function isNodeReady(node, loadedModels) {
38
+ /** Check if a model component's assets are loaded. */
39
+ function isNodeReady(model, loadedModels) {
42
40
  var _a;
43
- const model = findComponent(node, "Model");
44
41
  if (!((_a = model === null || model === void 0 ? void 0 : model.properties) === null || _a === void 0 ? void 0 : _a.filename))
45
42
  return true;
46
43
  return Boolean(loadedModels[model.properties.filename]);
47
44
  }
48
- function getNodeClickEventName(node) {
49
- var _a;
50
- const clickComponents = [
51
- findComponent(node, 'BufferGeometry'),
52
- findComponent(node, 'Geometry'),
53
- ];
54
- for (const component of clickComponents) {
55
- if (!((_a = component === null || component === void 0 ? void 0 : component.properties) === null || _a === void 0 ? void 0 : _a.emitClickEvent))
56
- continue;
57
- const eventName = component.properties.clickEventName;
58
- if (typeof eventName === 'string' && eventName.trim()) {
59
- return eventName.trim();
60
- }
61
- }
62
- return null;
63
- }
64
45
  function getNodeMetadataProps(node) {
65
46
  var _a, _b;
66
47
  const nodeName = (_b = (_a = node.name) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : '';
@@ -69,7 +50,7 @@ function getNodeMetadataProps(node) {
69
50
  userData: Object.assign(Object.assign({ prefabNodeId: node.id }, (nodeName ? { prefabNodeName: nodeName } : {})), getNodeUserData(node)),
70
51
  };
71
52
  }
72
- export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSelect, onClick, onObjectRefChange, basePath = "" }, ref) => {
53
+ export const PrefabRootInternal = forwardRef(({ editMode, data, store, selectedId, onSelect, onClick, onEditNodeClick, basePath = "" }, ref) => {
73
54
  var _a;
74
55
  const [models, setModels] = useState({});
75
56
  const [textures, setTextures] = useState({});
@@ -82,8 +63,7 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
82
63
  const failedTextures = useRef(new Set());
83
64
  const failedSounds = useRef(new Set());
84
65
  const objectRefs = useRef({});
85
- const rigidBodyRefs = useRef(new Map());
86
- const rootRef = useRef(null);
66
+ const nodeHandles = useRef(new Map());
87
67
  const parentStore = useOptionalPrefabStoreApi();
88
68
  const [ownedStore] = useState(() => {
89
69
  if (data)
@@ -95,35 +75,52 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
95
75
  const resolvedStore = (_a = store !== null && store !== void 0 ? store : parentStore) !== null && _a !== void 0 ? _a : ownedStore;
96
76
  const usesOwnedStore = resolvedStore === ownedStore;
97
77
  const shouldProvideStoreContext = !parentStore || parentStore !== resolvedStore;
78
+ const rootId = useStore(resolvedStore, state => state.rootId);
98
79
  const assetManifestKey = useStore(resolvedStore, state => state.assetManifestKey);
99
80
  const availableModels = useMemo(() => (Object.assign(Object.assign({}, models), injectedModels)), [models, injectedModels]);
100
81
  const availableTextures = useMemo(() => (Object.assign(Object.assign({}, textures), injectedTextures)), [textures, injectedTextures]);
101
82
  const availableSounds = useMemo(() => (Object.assign(Object.assign({}, sounds), injectedSounds)), [sounds, injectedSounds]);
102
- const getObject = useCallback((id) => {
83
+ const getNodeObject = useCallback((id) => {
103
84
  var _a;
104
85
  return (_a = objectRefs.current[id]) !== null && _a !== void 0 ? _a : null;
105
86
  }, []);
106
- const getRigidBody = useCallback((id) => {
107
- var _a;
108
- 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]]));
109
107
  }, []);
110
108
  useImperativeHandle(ref, () => ({
111
- root: rootRef.current,
112
- getObject,
113
- 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,
114
115
  addModel: (path, model) => setInjectedModels(prev => (Object.assign(Object.assign({}, prev), { [path]: model }))),
115
116
  addTexture: (path, texture) => setInjectedTextures(prev => (Object.assign(Object.assign({}, prev), { [path]: texture }))),
116
117
  addSound: (path, sound) => {
117
118
  soundManager.setBuffer(path, sound);
118
119
  setInjectedSounds(prev => (Object.assign(Object.assign({}, prev), { [path]: sound })));
119
120
  },
120
- }), [getObject]);
121
+ }), [getNodeHandle, getNodeObject, rootId]);
121
122
  const registerRef = useCallback((id, obj) => {
122
123
  objectRefs.current[id] = obj;
123
- onObjectRefChange === null || onObjectRefChange === void 0 ? void 0 : onObjectRefChange(id, obj);
124
- }, [onObjectRefChange]);
125
- const registerRigidBodyRef = useCallback((id, rb) => {
126
- rigidBodyRefs.current.set(id, rb);
127
124
  }, []);
128
125
  useEffect(() => {
129
126
  if (usesOwnedStore && data) {
@@ -188,25 +185,87 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
188
185
  syncAssets();
189
186
  }, [resolvedStore, assetManifestKey, basePath, injectedModels, injectedSounds, injectedTextures, models, sounds, textures]);
190
187
  const assetRuntime = useMemo(() => ({
191
- getObject,
192
- getRigidBody,
193
- registerRigidBodyRef,
188
+ registerNodeHandle,
189
+ getNodeHandle,
190
+ getNodeObject,
194
191
  getModel: (path) => { var _a; return (_a = availableModels[path]) !== null && _a !== void 0 ? _a : null; },
195
192
  getTexture: (path) => { var _a; return (_a = availableTextures[path]) !== null && _a !== void 0 ? _a : null; },
196
193
  getSound: (path) => { var _a; return (_a = availableSounds[path]) !== null && _a !== void 0 ? _a : null; },
197
194
  getAssetRevision: () => `${Object.keys(availableTextures).sort().join('|')}::${Object.keys(availableModels).sort().join('|')}`,
198
- }), [getObject, getRigidBody, registerRigidBodyRef, availableModels, availableTextures, availableSounds]);
199
- 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 }) }));
200
205
  const runtimeContent = _jsx(AssetRuntimeContext.Provider, { value: assetRuntime, children: content });
201
206
  if (!shouldProvideStoreContext) {
202
207
  return runtimeContent;
203
208
  }
204
209
  return _jsx(PrefabStoreProvider, { store: resolvedStore, children: runtimeContent });
205
210
  });
211
+ export const PrefabRoot = PrefabRootInternal;
206
212
  function StoreRootNode(props) {
207
213
  const rootId = usePrefabRootId();
208
214
  return _jsx(GameObjectRenderer, Object.assign({}, props, { nodeId: rootId }));
209
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
+ }
210
269
  function emitNodePointerEvent(eventName, event, nodeId, node, fallbackObject) {
211
270
  var _a;
212
271
  const trimmedEventName = eventName === null || eventName === void 0 ? void 0 : eventName.trim();
@@ -249,21 +308,19 @@ export function GameObjectRenderer(props) {
249
308
  ? _jsx(InstancedNode, Object.assign({}, props), key)
250
309
  : _jsx(StandardNode, Object.assign({}, props), key);
251
310
  }
252
- function InstancedNode({ nodeId, parentMatrix = IDENTITY, editMode, registerRef, onSelect, onClick }) {
311
+ function InstancedNode({ nodeId, parentMatrix = IDENTITY, editMode, registerRef, onSelect, onEditNodeClick, onClick, isVisible = true }) {
253
312
  var _a, _b;
254
313
  const gameObject = usePrefabNode(nodeId);
255
314
  if (!gameObject)
256
315
  return null;
316
+ const analyzedComponents = useMemo(() => analyzeNodeComponents(gameObject), [gameObject]);
257
317
  const localTransform = getNodeTransformProps(gameObject);
258
318
  const isLocked = Boolean(gameObject.locked);
319
+ const nodeVisible = isVisible && !gameObject.hidden;
259
320
  const metadataProps = getNodeMetadataProps(gameObject);
260
- const groupProps = Object.assign(Object.assign({}, metadataProps), { position: localTransform.position, rotation: localTransform.rotation, scale: localTransform.scale });
261
- const physicsData = findComponent(gameObject, "Physics");
262
- const physicsProps = isPhysicsProps(physicsData === null || physicsData === void 0 ? void 0 : physicsData.properties)
263
- ? physicsData === null || physicsData === void 0 ? void 0 : physicsData.properties
264
- : undefined;
265
- const modelUrl = (_b = (_a = findComponent(gameObject, "Model")) === null || _a === void 0 ? void 0 : _a.properties) === null || _b === void 0 ? void 0 : _b.filename;
266
- 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]);
267
324
  const groupRef = useRef(null);
268
325
  const handleGroupRef = useCallback((object) => {
269
326
  groupRef.current = object;
@@ -273,56 +330,44 @@ function InstancedNode({ nodeId, parentMatrix = IDENTITY, editMode, registerRef,
273
330
  }, [editMode, nodeId, registerRef]);
274
331
  const editClickHandlers = useClickValid(!!editMode && !isLocked, (event) => {
275
332
  onSelect === null || onSelect === void 0 ? void 0 : onSelect(nodeId);
276
- onClick === null || onClick === void 0 ? void 0 : onClick(event, gameObject);
333
+ onEditNodeClick === null || onEditNodeClick === void 0 ? void 0 : onEditNodeClick(event, gameObject);
277
334
  });
278
- 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)));
279
336
  if (editMode) {
280
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] }));
281
338
  }
282
339
  return _jsx(_Fragment, { children: renderedInstances });
283
340
  }
284
- function StandardNode({ nodeId, selectedId, onSelect, onClick, registerRef, loadedModels, editMode, parentMatrix = IDENTITY, }) {
285
- var _a, _b;
341
+ function StandardNode({ nodeId, selectedId, onSelect, onClick, onEditNodeClick, registerRef, loadedModels, editMode, parentMatrix = IDENTITY, isVisible = true, }) {
286
342
  const gameObject = usePrefabNode(nodeId);
287
343
  const childIds = usePrefabChildIds(nodeId);
288
344
  if (!gameObject)
289
345
  return null;
346
+ const analyzedComponents = useMemo(() => analyzeNodeComponents(gameObject), [gameObject]);
290
347
  const isSelected = selectedId === nodeId;
291
348
  const isLocked = Boolean(gameObject.locked);
349
+ const nodeVisible = isVisible && !gameObject.hidden;
292
350
  const stillInstanced = useInstanceCheck(nodeId);
293
- const clickEventName = getNodeClickEventName(gameObject);
294
351
  const metadataProps = getNodeMetadataProps(gameObject);
295
352
  const groupRef = useRef(null);
296
- const helperRef = useRef(null);
297
353
  const handleGroupRef = useCallback((object) => {
298
354
  groupRef.current = object;
299
355
  registerRef(nodeId, object);
300
356
  }, [nodeId, registerRef]);
301
- const handleHelperRef = useCallback((object) => {
302
- helperRef.current = object;
303
- }, []);
304
- const handleEditGroupRef = useCallback((object) => {
305
- handleGroupRef(object);
306
- handleHelperRef(object);
307
- }, [handleGroupRef, handleHelperRef]);
308
357
  const editClickHandlers = useClickValid(!!editMode && !isLocked, (event) => {
309
358
  onSelect === null || onSelect === void 0 ? void 0 : onSelect(nodeId);
310
- onClick === null || onClick === void 0 ? void 0 : onClick(event, gameObject);
359
+ onEditNodeClick === null || onEditNodeClick === void 0 ? void 0 : onEditNodeClick(event, gameObject);
311
360
  });
312
- const primaryClickHandlers = !editMode && (clickEventName || onClick)
361
+ const primaryClickHandlers = !editMode && onClick
313
362
  ? {
314
363
  onClick: (event) => {
315
364
  event.stopPropagation();
316
- emitNodePointerEvent(clickEventName, event, nodeId, gameObject, groupRef.current);
317
- onClick === null || onClick === void 0 ? void 0 : onClick(event, gameObject);
365
+ onClick(event, nodeId, groupRef.current);
318
366
  },
319
367
  }
320
368
  : undefined;
321
- useHelper(editMode && isSelected ? helperRef : null, BoxHelper, "cyan");
322
369
  const world = parentMatrix.clone().multiply(compose(gameObject));
323
- const physics = findComponent(gameObject, "Physics");
324
- const ready = isNodeReady(gameObject, loadedModels);
325
- const hasPhysics = physics && ready && !stillInstanced;
370
+ const ready = isNodeReady(analyzedComponents.model, loadedModels);
326
371
  const transform = getNodeTransformProps(gameObject);
327
372
  const transformProps = {
328
373
  position: transform.position,
@@ -330,40 +375,11 @@ function StandardNode({ nodeId, selectedId, onSelect, onClick, registerRef, load
330
375
  scale: transform.scale,
331
376
  };
332
377
  const groupProps = Object.assign(Object.assign({}, metadataProps), transformProps);
333
- const physicsDef = hasPhysics ? getComponentDef(physics.type) : null;
334
- const isInstanced = (_b = (_a = findComponent(gameObject, "Model")) === null || _a === void 0 ? void 0 : _a.properties) === null || _b === void 0 ? void 0 : _b.instanced;
335
- const physicsKey = `physics_${nodeId}_${isInstanced ? 'instanced' : 'standard'}`;
336
- const renderCtx = { loadedModels, editMode, registerRef };
337
- const childNodes = _jsx(ChildNodes, { childIds: childIds, parentMatrix: world, selectedId: selectedId, onSelect: onSelect, onClick: onClick, registerRef: registerRef, loadedModels: loadedModels, editMode: editMode });
338
- 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);
339
380
  const editAnchor = editMode ? (_jsx("mesh", { visible: false, children: _jsx("boxGeometry", { args: [0.01, 0.01, 0.01] }) })) : null;
340
- const standardNode = (_jsxs("group", Object.assign({ ref: editMode ? handleEditGroupRef : handleGroupRef }, groupProps, (editMode ? editClickHandlers : undefined), { children: [editAnchor, inner] })));
341
- 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: editMode ? handleEditGroupRef : handleGroupRef }, metadataProps, (editMode ? editClickHandlers : undefined), { children: [editAnchor, inner] })) }), physicsKey)) : null;
342
- return (_jsx(EntityRuntimeScope, { nodeId: nodeId, editMode: editMode, isSelected: isSelected, children: physicsNode !== null && physicsNode !== void 0 ? physicsNode : standardNode }));
343
- }
344
- function isRendererHandledComponent(componentType) {
345
- return componentType === "Transform"
346
- || componentType === "BufferGeometry"
347
- || componentType === "Geometry"
348
- || componentType === "Material"
349
- || componentType === "Physics"
350
- || componentType === "Model";
351
- }
352
- function getCompositionComponents(gameObject) {
353
- var _a;
354
- return Object.entries((_a = gameObject.components) !== null && _a !== void 0 ? _a : {}).reduce((result, [key, comp]) => {
355
- if (!(comp === null || comp === void 0 ? void 0 : comp.type) || isRendererHandledComponent(comp.type))
356
- return result;
357
- const def = getComponentDef(comp.type);
358
- if (!(def === null || def === void 0 ? void 0 : def.View))
359
- return result;
360
- result.push({
361
- key,
362
- View: def.View,
363
- properties: comp.properties,
364
- });
365
- return result;
366
- }, []);
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 }));
367
383
  }
368
384
  function ChildNodes(_a) {
369
385
  var { childIds, parentMatrix } = _a, props = __rest(_a, ["childIds", "parentMatrix"]);
@@ -381,7 +397,7 @@ function getModelRepeatSettings(node) {
381
397
  repeatAxes: getRepeatAxesFromModelProperties(properties),
382
398
  };
383
399
  }
384
- function buildRepeatedInstances(gameObject, parentMatrix, modelUrl, physics) {
400
+ function buildRepeatedInstances(gameObject, parentMatrix, modelUrl) {
385
401
  if (!modelUrl)
386
402
  return [];
387
403
  const transform = getNodeTransformProps(gameObject);
@@ -418,7 +434,6 @@ function buildRepeatedInstances(gameObject, parentMatrix, modelUrl, physics) {
418
434
  position,
419
435
  rotation,
420
436
  scale,
421
- physics,
422
437
  });
423
438
  }
424
439
  }
@@ -434,32 +449,30 @@ function getNodeTransformProps(node) {
434
449
  scale: (_d = t === null || t === void 0 ? void 0 : t.scale) !== null && _d !== void 0 ? _d : [1, 1, 1],
435
450
  };
436
451
  }
437
- function renderCompositionNode(gameObject, ctx, primaryClickHandlers, childNodes) {
438
- const primaryContent = renderNodePrimaryContent(gameObject, ctx, primaryClickHandlers);
439
- return applyNodeComposition(gameObject, _jsxs(_Fragment, { children: [primaryContent, childNodes] }));
440
- }
441
- function renderNodePrimaryContent(gameObject, ctx, primaryClickHandlers) {
442
- var _a, _b, _c;
443
- const geometry = (_a = findComponent(gameObject, "BufferGeometry")) !== null && _a !== void 0 ? _a : findComponent(gameObject, "Geometry");
444
- const material = findComponent(gameObject, "Material");
445
- const model = findComponent(gameObject, "Model");
446
- const geometryDef = geometry && getComponentDef(geometry.type);
447
- const materialDef = material && getComponentDef(material.type);
448
- const modelDef = model && getComponentDef(model.type);
449
- 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 : {};
450
459
  const meshVisible = geometryProperties.visible !== false;
451
460
  const meshCastShadow = meshVisible && geometryProperties.castShadow !== false;
452
461
  const meshReceiveShadow = meshVisible && geometryProperties.receiveShadow !== false;
453
- if ((geometry === null || geometry === void 0 ? void 0 : geometry.type) && (geometryDef === null || geometryDef === void 0 ? void 0 : geometryDef.View)) {
454
- 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"))] })));
455
465
  }
456
- 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)) {
457
- 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 }));
458
471
  }
459
- return null;
460
- }
461
- function applyNodeComposition(gameObject, subtree) {
462
- const components = getCompositionComponents(gameObject);
463
- 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;
464
477
  }
465
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;