react-three-game 0.0.55 → 0.0.57

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 (68) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/index.js +1 -1
  3. package/dist/shared/ContactShadow.d.ts +8 -0
  4. package/dist/shared/ContactShadow.js +32 -0
  5. package/dist/shared/GameCanvas.js +1 -3
  6. package/dist/tools/assetviewer/page.js +36 -15
  7. package/dist/tools/dragdrop/DragDropLoader.js +17 -40
  8. package/dist/tools/dragdrop/modelLoader.d.ts +5 -0
  9. package/dist/tools/dragdrop/modelLoader.js +39 -0
  10. package/dist/tools/prefabeditor/Dropdown.d.ts +15 -0
  11. package/dist/tools/prefabeditor/Dropdown.js +82 -0
  12. package/dist/tools/prefabeditor/EditorContext.d.ts +5 -0
  13. package/dist/tools/prefabeditor/EditorTree.js +139 -70
  14. package/dist/tools/prefabeditor/EditorUI.js +5 -10
  15. package/dist/tools/prefabeditor/PrefabEditor.d.ts +1 -0
  16. package/dist/tools/prefabeditor/PrefabEditor.js +70 -3
  17. package/dist/tools/prefabeditor/PrefabRoot.d.ts +3 -0
  18. package/dist/tools/prefabeditor/PrefabRoot.js +136 -35
  19. package/dist/tools/prefabeditor/components/AmbientLightComponent.js +3 -7
  20. package/dist/tools/prefabeditor/components/CameraComponent.d.ts +3 -0
  21. package/dist/tools/prefabeditor/components/CameraComponent.js +25 -0
  22. package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +2 -2
  23. package/dist/tools/prefabeditor/components/EnvironmentComponent.d.ts +3 -0
  24. package/dist/tools/prefabeditor/components/EnvironmentComponent.js +15 -0
  25. package/dist/tools/prefabeditor/components/GeometryComponent.js +46 -46
  26. package/dist/tools/prefabeditor/components/Input.d.ts +51 -1
  27. package/dist/tools/prefabeditor/components/Input.js +100 -47
  28. package/dist/tools/prefabeditor/components/MaterialComponent.d.ts +8 -2
  29. package/dist/tools/prefabeditor/components/MaterialComponent.js +129 -14
  30. package/dist/tools/prefabeditor/components/ModelComponent.js +44 -3
  31. package/dist/tools/prefabeditor/components/PhysicsComponent.js +16 -81
  32. package/dist/tools/prefabeditor/components/SpotLightComponent.js +6 -11
  33. package/dist/tools/prefabeditor/components/TextComponent.js +7 -53
  34. package/dist/tools/prefabeditor/components/TransformComponent.js +31 -19
  35. package/dist/tools/prefabeditor/components/index.js +5 -1
  36. package/dist/tools/prefabeditor/styles.d.ts +17 -4
  37. package/dist/tools/prefabeditor/styles.js +69 -32
  38. package/dist/tools/prefabeditor/utils.d.ts +8 -3
  39. package/dist/tools/prefabeditor/utils.js +92 -6
  40. package/package.json +1 -1
  41. package/react-three-game-skill/react-three-game/rules/LIGHTING.md +6 -0
  42. package/src/index.ts +7 -0
  43. package/src/shared/ContactShadow.tsx +74 -0
  44. package/src/shared/GameCanvas.tsx +0 -3
  45. package/src/tools/assetviewer/page.tsx +78 -46
  46. package/src/tools/dragdrop/DragDropLoader.tsx +7 -39
  47. package/src/tools/dragdrop/modelLoader.ts +36 -0
  48. package/src/tools/prefabeditor/Dropdown.tsx +112 -0
  49. package/src/tools/prefabeditor/EditorContext.tsx +5 -0
  50. package/src/tools/prefabeditor/EditorTree.tsx +237 -115
  51. package/src/tools/prefabeditor/EditorUI.tsx +6 -11
  52. package/src/tools/prefabeditor/PrefabEditor.tsx +77 -5
  53. package/src/tools/prefabeditor/PrefabRoot.tsx +228 -59
  54. package/src/tools/prefabeditor/components/AmbientLightComponent.tsx +5 -11
  55. package/src/tools/prefabeditor/components/CameraComponent.tsx +80 -0
  56. package/src/tools/prefabeditor/components/DirectionalLightComponent.tsx +2 -2
  57. package/src/tools/prefabeditor/components/EnvironmentComponent.tsx +47 -0
  58. package/src/tools/prefabeditor/components/GeometryComponent.tsx +69 -63
  59. package/src/tools/prefabeditor/components/Input.tsx +247 -53
  60. package/src/tools/prefabeditor/components/MaterialComponent.tsx +191 -20
  61. package/src/tools/prefabeditor/components/ModelComponent.tsx +52 -5
  62. package/src/tools/prefabeditor/components/PhysicsComponent.tsx +44 -85
  63. package/src/tools/prefabeditor/components/SpotLightComponent.tsx +14 -16
  64. package/src/tools/prefabeditor/components/TextComponent.tsx +58 -57
  65. package/src/tools/prefabeditor/components/TransformComponent.tsx +78 -20
  66. package/src/tools/prefabeditor/components/index.ts +5 -1
  67. package/src/tools/prefabeditor/styles.ts +71 -32
  68. package/src/tools/prefabeditor/utils.ts +96 -5
@@ -15,28 +15,55 @@ import { getComponent, registerComponent, getNonComposableKeys } from "./compone
15
15
  import components from "./components";
16
16
  import { loadModel } from "../dragdrop/modelLoader";
17
17
  import { GameInstance, GameInstanceProvider, useInstanceCheck } from "./InstanceProvider";
18
- import { updateNode } from "./utils";
18
+ import { focusCameraOnObject, updateNode } from "./utils";
19
19
  import { EditorContext } from "./EditorContext";
20
20
  components.forEach(registerComponent);
21
21
  const IDENTITY = new Matrix4();
22
22
  export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selectedId, onSelect, onClick, basePath = "" }, ref) => {
23
- var _a, _b;
23
+ var _a, _b, _c, _d;
24
24
  // optional editor context
25
25
  const editorContext = useContext(EditorContext);
26
26
  const transformMode = (_a = editorContext === null || editorContext === void 0 ? void 0 : editorContext.transformMode) !== null && _a !== void 0 ? _a : "translate";
27
27
  const snapResolution = (_b = editorContext === null || editorContext === void 0 ? void 0 : editorContext.snapResolution) !== null && _b !== void 0 ? _b : 0;
28
+ const positionSnap = (_c = editorContext === null || editorContext === void 0 ? void 0 : editorContext.positionSnap) !== null && _c !== void 0 ? _c : 0.5;
29
+ const rotationSnap = (_d = editorContext === null || editorContext === void 0 ? void 0 : editorContext.rotationSnap) !== null && _d !== void 0 ? _d : Math.PI / 4;
28
30
  // prefab root state
29
31
  const [models, setModels] = useState({});
30
32
  const [textures, setTextures] = useState({});
31
33
  const loading = useRef(new Set());
34
+ const failedTextures = useRef(new Set());
32
35
  const objectRefs = useRef({});
33
36
  const rigidBodyRefs = useRef(new Map());
34
37
  const [selectedObject, setSelectedObject] = useState(null);
35
38
  const rootRef = useRef(null);
39
+ const controlsRef = useRef(null);
40
+ const injectModel = useCallback((filename, model) => {
41
+ setModels(m => (Object.assign(Object.assign({}, m), { [filename]: model })));
42
+ }, []);
43
+ const injectTexture = useCallback((filename, file) => {
44
+ loading.current.add(filename);
45
+ const url = URL.createObjectURL(file);
46
+ const loader = new TextureLoader();
47
+ loader.load(url, tex => {
48
+ tex.colorSpace = SRGBColorSpace;
49
+ setTextures(t => (Object.assign(Object.assign({}, t), { [filename]: tex })));
50
+ URL.revokeObjectURL(url);
51
+ }, undefined, () => URL.revokeObjectURL(url));
52
+ }, []);
36
53
  useImperativeHandle(ref, () => ({
37
54
  root: rootRef.current,
38
- rigidBodyRefs: rigidBodyRefs.current
39
- }), []);
55
+ rigidBodyRefs: rigidBodyRefs.current,
56
+ injectModel,
57
+ injectTexture,
58
+ focusNode: (nodeId) => {
59
+ const object = objectRefs.current[nodeId];
60
+ const controls = controlsRef.current;
61
+ const camera = controls === null || controls === void 0 ? void 0 : controls.object;
62
+ if (!object || !controls || !camera)
63
+ return;
64
+ focusCameraOnObject(object, camera, controls.target, () => { var _a; return (_a = controls.update) === null || _a === void 0 ? void 0 : _a.call(controls); });
65
+ }
66
+ }), [injectModel, injectTexture]);
40
67
  const registerRef = useCallback((id, obj) => {
41
68
  objectRefs.current[id] = obj;
42
69
  if (id === selectedId)
@@ -77,11 +104,13 @@ export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selected
77
104
  const modelsToLoad = new Set();
78
105
  const texturesToLoad = new Set();
79
106
  walk(data.root, node => {
80
- var _a, _b, _c, _d, _e, _f;
107
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
81
108
  ((_c = (_b = (_a = node.components) === null || _a === void 0 ? void 0 : _a.model) === null || _b === void 0 ? void 0 : _b.properties) === null || _c === void 0 ? void 0 : _c.filename) &&
82
109
  modelsToLoad.add(node.components.model.properties.filename);
83
110
  ((_f = (_e = (_d = node.components) === null || _d === void 0 ? void 0 : _d.material) === null || _e === void 0 ? void 0 : _e.properties) === null || _f === void 0 ? void 0 : _f.texture) &&
84
111
  texturesToLoad.add(node.components.material.properties.texture);
112
+ ((_j = (_h = (_g = node.components) === null || _g === void 0 ? void 0 : _g.material) === null || _h === void 0 ? void 0 : _h.properties) === null || _j === void 0 ? void 0 : _j.normalMapTexture) &&
113
+ texturesToLoad.add(node.components.material.properties.normalMapTexture);
85
114
  });
86
115
  modelsToLoad.forEach((file) => __awaiter(void 0, void 0, void 0, function* () {
87
116
  if (models[file] || loading.current.has(file))
@@ -96,7 +125,7 @@ export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selected
96
125
  }));
97
126
  const loader = new TextureLoader();
98
127
  texturesToLoad.forEach(file => {
99
- if (textures[file] || loading.current.has(file))
128
+ if (textures[file] || loading.current.has(file) || failedTextures.current.has(file))
100
129
  return;
101
130
  loading.current.add(file);
102
131
  // Handle full URLs (http/https) or regular paths
@@ -109,12 +138,13 @@ export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selected
109
138
  tex.colorSpace = SRGBColorSpace;
110
139
  setTextures(t => (Object.assign(Object.assign({}, t), { [file]: tex })));
111
140
  }, undefined, (err) => {
112
- console.error(`Failed to load texture: ${path}`, err);
141
+ console.warn(`Failed to load texture: ${path}`, err);
113
142
  loading.current.delete(file);
143
+ failedTextures.current.add(file);
114
144
  });
115
145
  });
116
146
  }, [data, models, textures]);
117
- return (_jsxs("group", { ref: rootRef, children: [_jsx(GameInstanceProvider, { models: models, selectedId: selectedId, editMode: editMode, onSelect: editMode ? onSelect : undefined, registerRef: registerRef, children: _jsx(GameObjectRenderer, { gameObject: data.root, selectedId: selectedId, onSelect: editMode ? onSelect : undefined, onClick: onClick, registerRef: registerRef, registerRigidBodyRef: registerRigidBodyRef, loadedModels: models, loadedTextures: textures, editMode: editMode, parentMatrix: IDENTITY }) }), editMode && (_jsxs(_Fragment, { children: [_jsx(MapControls, { makeDefault: true }), selectedObject && (_jsx(TransformControls, { object: selectedObject, mode: transformMode, space: "local", onObjectChange: onTransformChange, translationSnap: snapResolution > 0 ? snapResolution : undefined, rotationSnap: snapResolution > 0 ? snapResolution : undefined, scaleSnap: snapResolution > 0 ? snapResolution : undefined }, `transform-${snapResolution}`))] }))] }));
147
+ return (_jsxs("group", { ref: rootRef, children: [_jsx(GameInstanceProvider, { models: models, selectedId: selectedId, editMode: editMode, onSelect: editMode ? onSelect : undefined, registerRef: registerRef, children: _jsx(GameObjectRenderer, { gameObject: data.root, selectedId: selectedId, onSelect: editMode ? onSelect : undefined, onClick: onClick, registerRef: registerRef, registerRigidBodyRef: registerRigidBodyRef, loadedModels: models, loadedTextures: textures, editMode: editMode, parentMatrix: IDENTITY }) }), editMode && (_jsxs(_Fragment, { children: [_jsx(MapControls, { ref: controlsRef, makeDefault: true }), selectedObject && (_jsx(TransformControls, { object: selectedObject, mode: transformMode, space: "local", onObjectChange: onTransformChange, translationSnap: positionSnap > 0 ? positionSnap : undefined, rotationSnap: rotationSnap > 0 ? rotationSnap : undefined, scaleSnap: snapResolution > 0 ? snapResolution : undefined }, `transform-${transformMode}-${positionSnap}-${rotationSnap}-${snapResolution}`))] }))] }));
118
148
  });
119
149
  export function GameObjectRenderer(props) {
120
150
  var _a, _b, _c;
@@ -172,7 +202,7 @@ function InstancedNode({ gameObject, parentMatrix = IDENTITY, editMode, register
172
202
  return (_jsx(GameInstance, { id: gameObject.id, modelUrl: (_k = (_j = (_h = gameObject.components) === null || _h === void 0 ? void 0 : _h.model) === null || _j === void 0 ? void 0 : _j.properties) === null || _k === void 0 ? void 0 : _k.filename, position: worldPosition, rotation: worldRotation, scale: worldScale, physics: physicsProps }));
173
203
  }
174
204
  function StandardNode({ gameObject, selectedId, onSelect, onClick, registerRef, registerRigidBodyRef, loadedModels, loadedTextures, editMode, parentMatrix = IDENTITY, }) {
175
- var _a, _b, _c, _d, _e, _f;
205
+ var _a, _b, _c, _d, _e;
176
206
  const groupRef = useRef(null);
177
207
  const helperRef = useRef(null);
178
208
  const clickValid = useRef(false);
@@ -204,7 +234,20 @@ function StandardNode({ gameObject, selectedId, onSelect, onClick, registerRef,
204
234
  const physicsDef = hasPhysics ? getComponent("Physics") : null;
205
235
  const isInstanced = (_e = (_d = (_c = gameObject.components) === null || _c === void 0 ? void 0 : _c.model) === null || _d === void 0 ? void 0 : _d.properties) === null || _e === void 0 ? void 0 : _e.instanced;
206
236
  const physicsKey = `physics_${gameObject.id}_${isInstanced ? 'instanced' : 'standard'}`;
207
- const inner = (_jsxs("group", { onPointerDown: editMode ? onDown : undefined, onPointerMove: editMode ? () => (clickValid.current = false) : undefined, onPointerUp: editMode ? onUp : undefined, children: [renderCoreNode(gameObject, { loadedModels, loadedTextures, editMode, registerRef }, parentMatrix), (_f = gameObject.children) === null || _f === void 0 ? void 0 : _f.map(child => (_jsx(GameObjectRenderer, { gameObject: child, selectedId: selectedId, onSelect: onSelect, onClick: onClick, registerRef: registerRef, registerRigidBodyRef: registerRigidBodyRef, loadedModels: loadedModels, loadedTextures: loadedTextures, editMode: editMode, parentMatrix: world }, child.id)))] }));
237
+ const renderCtx = createRenderContext(loadedModels, loadedTextures, editMode, selectedId, registerRef);
238
+ const childNodes = getChildHostComponents(gameObject).length > 0
239
+ ? renderHostedChildren(gameObject, renderCtx, world)
240
+ : renderSceneChildren(gameObject, world, {
241
+ selectedId,
242
+ onSelect,
243
+ onClick,
244
+ registerRef,
245
+ registerRigidBodyRef,
246
+ loadedModels,
247
+ loadedTextures,
248
+ editMode,
249
+ });
250
+ const inner = (_jsx("group", { onPointerDown: editMode ? onDown : undefined, onPointerMove: editMode ? () => (clickValid.current = false) : undefined, onPointerUp: editMode ? onUp : undefined, children: renderCompositionNode(gameObject, renderCtx, parentMatrix, childNodes) }));
208
251
  if (editMode) {
209
252
  return (_jsxs(_Fragment, { children: [_jsx("group", { ref: groupRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: _jsx("mesh", { visible: false, children: _jsx("boxGeometry", { args: [0.01, 0.01, 0.01] }) }) }), _jsx("group", { ref: helperRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: inner }), hasPhysics && (physicsDef === null || physicsDef === void 0 ? void 0 : physicsDef.View) ? (_jsx(physicsDef.View, { properties: physics.properties, position: transform.position, rotation: transform.rotation, scale: transform.scale, editMode: editMode, nodeId: gameObject.id, registerRigidBodyRef: registerRigidBodyRef, children: inner }, physicsKey)) : null] }));
210
253
  }
@@ -213,6 +256,28 @@ function StandardNode({ gameObject, selectedId, onSelect, onClick, registerRef,
213
256
  }
214
257
  return (_jsx("group", { ref: groupRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, onPointerDown: onDown, onPointerMove: () => (clickValid.current = false), onPointerUp: onUp, children: inner }));
215
258
  }
259
+ const CHILD_HOST_COMPONENT_TYPES = new Set(["Environment"]);
260
+ function isChildHostType(type) {
261
+ return CHILD_HOST_COMPONENT_TYPES.has(type);
262
+ }
263
+ function getChildHostComponents(gameObject) {
264
+ var _a;
265
+ return Object.entries((_a = gameObject.components) !== null && _a !== void 0 ? _a : {}).flatMap(([key, comp]) => {
266
+ if (!(comp === null || comp === void 0 ? void 0 : comp.type) || !isChildHostType(comp.type))
267
+ return [];
268
+ const def = getComponent(comp.type);
269
+ if (!(def === null || def === void 0 ? void 0 : def.View))
270
+ return [];
271
+ return { key, View: def.View, properties: comp.properties };
272
+ });
273
+ }
274
+ function createRenderContext(loadedModels, loadedTextures, editMode, selectedId, registerRef) {
275
+ return { loadedModels, loadedTextures, editMode, selectedId, registerRef };
276
+ }
277
+ function renderSceneChildren(gameObject, parentMatrix, props) {
278
+ var _a;
279
+ return (_a = gameObject.children) === null || _a === void 0 ? void 0 : _a.map(child => _jsx(GameObjectRenderer, { gameObject: child, selectedId: props.selectedId, onSelect: props.onSelect, onClick: props.onClick, registerRef: props.registerRef, registerRigidBodyRef: props.registerRigidBodyRef, loadedModels: props.loadedModels, loadedTextures: props.loadedTextures, editMode: props.editMode, parentMatrix: parentMatrix }, child.id));
280
+ }
216
281
  function walk(node, fn) {
217
282
  var _a;
218
283
  fn(node);
@@ -255,7 +320,25 @@ function computeParentWorldMatrix(root, targetId) {
255
320
  visit(root, IDENTITY);
256
321
  return result !== null && result !== void 0 ? result : IDENTITY;
257
322
  }
258
- function renderCoreNode(gameObject, ctx, parentMatrix) {
323
+ function renderCompositionSubtree(gameObject, ctx, parentMatrix = IDENTITY) {
324
+ if (!gameObject || gameObject.disabled)
325
+ return null;
326
+ const transform = getNodeTransformProps(gameObject);
327
+ const world = parentMatrix.clone().multiply(compose(gameObject));
328
+ const childNodes = renderHostedChildren(gameObject, ctx, world);
329
+ return (_jsx("group", { position: transform.position, rotation: transform.rotation, scale: transform.scale, children: renderCompositionNode(gameObject, ctx, parentMatrix, childNodes) }, gameObject.id));
330
+ }
331
+ function renderHostedChildren(gameObject, ctx, parentMatrix) {
332
+ var _a;
333
+ return (_a = gameObject.children) === null || _a === void 0 ? void 0 : _a.map(child => renderCompositionSubtree(child, ctx, parentMatrix));
334
+ }
335
+ function renderCompositionNode(gameObject, ctx, parentMatrix, childNodes) {
336
+ const ownContent = renderNodeOwnContent(gameObject, ctx, parentMatrix);
337
+ const siblingContent = renderNodeSiblingComponents(gameObject, ctx, parentMatrix);
338
+ const subtree = _jsxs(_Fragment, { children: [ownContent, siblingContent, childNodes] });
339
+ return wrapWithChildHosts(gameObject, ctx, parentMatrix, subtree);
340
+ }
341
+ function renderNodeOwnContent(gameObject, ctx, parentMatrix) {
259
342
  var _a, _b, _c, _d;
260
343
  const geometry = (_a = gameObject.components) === null || _a === void 0 ? void 0 : _a.geometry;
261
344
  const material = (_b = gameObject.components) === null || _b === void 0 ? void 0 : _b.material;
@@ -269,41 +352,59 @@ function renderCoreNode(gameObject, ctx, parentMatrix) {
269
352
  loadedModels: ctx.loadedModels,
270
353
  loadedTextures: ctx.loadedTextures,
271
354
  editMode: ctx.editMode,
355
+ isSelected: ctx.selectedId === gameObject.id,
356
+ nodeId: gameObject.id,
272
357
  parentMatrix,
273
358
  registerRef: ctx.registerRef,
274
359
  };
275
- const wrappers = [];
276
- const leaves = [];
277
- if (gameObject.components) {
278
- Object.entries(gameObject.components)
279
- .filter(([k]) => !getNonComposableKeys().includes(k))
280
- .forEach(([key, comp]) => {
281
- if (!(comp === null || comp === void 0 ? void 0 : comp.type))
282
- return;
283
- const def = getComponent(comp.type);
284
- if (!(def === null || def === void 0 ? void 0 : def.View))
285
- return;
286
- if (def.View.toString().includes("children")) {
287
- wrappers.push({ key, View: def.View, properties: comp.properties });
288
- }
289
- else {
290
- leaves.push(_jsx(def.View, Object.assign({ properties: comp.properties }, contextProps), key));
291
- }
292
- });
293
- }
294
360
  let core;
295
361
  if (model && (modelDef === null || modelDef === void 0 ? void 0 : modelDef.View)) {
296
- core = (_jsxs(modelDef.View, Object.assign({ properties: model.properties }, contextProps, { children: [material && (materialDef === null || materialDef === void 0 ? void 0 : materialDef.View) && (_jsx(materialDef.View, Object.assign({ properties: material.properties }, contextProps), "material")), leaves] })));
362
+ core = (_jsx(modelDef.View, Object.assign({ properties: model.properties }, contextProps, { children: material && (materialDef === null || materialDef === void 0 ? void 0 : materialDef.View) && (_jsx(materialDef.View, Object.assign({ properties: material.properties }, contextProps), "material")) })));
297
363
  }
298
364
  else if (geometry && (geometryDef === null || geometryDef === void 0 ? void 0 : geometryDef.View)) {
299
- core = (_jsxs("mesh", { castShadow: true, receiveShadow: true, children: [_jsx(geometryDef.View, Object.assign({ properties: geometry.properties }, contextProps)), material && (materialDef === null || materialDef === void 0 ? void 0 : materialDef.View) && (_jsx(materialDef.View, Object.assign({ properties: material.properties }, contextProps), "material")), leaves] }));
365
+ core = (_jsxs("mesh", { castShadow: true, receiveShadow: true, children: [_jsx(geometryDef.View, Object.assign({ properties: geometry.properties }, contextProps)), material && (materialDef === null || materialDef === void 0 ? void 0 : materialDef.View) && (_jsx(materialDef.View, Object.assign({ properties: material.properties }, contextProps), "material"))] }));
300
366
  }
301
367
  else if (text && (textDef === null || textDef === void 0 ? void 0 : textDef.View)) {
302
- core = (_jsxs(_Fragment, { children: [_jsx(textDef.View, Object.assign({ properties: text.properties }, contextProps)), leaves] }));
368
+ core = (_jsx(_Fragment, { children: _jsx(textDef.View, Object.assign({ properties: text.properties }, contextProps)) }));
303
369
  }
304
370
  else {
305
- core = _jsx(_Fragment, { children: leaves });
371
+ core = null;
306
372
  }
307
- return wrappers.reduce((acc, { key, View, properties }) => (_jsx(View, Object.assign({ properties: properties }, contextProps, { children: acc }), key)), core);
373
+ return core;
374
+ }
375
+ function renderNodeSiblingComponents(gameObject, ctx, parentMatrix) {
376
+ var _a;
377
+ const contextProps = {
378
+ loadedModels: ctx.loadedModels,
379
+ loadedTextures: ctx.loadedTextures,
380
+ editMode: ctx.editMode,
381
+ isSelected: ctx.selectedId === gameObject.id,
382
+ nodeId: gameObject.id,
383
+ parentMatrix,
384
+ registerRef: ctx.registerRef,
385
+ };
386
+ return Object.entries((_a = gameObject.components) !== null && _a !== void 0 ? _a : {})
387
+ .filter(([key]) => !getNonComposableKeys().includes(key))
388
+ .flatMap(([key, comp]) => {
389
+ if (!(comp === null || comp === void 0 ? void 0 : comp.type) || isChildHostType(comp.type))
390
+ return [];
391
+ const def = getComponent(comp.type);
392
+ if (!(def === null || def === void 0 ? void 0 : def.View))
393
+ return [];
394
+ return _jsx(def.View, Object.assign({ properties: comp.properties }, contextProps), key);
395
+ });
396
+ }
397
+ function wrapWithChildHosts(gameObject, ctx, parentMatrix, subtree) {
398
+ const contextProps = {
399
+ loadedModels: ctx.loadedModels,
400
+ loadedTextures: ctx.loadedTextures,
401
+ editMode: ctx.editMode,
402
+ isSelected: ctx.selectedId === gameObject.id,
403
+ nodeId: gameObject.id,
404
+ parentMatrix,
405
+ registerRef: ctx.registerRef,
406
+ };
407
+ const childHosts = getChildHostComponents(gameObject);
408
+ return childHosts.reduce((acc, { key, View, properties }) => (_jsx(View, Object.assign({ properties: properties }, contextProps, { children: acc }), key)), subtree);
308
409
  }
309
410
  export default PrefabRoot;
@@ -1,11 +1,7 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { FieldRenderer } from "./Input";
3
- const ambientLightFields = [
4
- { name: 'color', type: 'color', label: 'Color' },
5
- { name: 'intensity', type: 'number', label: 'Intensity', step: 0.1, min: 0 },
6
- ];
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ColorField, FieldGroup, NumberField } from "./Input";
7
3
  function AmbientLightComponentEditor({ component, onUpdate, }) {
8
- return (_jsx(FieldRenderer, { fields: ambientLightFields, values: component.properties, onChange: onUpdate }));
4
+ return (_jsxs(FieldGroup, { children: [_jsx(ColorField, { name: "color", label: "Color", values: component.properties, onChange: onUpdate }), _jsx(NumberField, { name: "intensity", label: "Intensity", values: component.properties, onChange: onUpdate, min: 0, step: 0.1, fallback: 1 })] }));
9
5
  }
10
6
  function AmbientLightComponentView({ properties }) {
11
7
  const { color = '#ffffff', intensity = 1 } = properties;
@@ -0,0 +1,3 @@
1
+ import { Component } from './ComponentRegistry';
2
+ declare const CameraComponent: Component;
3
+ export default CameraComponent;
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { PerspectiveCamera, useHelper } from '@react-three/drei';
3
+ import { useRef } from 'react';
4
+ import { CameraHelper } from 'three';
5
+ import { FieldGroup, NumberField } from './Input';
6
+ function CameraComponentEditor({ component, onUpdate }) {
7
+ return (_jsxs(FieldGroup, { children: [_jsx(NumberField, { name: "fov", label: "FOV", values: component.properties, onChange: onUpdate, fallback: 50, min: 1, max: 179, step: 1 }), _jsx(NumberField, { name: "near", label: "Near", values: component.properties, onChange: onUpdate, fallback: 0.1, min: 0.001, step: 0.1 }), _jsx(NumberField, { name: "zoom", label: "Zoom", values: component.properties, onChange: onUpdate, fallback: 1, min: 0.01, step: 0.1 }), _jsx(NumberField, { name: "far", label: "Far", values: component.properties, onChange: onUpdate, fallback: 1000, min: 0.1, step: 1 })] }));
8
+ }
9
+ function CameraComponentView({ properties, editMode, isSelected }) {
10
+ var _a, _b, _c, _d;
11
+ const fov = (_a = properties === null || properties === void 0 ? void 0 : properties.fov) !== null && _a !== void 0 ? _a : 50;
12
+ const near = (_b = properties === null || properties === void 0 ? void 0 : properties.near) !== null && _b !== void 0 ? _b : 0.1;
13
+ const zoom = (_c = properties === null || properties === void 0 ? void 0 : properties.zoom) !== null && _c !== void 0 ? _c : 1;
14
+ const far = (_d = properties === null || properties === void 0 ? void 0 : properties.far) !== null && _d !== void 0 ? _d : 1000;
15
+ const cameraRef = useRef(null);
16
+ useHelper(editMode && isSelected ? cameraRef : null, CameraHelper);
17
+ return (_jsxs(_Fragment, { children: [_jsx(PerspectiveCamera, { ref: cameraRef, makeDefault: !editMode, fov: fov, near: near, zoom: zoom, far: far }), editMode && !isSelected ? (_jsxs("mesh", { children: [_jsx("boxGeometry", { args: [0.34, 0.22, 0.18] }), _jsx("meshBasicMaterial", { color: "#22d3ee", wireframe: true })] })) : null] }));
18
+ }
19
+ const CameraComponent = {
20
+ name: 'Camera',
21
+ Editor: CameraComponentEditor,
22
+ View: CameraComponentView,
23
+ defaultProperties: {},
24
+ };
25
+ export default CameraComponent;
@@ -31,7 +31,7 @@ const directionalLightFields = [
31
31
  function DirectionalLightComponentEditor({ component, onUpdate }) {
32
32
  return (_jsx(FieldRenderer, { fields: directionalLightFields, values: component.properties, onChange: onUpdate }));
33
33
  }
34
- function DirectionalLightView({ properties, editMode }) {
34
+ function DirectionalLightView({ properties, editMode, isSelected }) {
35
35
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
36
36
  const color = (_a = properties.color) !== null && _a !== void 0 ? _a : '#ffffff';
37
37
  const intensity = (_b = properties.intensity) !== null && _b !== void 0 ? _b : 1.0;
@@ -61,7 +61,7 @@ function DirectionalLightView({ properties, editMode }) {
61
61
  // Target is positioned relative to light's world position
62
62
  targetRef.current.position.set(lightWorldPos.x + targetOffset[0], lightWorldPos.y + targetOffset[1], lightWorldPos.z + targetOffset[2]);
63
63
  });
64
- return (_jsxs(_Fragment, { children: [_jsx("directionalLight", { ref: directionalLightRef, color: color, intensity: intensity, castShadow: castShadow, "shadow-mapSize-width": shadowMapSize, "shadow-mapSize-height": shadowMapSize, "shadow-camera-near": shadowCameraNear, "shadow-camera-far": shadowCameraFar, "shadow-camera-top": shadowCameraTop, "shadow-camera-bottom": shadowCameraBottom, "shadow-camera-left": shadowCameraLeft, "shadow-camera-right": shadowCameraRight, "shadow-bias": -0.001, "shadow-normalBias": 0.02 }), _jsx("object3D", { ref: targetRef }), editMode && (_jsxs(_Fragment, { children: [_jsxs("mesh", { children: [_jsx("sphereGeometry", { args: [0.3, 8, 6] }), _jsx("meshBasicMaterial", { color: color, wireframe: true })] }), _jsxs("mesh", { position: targetOffset, children: [_jsx("sphereGeometry", { args: [0.2, 8, 6] }), _jsx("meshBasicMaterial", { color: color, wireframe: true, opacity: 0.5, transparent: true })] }), _jsxs("line", { children: [_jsx("bufferGeometry", { onUpdate: (geo) => {
64
+ return (_jsxs(_Fragment, { children: [_jsx("directionalLight", { ref: directionalLightRef, color: color, intensity: intensity, castShadow: castShadow, "shadow-mapSize-width": shadowMapSize, "shadow-mapSize-height": shadowMapSize, "shadow-camera-near": shadowCameraNear, "shadow-camera-far": shadowCameraFar, "shadow-camera-top": shadowCameraTop, "shadow-camera-bottom": shadowCameraBottom, "shadow-camera-left": shadowCameraLeft, "shadow-camera-right": shadowCameraRight, "shadow-bias": -0.001, "shadow-normalBias": 0.02 }), _jsx("object3D", { ref: targetRef }), editMode && isSelected && (_jsxs(_Fragment, { children: [_jsxs("mesh", { children: [_jsx("sphereGeometry", { args: [0.3, 8, 6] }), _jsx("meshBasicMaterial", { color: color, wireframe: true })] }), _jsxs("mesh", { position: targetOffset, children: [_jsx("sphereGeometry", { args: [0.2, 8, 6] }), _jsx("meshBasicMaterial", { color: color, wireframe: true, opacity: 0.5, transparent: true })] }), _jsxs("line", { children: [_jsx("bufferGeometry", { onUpdate: (geo) => {
65
65
  const points = [
66
66
  new Vector3(0, 0, 0),
67
67
  new Vector3(targetOffset[0], targetOffset[1], targetOffset[2])
@@ -0,0 +1,3 @@
1
+ import { Component } from './ComponentRegistry';
2
+ declare const EnvironmentComponent: Component;
3
+ export default EnvironmentComponent;
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Environment } from '@react-three/drei';
3
+ import { FieldGroup, NumberField } from './Input';
4
+ function EnvironmentView({ properties, children, editMode, loadedTextures, loadedModels, }) {
5
+ const { intensity = 1, resolution = 256 } = properties;
6
+ const assetRevision = `${Object.keys(loadedTextures !== null && loadedTextures !== void 0 ? loadedTextures : {}).sort().join('|')}::${Object.keys(loadedModels !== null && loadedModels !== void 0 ? loadedModels : {}).sort().join('|')}`;
7
+ return (_jsx(Environment, { background: true, environmentIntensity: intensity, resolution: resolution, frames: editMode ? undefined : 1, children: children }, assetRevision));
8
+ }
9
+ const EnvironmentComponent = {
10
+ name: 'Environment',
11
+ Editor: ({ component, onUpdate }) => (_jsxs(FieldGroup, { children: [_jsx(NumberField, { name: "intensity", label: "Intensity", values: component.properties, onChange: onUpdate, min: 0, step: 0.1, fallback: 1 }), _jsx(NumberField, { name: "resolution", label: "Resolution", values: component.properties, onChange: onUpdate, min: 64, step: 64, fallback: 256 })] })),
12
+ View: EnvironmentView,
13
+ defaultProperties: {},
14
+ };
15
+ export default EnvironmentComponent;
@@ -1,67 +1,67 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { FieldRenderer, Input } from "./Input";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { FieldGroup, NumberField, SelectField } from "./Input";
3
3
  const GEOMETRY_ARGS = {
4
4
  box: {
5
- labels: ["Width", "Height", "Depth"],
6
- defaults: [1, 1, 1],
5
+ fields: [
6
+ { name: 'width', label: 'Width', defaultValue: 1, min: 0.01, step: 0.1 },
7
+ { name: 'height', label: 'Height', defaultValue: 1, min: 0.01, step: 0.1 },
8
+ { name: 'depth', label: 'Depth', defaultValue: 1, min: 0.01, step: 0.1 },
9
+ ],
7
10
  },
8
11
  sphere: {
9
- labels: ["Radius", "Width Segments", "Height Segments"],
10
- defaults: [1, 32, 16],
12
+ fields: [
13
+ { name: 'radius', label: 'Radius', defaultValue: 1, min: 0.01, step: 0.1 },
14
+ { name: 'widthSegments', label: 'Width Segments', defaultValue: 32, min: 3, step: 1 },
15
+ { name: 'heightSegments', label: 'Height Segments', defaultValue: 16, min: 2, step: 1 },
16
+ ],
11
17
  },
12
18
  plane: {
13
- labels: ["Width", "Height"],
14
- defaults: [1, 1],
19
+ fields: [
20
+ { name: 'width', label: 'Width', defaultValue: 1, min: 0.01, step: 0.1 },
21
+ { name: 'height', label: 'Height', defaultValue: 1, min: 0.01, step: 0.1 },
22
+ ],
15
23
  },
16
24
  cylinder: {
17
- labels: ["Radius Top", "Radius Bottom", "Height", "Radial Segments"],
18
- defaults: [1, 1, 1, 32],
25
+ fields: [
26
+ { name: 'radiusTop', label: 'Radius Top', defaultValue: 1, min: 0.01, step: 0.1 },
27
+ { name: 'radiusBottom', label: 'Radius Bottom', defaultValue: 1, min: 0.01, step: 0.1 },
28
+ { name: 'height', label: 'Height', defaultValue: 1, min: 0.01, step: 0.1 },
29
+ { name: 'radialSegments', label: 'Radial Segments', defaultValue: 32, min: 3, step: 1 },
30
+ ],
19
31
  },
20
32
  };
33
+ function getDefaultArgs(geometryType) {
34
+ var _a, _b;
35
+ return ((_b = (_a = GEOMETRY_ARGS[geometryType]) === null || _a === void 0 ? void 0 : _a.fields) !== null && _b !== void 0 ? _b : []).map(field => field.defaultValue);
36
+ }
21
37
  function GeometryComponentEditor({ component, onUpdate, }) {
22
- const { geometryType, args = [] } = component.properties;
23
- const schema = GEOMETRY_ARGS[geometryType];
24
- const fields = [
25
- {
26
- name: 'geometryType',
27
- type: 'select',
28
- label: 'Type',
29
- options: [
30
- { value: 'box', label: 'Box' },
31
- { value: 'sphere', label: 'Sphere' },
32
- { value: 'plane', label: 'Plane' },
33
- { value: 'cylinder', label: 'Cylinder' },
34
- ],
35
- },
36
- {
37
- name: 'args',
38
- type: 'custom',
39
- label: '',
40
- render: ({ values, onChangeMultiple }) => {
41
- const currentType = values.geometryType;
42
- const currentSchema = GEOMETRY_ARGS[currentType];
43
- const currentArgs = values.args || currentSchema.defaults;
44
- return (_jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 4 }, children: currentSchema.labels.map((label, i) => {
45
- var _a;
46
- return (_jsx(Input, { label: label, value: (_a = currentArgs[i]) !== null && _a !== void 0 ? _a : currentSchema.defaults[i], step: 0.1, min: 0.01, onChange: value => {
47
- const next = [...currentArgs];
48
- next[i] = value;
49
- onChangeMultiple({ args: next });
50
- } }, label));
51
- }) }));
52
- },
53
- },
54
- ];
38
+ var _a, _b, _c;
39
+ const geometryType = (_a = component.properties.geometryType) !== null && _a !== void 0 ? _a : 'box';
40
+ const schema = (_b = GEOMETRY_ARGS[geometryType]) !== null && _b !== void 0 ? _b : GEOMETRY_ARGS.box;
41
+ const args = (_c = component.properties.args) !== null && _c !== void 0 ? _c : getDefaultArgs(geometryType);
55
42
  // Handle geometry type change to reset args
56
43
  const handleChange = (newValues) => {
57
44
  if ('geometryType' in newValues && newValues.geometryType !== geometryType) {
58
- onUpdate({ geometryType: newValues.geometryType, args: GEOMETRY_ARGS[newValues.geometryType].defaults });
45
+ onUpdate({ geometryType: newValues.geometryType, args: getDefaultArgs(newValues.geometryType) });
59
46
  }
60
47
  else {
61
48
  onUpdate(newValues);
62
49
  }
63
50
  };
64
- return (_jsx(FieldRenderer, { fields: fields, values: component.properties, onChange: handleChange }));
51
+ const updateArg = (index, value) => {
52
+ const next = [...args];
53
+ next[index] = value;
54
+ onUpdate({ args: next });
55
+ };
56
+ return (_jsxs(FieldGroup, { children: [_jsx(SelectField, { name: "geometryType", label: "Type", values: component.properties, onChange: handleChange, options: [
57
+ { value: 'box', label: 'Box' },
58
+ { value: 'sphere', label: 'Sphere' },
59
+ { value: 'plane', label: 'Plane' },
60
+ { value: 'cylinder', label: 'Cylinder' },
61
+ ] }), schema.fields.map((field, index) => {
62
+ var _a;
63
+ return (_jsx(NumberField, { name: field.name, label: field.label, values: { [field.name]: (_a = args[index]) !== null && _a !== void 0 ? _a : field.defaultValue }, onChange: (next) => updateArg(index, next[field.name]), fallback: field.defaultValue, min: field.min, step: field.step }, field.name));
64
+ })] }));
65
65
  }
66
66
  // View for Geometry component
67
67
  function GeometryComponentView({ properties, children }) {
@@ -87,7 +87,7 @@ const GeometryComponent = {
87
87
  nonComposable: true,
88
88
  defaultProperties: {
89
89
  geometryType: 'box',
90
- args: GEOMETRY_ARGS.box.defaults,
90
+ args: getDefaultArgs('box'),
91
91
  }
92
92
  };
93
93
  export default GeometryComponent;
@@ -58,11 +58,12 @@ export declare function Input({ value, onChange, step, min, max, style, label }:
58
58
  export declare function Label({ children }: {
59
59
  children: React.ReactNode;
60
60
  }): import("react/jsx-runtime").JSX.Element;
61
- export declare function Vector3Input({ label, value, onChange, snap }: {
61
+ export declare function Vector3Input({ label, value, onChange, snap, labelExtra }: {
62
62
  label: string;
63
63
  value: [number, number, number];
64
64
  onChange: (v: [number, number, number]) => void;
65
65
  snap?: number;
66
+ labelExtra?: React.ReactNode;
66
67
  }): import("react/jsx-runtime").JSX.Element;
67
68
  export declare function ColorInput({ label, value, onChange }: {
68
69
  label?: string;
@@ -89,6 +90,55 @@ export declare function SelectInput({ label, value, onChange, options }: {
89
90
  label: string;
90
91
  }[];
91
92
  }): import("react/jsx-runtime").JSX.Element;
93
+ interface BoundFieldProps {
94
+ name: string;
95
+ values: Record<string, any>;
96
+ onChange: (values: Record<string, any>) => void;
97
+ }
98
+ interface BoundNumberFieldProps extends BoundFieldProps {
99
+ label: string;
100
+ fallback?: number;
101
+ step?: string | number;
102
+ min?: number;
103
+ max?: number;
104
+ style?: React.CSSProperties;
105
+ }
106
+ interface BoundStringFieldProps extends BoundFieldProps {
107
+ label: string;
108
+ fallback?: string;
109
+ placeholder?: string;
110
+ }
111
+ interface BoundColorFieldProps extends BoundFieldProps {
112
+ label: string;
113
+ fallback?: string;
114
+ }
115
+ interface BoundBooleanFieldProps extends BoundFieldProps {
116
+ label: string;
117
+ fallback?: boolean;
118
+ }
119
+ interface BoundSelectFieldProps extends BoundFieldProps {
120
+ label: string;
121
+ fallback?: string;
122
+ options: {
123
+ value: string;
124
+ label: string;
125
+ }[];
126
+ }
127
+ interface BoundVector3FieldProps extends BoundFieldProps {
128
+ label: string;
129
+ fallback?: [number, number, number];
130
+ snap?: number;
131
+ labelExtra?: React.ReactNode;
132
+ }
133
+ export declare function FieldGroup({ children }: {
134
+ children: React.ReactNode;
135
+ }): import("react/jsx-runtime").JSX.Element;
136
+ export declare function NumberField({ name, label, values, onChange, fallback, step, min, max, style, }: BoundNumberFieldProps): import("react/jsx-runtime").JSX.Element;
137
+ export declare function StringField({ name, label, values, onChange, fallback, placeholder, }: BoundStringFieldProps): import("react/jsx-runtime").JSX.Element;
138
+ export declare function ColorField({ name, label, values, onChange, fallback, }: BoundColorFieldProps): import("react/jsx-runtime").JSX.Element;
139
+ export declare function BooleanField({ name, label, values, onChange, fallback, }: BoundBooleanFieldProps): import("react/jsx-runtime").JSX.Element;
140
+ export declare function SelectField({ name, label, values, onChange, fallback, options, }: BoundSelectFieldProps): import("react/jsx-runtime").JSX.Element;
141
+ export declare function Vector3Field({ name, label, values, onChange, fallback, snap, labelExtra, }: BoundVector3FieldProps): import("react/jsx-runtime").JSX.Element;
92
142
  interface FieldRendererProps {
93
143
  fields: FieldDefinition[];
94
144
  values: Record<string, any>;