react-three-game 0.0.57 → 0.0.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/.github/copilot-instructions.md +1 -1
  2. package/README.md +59 -35
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.js +1 -1
  5. package/dist/tools/assetviewer/page.js +1 -1
  6. package/dist/tools/dragdrop/DragDropLoader.d.ts +19 -6
  7. package/dist/tools/dragdrop/DragDropLoader.js +77 -40
  8. package/dist/tools/dragdrop/index.d.ts +4 -0
  9. package/dist/tools/dragdrop/index.js +2 -0
  10. package/dist/tools/dragdrop/modelLoader.d.ts +5 -6
  11. package/dist/tools/dragdrop/modelLoader.js +62 -49
  12. package/dist/tools/dragdrop/page.js +3 -3
  13. package/dist/tools/prefabeditor/EditorTree.js +24 -48
  14. package/dist/tools/prefabeditor/EditorTreeMenus.d.ts +33 -0
  15. package/dist/tools/prefabeditor/EditorTreeMenus.js +136 -0
  16. package/dist/tools/prefabeditor/PrefabEditor.js +1 -1
  17. package/dist/tools/prefabeditor/PrefabRoot.js +5 -3
  18. package/dist/tools/prefabeditor/components/CameraComponent.js +32 -12
  19. package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +49 -23
  20. package/dist/tools/prefabeditor/components/MaterialComponent.d.ts +8 -0
  21. package/dist/tools/prefabeditor/components/MaterialComponent.js +11 -5
  22. package/dist/tools/prefabeditor/components/SpotLightComponent.js +34 -13
  23. package/package.json +2 -2
  24. package/react-three-game-skill/react-three-game/SKILL.md +63 -5
  25. package/react-three-game-skill/react-three-game/rules/ADVANCED_PHYSICS.md +7 -5
  26. package/src/index.ts +1 -1
  27. package/src/tools/assetviewer/page.tsx +1 -1
  28. package/src/tools/dragdrop/DragDropLoader.tsx +118 -55
  29. package/src/tools/dragdrop/index.ts +4 -0
  30. package/src/tools/dragdrop/modelLoader.ts +95 -50
  31. package/src/tools/dragdrop/page.tsx +7 -4
  32. package/src/tools/prefabeditor/EditorTree.tsx +56 -125
  33. package/src/tools/prefabeditor/EditorTreeMenus.tsx +307 -0
  34. package/src/tools/prefabeditor/PrefabEditor.tsx +1 -1
  35. package/src/tools/prefabeditor/PrefabRoot.tsx +6 -3
  36. package/src/tools/prefabeditor/components/CameraComponent.tsx +51 -14
  37. package/src/tools/prefabeditor/components/DirectionalLightComponent.tsx +59 -28
  38. package/src/tools/prefabeditor/components/MaterialComponent.tsx +18 -9
  39. package/src/tools/prefabeditor/components/SpotLightComponent.tsx +49 -18
@@ -1,19 +1,11 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
11
2
  import { useState } from 'react';
12
3
  import { getComponent } from './components/ComponentRegistry';
13
- import { base, colors, tree, menu } from './styles';
14
- import { findNode, findParent, deleteNode, cloneNode, updateNodeById, loadJson, saveJson, regenerateIds } from './utils';
4
+ import { base, colors, tree } from './styles';
5
+ import { findNode, findParent, deleteNode, cloneNode, updateNodeById } from './utils';
15
6
  import { useEditorContext } from './EditorContext';
16
7
  import { Dropdown } from './Dropdown';
8
+ import { FileMenu, MenuTriggerButton, TreeContextMenu, TreeNodeMenu } from './EditorTreeMenus';
17
9
  function moveNode(root, draggedId, targetId, position) {
18
10
  const draggedNode = findNode(root, draggedId);
19
11
  const oldParent = findParent(root, draggedId);
@@ -79,6 +71,7 @@ export default function EditorTree({ prefabData, setPrefabData, selectedId, setS
79
71
  const [collapsedIds, setCollapsedIds] = useState(new Set());
80
72
  const [collapsed, setCollapsed] = useState(false);
81
73
  const [searchQuery, setSearchQuery] = useState('');
74
+ const [contextMenu, setContextMenu] = useState(null);
82
75
  if (!prefabData || !setPrefabData)
83
76
  return null;
84
77
  const toggleCollapse = (e, id) => {
@@ -128,6 +121,16 @@ export default function EditorTree({ prefabData, setPrefabData, selectedId, setS
128
121
  const handleToggleDisabled = (nodeId) => {
129
122
  setPrefabData(prev => (Object.assign(Object.assign({}, prev), { root: updateNodeById(prev.root, nodeId, node => (Object.assign(Object.assign({}, node), { disabled: !node.disabled }))) })));
130
123
  };
124
+ const closeContextMenu = () => setContextMenu(null);
125
+ const openContextMenu = (nodeId, x, y) => {
126
+ setSelectedId(nodeId);
127
+ setContextMenu({ nodeId, x, y });
128
+ };
129
+ const handleFocus = (nodeId) => {
130
+ setSelectedId(nodeId);
131
+ onFocusNode === null || onFocusNode === void 0 ? void 0 : onFocusNode(nodeId);
132
+ };
133
+ const renderTreeNodeMenu = (nodeId, isRoot, onClose) => (_jsx(TreeNodeMenu, { isRoot: isRoot, nodeId: nodeId, onAddChild: handleAddChild, onFocus: handleFocus, onDuplicate: isRoot ? undefined : handleDuplicate, onDelete: isRoot ? undefined : handleDelete, onClose: onClose }));
131
134
  const handleDragStart = (e, id) => {
132
135
  if (id === prefabData.root.id)
133
136
  return e.preventDefault();
@@ -190,13 +193,17 @@ export default function EditorTree({ prefabData, setPrefabData, selectedId, setS
190
193
  const isDropTarget = (dropTarget === null || dropTarget === void 0 ? void 0 : dropTarget.id) === node.id;
191
194
  const showDropBefore = isDropTarget && (dropTarget === null || dropTarget === void 0 ? void 0 : dropTarget.position) === 'before';
192
195
  const showDropInside = isDropTarget && (dropTarget === null || dropTarget === void 0 ? void 0 : dropTarget.position) === 'inside';
193
- return (_jsxs("div", { children: [_jsxs("div", { style: Object.assign(Object.assign(Object.assign({}, tree.row), (isSelected ? tree.selected : {})), { paddingLeft: `${depth * 12 + 6}px`, opacity: node.disabled ? 0.4 : 1, display: 'flex', alignItems: 'center', justifyContent: 'space-between', borderTop: showDropBefore ? `2px solid ${colors.accent}` : undefined, boxShadow: showDropInside ? `inset 0 0 0 1px ${colors.accentBorder}` : undefined }), draggable: !isRoot, onClick: (e) => { e.stopPropagation(); setSelectedId(node.id); }, onDragStart: (e) => handleDragStart(e, node.id), onDragEnd: () => { setDraggedId(null); setDropTarget(null); }, onDragOver: (e) => handleDragOver(e, node.id, isRoot), onDragLeave: (e) => handleDragLeave(e, node.id), onDrop: (e) => handleDrop(e, node.id, isRoot), children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', flex: 1, minWidth: 0 }, children: [_jsx("span", { style: {
196
+ return (_jsxs("div", { children: [_jsxs("div", { style: Object.assign(Object.assign(Object.assign({}, tree.row), (isSelected ? tree.selected : {})), { paddingLeft: `${depth * 12 + 6}px`, opacity: node.disabled ? 0.4 : 1, display: 'flex', alignItems: 'center', justifyContent: 'space-between', borderTop: showDropBefore ? `2px solid ${colors.accent}` : undefined, boxShadow: showDropInside ? `inset 0 0 0 1px ${colors.accentBorder}` : undefined }), draggable: !isRoot, onClick: (e) => { e.stopPropagation(); setSelectedId(node.id); }, onContextMenu: (e) => {
197
+ e.preventDefault();
198
+ e.stopPropagation();
199
+ openContextMenu(node.id, e.clientX, e.clientY);
200
+ }, onDragStart: (e) => handleDragStart(e, node.id), onDragEnd: () => { setDraggedId(null); setDropTarget(null); }, onDragOver: (e) => handleDragOver(e, node.id, isRoot), onDragLeave: (e) => handleDragLeave(e, node.id), onDrop: (e) => handleDrop(e, node.id, isRoot), children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', flex: 1, minWidth: 0 }, children: [_jsx("span", { style: {
194
201
  width: 12,
195
202
  opacity: 0.6,
196
203
  marginRight: 4,
197
204
  cursor: 'pointer',
198
205
  visibility: hasChildren ? 'visible' : 'hidden'
199
- }, onClick: (e) => hasChildren && toggleCollapse(e, node.id), children: isCollapsed ? '▶' : '▼' }), !isRoot && _jsx("span", { style: { marginRight: 4, opacity: 0.4 }, children: "\u22EE\u22EE" }), _jsx("span", { style: { overflow: 'hidden', textOverflow: 'ellipsis' }, children: (_a = node.name) !== null && _a !== void 0 ? _a : node.id })] }), !isRoot && (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Dropdown, { placement: "bottom-end", trigger: ({ ref, toggle }) => (_jsx("button", { ref: ref, style: {
206
+ }, onClick: (e) => hasChildren && toggleCollapse(e, node.id), children: isCollapsed ? '▶' : '▼' }), !isRoot && _jsx("span", { style: { marginRight: 4, opacity: 0.4 }, children: "\u22EE\u22EE" }), _jsx("span", { style: { overflow: 'hidden', textOverflow: 'ellipsis' }, children: (_a = node.name) !== null && _a !== void 0 ? _a : node.id })] }), !isRoot && (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Dropdown, { placement: "bottom-end", trigger: ({ ref, toggle }) => (_jsx(MenuTriggerButton, { buttonRef: ref, onToggle: toggle, title: "Node Actions", style: {
200
207
  background: 'none',
201
208
  border: 'none',
202
209
  cursor: 'pointer',
@@ -204,10 +211,7 @@ export default function EditorTree({ prefabData, setPrefabData, selectedId, setS
204
211
  fontSize: 14,
205
212
  opacity: 0.7,
206
213
  color: 'inherit',
207
- }, onClick: (e) => {
208
- e.stopPropagation();
209
- toggle();
210
- }, title: "Node Actions", children: "\u22EF" })), children: (close) => (_jsxs("div", { style: Object.assign(Object.assign({}, menu.container), { position: 'static' }), onClick: (e) => e.stopPropagation(), children: [_jsx("button", { style: menu.item, onClick: () => { handleAddChild(node.id); close(); }, children: "Add Child" }), _jsx("button", { style: menu.item, onClick: () => { setSelectedId(node.id); onFocusNode === null || onFocusNode === void 0 ? void 0 : onFocusNode(node.id); close(); }, children: "Focus Camera" }), _jsx("button", { style: menu.item, onClick: () => { handleDuplicate(node.id); close(); }, children: "Duplicate" }), _jsx("button", { style: Object.assign(Object.assign({}, menu.item), menu.danger), onClick: () => { handleDelete(node.id); close(); }, children: "Delete" })] })) }), _jsx("button", { style: {
214
+ }, children: "\u22EF" })), children: (close) => renderTreeNodeMenu(node.id, false, close) }), _jsx("button", { style: {
211
215
  background: 'none',
212
216
  border: 'none',
213
217
  cursor: 'pointer',
@@ -218,7 +222,7 @@ export default function EditorTree({ prefabData, setPrefabData, selectedId, setS
218
222
  }, onClick: (e) => {
219
223
  e.stopPropagation();
220
224
  handleToggleDisabled(node.id);
221
- }, title: node.disabled ? 'Enable' : 'Disable', children: node.disabled ? '◎' : '◉' })] })), isRoot && (_jsx(Dropdown, { placement: "bottom-end", trigger: ({ ref, toggle }) => (_jsx("button", { ref: ref, style: {
225
+ }, title: node.disabled ? 'Enable' : 'Disable', children: node.disabled ? '◎' : '◉' })] })), isRoot && (_jsx(Dropdown, { placement: "bottom-end", trigger: ({ ref, toggle }) => (_jsx(MenuTriggerButton, { buttonRef: ref, onToggle: toggle, title: "Scene Actions", style: {
222
226
  background: 'none',
223
227
  border: 'none',
224
228
  cursor: 'pointer',
@@ -226,35 +230,7 @@ export default function EditorTree({ prefabData, setPrefabData, selectedId, setS
226
230
  fontSize: 14,
227
231
  opacity: 0.7,
228
232
  color: 'inherit',
229
- }, onClick: (e) => {
230
- e.stopPropagation();
231
- toggle();
232
- }, title: "Scene Actions", children: "\u22EF" })), children: (close) => (_jsxs("div", { style: Object.assign(Object.assign({}, menu.container), { position: 'static' }), onClick: (e) => e.stopPropagation(), children: [_jsx("button", { style: menu.item, onClick: () => { handleAddChild(node.id); close(); }, children: "Add Child" }), _jsx("button", { style: menu.item, onClick: () => { setSelectedId(node.id); onFocusNode === null || onFocusNode === void 0 ? void 0 : onFocusNode(node.id); close(); }, children: "Focus Camera" })] })) }))] }), !isCollapsed && node.children && node.children.map(child => renderNode(child, depth + 1))] }, node.id));
233
+ }, children: "\u22EF" })), children: (close) => renderTreeNodeMenu(node.id, true, close) }))] }), !isCollapsed && node.children && node.children.map(child => renderNode(child, depth + 1))] }, node.id));
233
234
  };
234
- return (_jsx(_Fragment, { children: _jsxs("div", { style: Object.assign(Object.assign({}, tree.panel), { width: collapsed ? 'auto' : 224 }), children: [_jsxs("div", { style: base.header, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 6, cursor: 'pointer' }, onClick: () => setCollapsed(!collapsed), children: [_jsx("span", { children: collapsed ? '▶' : '▼' }), _jsx("span", { children: "Scene" })] }), !collapsed && (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 4 }, children: [_jsx("button", { style: Object.assign(Object.assign({}, base.btn), { padding: '2px 6px', fontSize: 10, opacity: canUndo ? 1 : 0.4 }), onClick: (e) => { e.stopPropagation(); onUndo === null || onUndo === void 0 ? void 0 : onUndo(); }, disabled: !canUndo, title: "Undo", children: "\u21B6" }), _jsx("button", { style: Object.assign(Object.assign({}, base.btn), { padding: '2px 6px', fontSize: 10, opacity: canRedo ? 1 : 0.4 }), onClick: (e) => { e.stopPropagation(); onRedo === null || onRedo === void 0 ? void 0 : onRedo(); }, disabled: !canRedo, title: "Redo", children: "\u21B7" }), _jsx(Dropdown, { placement: "bottom-end", trigger: ({ ref, toggle }) => (_jsx("button", { ref: ref, style: Object.assign(Object.assign({}, base.btn), { padding: '2px 6px', fontSize: 10 }), onClick: (e) => { e.stopPropagation(); toggle(); }, title: "File", children: "\u22EE" })), children: (close) => (_jsx(FileMenu, { prefabData: prefabData, setPrefabData: setPrefabData, onClose: close })) })] }))] }), !collapsed && (_jsxs(_Fragment, { children: [_jsx("div", { style: { padding: '4px 4px', borderBottom: `1px solid ${colors.borderLight}` }, children: _jsx("input", { type: "text", placeholder: "Search nodes...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), onClick: (e) => e.stopPropagation(), style: Object.assign(Object.assign({}, base.input), { padding: '4px 8px' }) }) }), _jsx("div", { className: "tree-scroll", style: tree.scroll, children: renderNode(prefabData.root) })] }))] }) }));
235
- }
236
- function FileMenu({ prefabData, setPrefabData, onClose }) {
237
- const { onScreenshot, onExportGLB } = useEditorContext();
238
- const handleLoad = () => __awaiter(this, void 0, void 0, function* () {
239
- const loadedPrefab = yield loadJson();
240
- if (!loadedPrefab)
241
- return;
242
- setPrefabData(loadedPrefab);
243
- onClose();
244
- });
245
- const handleSave = () => {
246
- saveJson(prefabData, "prefab");
247
- onClose();
248
- };
249
- const handleLoadIntoScene = () => __awaiter(this, void 0, void 0, function* () {
250
- const loadedPrefab = yield loadJson();
251
- if (!loadedPrefab)
252
- return;
253
- setPrefabData(prev => (Object.assign(Object.assign({}, prev), { root: updateNodeById(prev.root, prev.root.id, root => {
254
- var _a;
255
- return (Object.assign(Object.assign({}, root), { children: [...((_a = root.children) !== null && _a !== void 0 ? _a : []), regenerateIds(loadedPrefab.root)] }));
256
- }) })));
257
- onClose();
258
- });
259
- return (_jsxs("div", { style: Object.assign(Object.assign({}, menu.container), { position: 'static' }), onClick: (e) => e.stopPropagation(), children: [_jsx("button", { style: menu.item, onClick: handleLoad, children: "\uD83D\uDCE5 Load Prefab JSON" }), _jsx("button", { style: menu.item, onClick: handleSave, children: "\uD83D\uDCBE Save Prefab JSON" }), _jsx("button", { style: menu.item, onClick: handleLoadIntoScene, children: "\uD83D\uDCC2 Load into Scene" }), _jsx("button", { style: menu.item, onClick: () => { onScreenshot === null || onScreenshot === void 0 ? void 0 : onScreenshot(); onClose(); }, children: "\uD83D\uDCF8 Screenshot" }), _jsx("button", { style: menu.item, onClick: () => { onExportGLB === null || onExportGLB === void 0 ? void 0 : onExportGLB(); onClose(); }, children: "\uD83D\uDCE6 Export GLB" })] }));
235
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { style: Object.assign(Object.assign({}, tree.panel), { width: collapsed ? 'auto' : 224 }), children: [_jsxs("div", { style: base.header, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 6, cursor: 'pointer' }, onClick: () => setCollapsed(!collapsed), children: [_jsx("span", { children: collapsed ? '▶' : '▼' }), _jsx("span", { children: "Scene" })] }), !collapsed && (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 4 }, children: [_jsx("button", { style: Object.assign(Object.assign({}, base.btn), { padding: '2px 6px', fontSize: 10, opacity: canUndo ? 1 : 0.4 }), onClick: (e) => { e.stopPropagation(); onUndo === null || onUndo === void 0 ? void 0 : onUndo(); }, disabled: !canUndo, title: "Undo", children: "\u21B6" }), _jsx("button", { style: Object.assign(Object.assign({}, base.btn), { padding: '2px 6px', fontSize: 10, opacity: canRedo ? 1 : 0.4 }), onClick: (e) => { e.stopPropagation(); onRedo === null || onRedo === void 0 ? void 0 : onRedo(); }, disabled: !canRedo, title: "Redo", children: "\u21B7" }), _jsx(Dropdown, { placement: "bottom-end", trigger: ({ ref, toggle }) => (_jsx(MenuTriggerButton, { buttonRef: ref, onToggle: toggle, title: "Menu", style: Object.assign(Object.assign({}, base.btn), { padding: '2px 6px', fontSize: 10 }), children: "\u22EE" })), children: (close) => (_jsx(FileMenu, { prefabData: prefabData, setPrefabData: setPrefabData, onClose: close })) })] }))] }), !collapsed && (_jsxs(_Fragment, { children: [_jsx("div", { style: { padding: '4px 4px', borderBottom: `1px solid ${colors.borderLight}` }, children: _jsx("input", { type: "text", placeholder: "Search nodes...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), onClick: (e) => e.stopPropagation(), style: Object.assign(Object.assign({}, base.input), { padding: '4px 8px' }) }) }), _jsx("div", { className: "tree-scroll", style: tree.scroll, children: renderNode(prefabData.root) })] }))] }), _jsx(TreeContextMenu, { contextMenu: contextMenu, onClose: closeContextMenu, children: (nodeId, close) => renderTreeNodeMenu(nodeId, nodeId === prefabData.root.id, close) })] }));
260
236
  }
@@ -0,0 +1,33 @@
1
+ import { Dispatch, SetStateAction } from 'react';
2
+ import { Prefab } from './types';
3
+ export type TreeContextMenuState = {
4
+ nodeId: string;
5
+ x: number;
6
+ y: number;
7
+ } | null;
8
+ export declare function MenuTriggerButton({ buttonRef, onToggle, title, style, children, }: {
9
+ buttonRef: React.RefObject<HTMLButtonElement | null>;
10
+ onToggle: () => void;
11
+ title: string;
12
+ style: React.CSSProperties;
13
+ children: React.ReactNode;
14
+ }): import("react/jsx-runtime").JSX.Element;
15
+ export declare function TreeNodeMenu({ isRoot, nodeId, onAddChild, onFocus, onDuplicate, onDelete, onClose, }: {
16
+ isRoot: boolean;
17
+ nodeId: string;
18
+ onAddChild: (parentId: string) => void;
19
+ onFocus: (nodeId: string) => void;
20
+ onDuplicate?: (nodeId: string) => void;
21
+ onDelete?: (nodeId: string) => void;
22
+ onClose: () => void;
23
+ }): import("react/jsx-runtime").JSX.Element;
24
+ export declare function TreeContextMenu({ contextMenu, onClose, children, }: {
25
+ contextMenu: TreeContextMenuState;
26
+ onClose: () => void;
27
+ children: (nodeId: string, onClose: () => void) => React.ReactNode;
28
+ }): import("react").ReactPortal | null;
29
+ export declare function FileMenu({ prefabData, setPrefabData, onClose }: {
30
+ prefabData: Prefab;
31
+ setPrefabData: Dispatch<SetStateAction<Prefab>>;
32
+ onClose: () => void;
33
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,136 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
+ import { useEffect, useRef, useState } from 'react';
12
+ import { createPortal } from 'react-dom';
13
+ import { menu } from './styles';
14
+ import { useEditorContext } from './EditorContext';
15
+ import { getComponent } from './components/ComponentRegistry';
16
+ import { loadJson, saveJson, regenerateIds, updateNodeById } from './utils';
17
+ function createEmptyPrefab() {
18
+ var _a;
19
+ return {
20
+ id: crypto.randomUUID(),
21
+ name: 'New Scene',
22
+ root: {
23
+ id: crypto.randomUUID(),
24
+ name: 'Scene',
25
+ components: {
26
+ transform: {
27
+ type: 'Transform',
28
+ properties: Object.assign({}, (_a = getComponent('Transform')) === null || _a === void 0 ? void 0 : _a.defaultProperties)
29
+ }
30
+ },
31
+ children: []
32
+ }
33
+ };
34
+ }
35
+ function MenuPanel({ children, style, }) {
36
+ return (_jsx("div", { style: Object.assign(Object.assign(Object.assign({}, menu.container), { position: 'static' }), style), onClick: (e) => e.stopPropagation(), children: children }));
37
+ }
38
+ function MenuItemButton({ children, onClick, danger = false, style, }) {
39
+ return (_jsx("button", { style: danger ? Object.assign(Object.assign(Object.assign({}, menu.item), menu.danger), style) : Object.assign(Object.assign({}, menu.item), style), onClick: onClick, children: children }));
40
+ }
41
+ function MenuSubmenu({ label, children, }) {
42
+ const [isOpen, setIsOpen] = useState(false);
43
+ return (_jsxs("div", { style: { position: 'relative' }, onMouseEnter: () => setIsOpen(true), onMouseLeave: () => setIsOpen(false), children: [_jsxs(MenuItemButton, { onClick: () => setIsOpen(open => !open), style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12 }, children: [_jsx("span", { children: label }), _jsx("span", { "aria-hidden": "true", children: "\u203A" })] }), isOpen && (_jsx("div", { style: {
44
+ position: 'absolute',
45
+ top: 0,
46
+ left: '100%',
47
+ zIndex: 1,
48
+ }, children: _jsx(MenuPanel, { children: children }) }))] }));
49
+ }
50
+ export function MenuTriggerButton({ buttonRef, onToggle, title, style, children, }) {
51
+ return (_jsx("button", { ref: buttonRef, style: style, onClick: (e) => {
52
+ e.stopPropagation();
53
+ onToggle();
54
+ }, title: title, children: children }));
55
+ }
56
+ export function TreeNodeMenu({ isRoot, nodeId, onAddChild, onFocus, onDuplicate, onDelete, onClose, }) {
57
+ return (_jsxs(MenuPanel, { children: [_jsx(MenuItemButton, { onClick: () => { onAddChild(nodeId); onClose(); }, children: "Add Child" }), _jsx(MenuItemButton, { onClick: () => { onFocus(nodeId); onClose(); }, children: "Focus Camera" }), !isRoot && onDuplicate && (_jsx(MenuItemButton, { onClick: () => { onDuplicate(nodeId); onClose(); }, children: "Duplicate" })), !isRoot && onDelete && (_jsx(MenuItemButton, { danger: true, onClick: () => { onDelete(nodeId); onClose(); }, children: "Delete" }))] }));
58
+ }
59
+ export function TreeContextMenu({ contextMenu, onClose, children, }) {
60
+ var _a, _b;
61
+ const panelRef = useRef(null);
62
+ const [position, setPosition] = useState(null);
63
+ useEffect(() => {
64
+ if (!contextMenu)
65
+ return;
66
+ const handlePointerDown = (event) => {
67
+ var _a;
68
+ const target = event.target;
69
+ if (!target)
70
+ return;
71
+ if ((_a = panelRef.current) === null || _a === void 0 ? void 0 : _a.contains(target))
72
+ return;
73
+ onClose();
74
+ };
75
+ const handleKeyDown = (event) => {
76
+ if (event.key === 'Escape')
77
+ onClose();
78
+ };
79
+ document.addEventListener('pointerdown', handlePointerDown);
80
+ document.addEventListener('keydown', handleKeyDown);
81
+ return () => {
82
+ document.removeEventListener('pointerdown', handlePointerDown);
83
+ document.removeEventListener('keydown', handleKeyDown);
84
+ };
85
+ }, [contextMenu, onClose]);
86
+ useEffect(() => {
87
+ if (!contextMenu || !panelRef.current || typeof window === 'undefined')
88
+ return;
89
+ const panelRect = panelRef.current.getBoundingClientRect();
90
+ const left = Math.max(8, Math.min(contextMenu.x, window.innerWidth - panelRect.width - 8));
91
+ const top = Math.max(8, Math.min(contextMenu.y, window.innerHeight - panelRect.height - 8));
92
+ setPosition({ left, top });
93
+ }, [contextMenu]);
94
+ useEffect(() => {
95
+ if (!contextMenu) {
96
+ setPosition(null);
97
+ }
98
+ }, [contextMenu]);
99
+ if (!contextMenu || typeof document === 'undefined')
100
+ return null;
101
+ return createPortal(_jsx("div", { ref: panelRef, style: {
102
+ position: 'fixed',
103
+ left: (_a = position === null || position === void 0 ? void 0 : position.left) !== null && _a !== void 0 ? _a : contextMenu.x,
104
+ top: (_b = position === null || position === void 0 ? void 0 : position.top) !== null && _b !== void 0 ? _b : contextMenu.y,
105
+ zIndex: 1000,
106
+ }, onMouseLeave: onClose, onContextMenu: (e) => e.preventDefault(), children: children(contextMenu.nodeId, onClose) }), document.body);
107
+ }
108
+ export function FileMenu({ prefabData, setPrefabData, onClose }) {
109
+ const { onScreenshot, onExportGLB } = useEditorContext();
110
+ const handleNewScene = () => {
111
+ setPrefabData(createEmptyPrefab());
112
+ onClose();
113
+ };
114
+ const handleNewSceneFromPrefab = () => __awaiter(this, void 0, void 0, function* () {
115
+ const loadedPrefab = yield loadJson();
116
+ if (!loadedPrefab)
117
+ return;
118
+ setPrefabData(loadedPrefab);
119
+ onClose();
120
+ });
121
+ const handleSave = () => {
122
+ saveJson(prefabData, 'prefab');
123
+ onClose();
124
+ };
125
+ const handleLoadIntoScene = () => __awaiter(this, void 0, void 0, function* () {
126
+ const loadedPrefab = yield loadJson();
127
+ if (!loadedPrefab)
128
+ return;
129
+ setPrefabData(prev => (Object.assign(Object.assign({}, prev), { root: updateNodeById(prev.root, prev.root.id, root => {
130
+ var _a;
131
+ return (Object.assign(Object.assign({}, root), { children: [...((_a = root.children) !== null && _a !== void 0 ? _a : []), regenerateIds(loadedPrefab.root)] }));
132
+ }) })));
133
+ onClose();
134
+ });
135
+ return (_jsxs(MenuPanel, { style: { overflow: 'visible' }, children: [_jsxs(MenuSubmenu, { label: "File", children: [_jsx(MenuItemButton, { onClick: handleNewScene, children: "New Scene" }), _jsx(MenuItemButton, { onClick: handleNewSceneFromPrefab, children: "New Scene from Prefab" }), _jsx(MenuItemButton, { onClick: handleLoadIntoScene, children: "Load Prefab into Scene" }), _jsx(MenuItemButton, { onClick: handleSave, children: "Save Prefab" })] }), _jsxs(MenuSubmenu, { label: "Export", children: [_jsx(MenuItemButton, { onClick: () => { onExportGLB === null || onExportGLB === void 0 ? void 0 : onExportGLB(); onClose(); }, children: "GLB" }), _jsx(MenuItemButton, { onClick: () => { onScreenshot === null || onScreenshot === void 0 ? void 0 : onScreenshot(); onClose(); }, children: "PNG" })] })] }));
136
+ }
@@ -7,7 +7,7 @@ import EditorUI from "./EditorUI";
7
7
  import { base, toolbar } from "./styles";
8
8
  import { EditorContext } from "./EditorContext";
9
9
  import { exportGLB, createModelNode, createImageNode } from "./utils";
10
- import { parseModelFromFile } from "../dragdrop/modelLoader";
10
+ import { parseModelFromFile } from "../dragdrop";
11
11
  const DEFAULT_PREFAB = {
12
12
  id: "prefab-default",
13
13
  name: "New Prefab",
@@ -13,7 +13,7 @@ import { forwardRef, useCallback, useContext, useEffect, useImperativeHandle, us
13
13
  import { BoxHelper, Euler, Matrix4, Quaternion, SRGBColorSpace, TextureLoader, Vector3, } from "three";
14
14
  import { getComponent, registerComponent, getNonComposableKeys } from "./components/ComponentRegistry";
15
15
  import components from "./components";
16
- import { loadModel } from "../dragdrop/modelLoader";
16
+ import { loadModel } from "../dragdrop";
17
17
  import { GameInstance, GameInstanceProvider, useInstanceCheck } from "./InstanceProvider";
18
18
  import { focusCameraOnObject, updateNode } from "./utils";
19
19
  import { EditorContext } from "./EditorContext";
@@ -120,8 +120,10 @@ export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selected
120
120
  ? `${basePath}${file}`
121
121
  : `${basePath}/${file}`;
122
122
  const res = yield loadModel(path);
123
- res.success && res.model &&
124
- setModels(m => (Object.assign(Object.assign({}, m), { [file]: res.model })));
123
+ const model = res.model;
124
+ if (res.success && model) {
125
+ setModels(m => (Object.assign(Object.assign({}, m), { [file]: model })));
126
+ }
125
127
  }));
126
128
  const loader = new TextureLoader();
127
129
  texturesToLoad.forEach(file => {
@@ -1,25 +1,45 @@
1
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';
2
+ import { PerspectiveCamera } from '@react-three/drei';
3
+ import { useEffect, useMemo, useState } from 'react';
4
4
  import { CameraHelper } from 'three';
5
+ import { useFrame } from '@react-three/fiber';
5
6
  import { FieldGroup, NumberField } from './Input';
7
+ const cameraDefaults = {
8
+ fov: 50,
9
+ near: 0.1,
10
+ zoom: 1,
11
+ far: 1000,
12
+ };
6
13
  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 })] }));
14
+ const values = Object.assign(Object.assign({}, cameraDefaults), component.properties);
15
+ return (_jsxs(FieldGroup, { children: [_jsx(NumberField, { name: "fov", label: "FOV", values: values, onChange: onUpdate, fallback: 50, min: 1, max: 179, step: 1 }), _jsx(NumberField, { name: "near", label: "Near", values: values, onChange: onUpdate, fallback: 0.1, min: 0.001, step: 0.1 }), _jsx(NumberField, { name: "zoom", label: "Zoom", values: values, onChange: onUpdate, fallback: 1, min: 0.01, step: 0.1 }), _jsx(NumberField, { name: "far", label: "Far", values: values, onChange: onUpdate, fallback: 1000, min: 0.1, step: 1 })] }));
8
16
  }
9
17
  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
+ const merged = Object.assign(Object.assign({}, cameraDefaults), properties);
19
+ const fov = merged.fov;
20
+ const near = merged.near;
21
+ const zoom = merged.zoom;
22
+ const far = merged.far;
23
+ const [camera, setCamera] = useState(null);
24
+ const cameraHelper = useMemo(() => camera ? new CameraHelper(camera) : null, [camera]);
25
+ useEffect(() => {
26
+ return () => {
27
+ cameraHelper === null || cameraHelper === void 0 ? void 0 : cameraHelper.dispose();
28
+ };
29
+ }, [cameraHelper]);
30
+ useFrame(() => {
31
+ if (camera && cameraHelper && editMode && isSelected) {
32
+ camera.updateProjectionMatrix();
33
+ camera.updateMatrixWorld();
34
+ cameraHelper.update();
35
+ }
36
+ });
37
+ return (_jsxs(_Fragment, { children: [_jsx(PerspectiveCamera, { ref: (instance) => setCamera(instance), makeDefault: !editMode, fov: fov, near: near, zoom: zoom, far: far }), editMode && isSelected && cameraHelper && (_jsx("primitive", { object: cameraHelper })), editMode && !isSelected ? (_jsxs("mesh", { children: [_jsx("boxGeometry", { args: [0.34, 0.22, 0.18] }), _jsx("meshBasicMaterial", { color: "#22d3ee", wireframe: true })] })) : null] }));
18
38
  }
19
39
  const CameraComponent = {
20
40
  name: 'Camera',
21
41
  Editor: CameraComponentEditor,
22
42
  View: CameraComponentView,
23
- defaultProperties: {},
43
+ defaultProperties: cameraDefaults,
24
44
  };
25
45
  export default CameraComponent;
@@ -1,9 +1,22 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useRef, useEffect } from "react";
2
+ import { useRef, useEffect, useMemo, useState } from "react";
3
3
  import { useFrame } from "@react-three/fiber";
4
- import { Vector3 } from "three";
4
+ import { CameraHelper, Vector3 } from "three";
5
5
  import { FieldRenderer, Input } from "./Input";
6
6
  const smallLabel = { display: 'block', fontSize: '8px', color: 'rgba(34, 211, 238, 0.5)', marginBottom: 2 };
7
+ const directionalLightDefaults = {
8
+ color: '#ffffff',
9
+ intensity: 1,
10
+ castShadow: true,
11
+ shadowMapSize: 1024,
12
+ shadowCameraNear: 0.1,
13
+ shadowCameraFar: 100,
14
+ shadowCameraTop: 30,
15
+ shadowCameraBottom: -30,
16
+ shadowCameraLeft: -30,
17
+ shadowCameraRight: 30,
18
+ targetOffset: [0, -5, 0],
19
+ };
7
20
  const directionalLightFields = [
8
21
  { name: 'color', type: 'color', label: 'Color' },
9
22
  { name: 'intensity', type: 'number', label: 'Intensity', step: 0.1, min: 0 },
@@ -29,39 +42,52 @@ const directionalLightFields = [
29
42
  },
30
43
  ];
31
44
  function DirectionalLightComponentEditor({ component, onUpdate }) {
32
- return (_jsx(FieldRenderer, { fields: directionalLightFields, values: component.properties, onChange: onUpdate }));
45
+ const values = Object.assign(Object.assign({}, directionalLightDefaults), component.properties);
46
+ const fields = values.castShadow
47
+ ? directionalLightFields
48
+ : directionalLightFields.filter(field => field.name !== '_shadowCamera');
49
+ return (_jsx(FieldRenderer, { fields: fields, values: values, onChange: onUpdate }));
33
50
  }
34
51
  function DirectionalLightView({ properties, editMode, isSelected }) {
35
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
36
- const color = (_a = properties.color) !== null && _a !== void 0 ? _a : '#ffffff';
37
- const intensity = (_b = properties.intensity) !== null && _b !== void 0 ? _b : 1.0;
38
- const castShadow = (_c = properties.castShadow) !== null && _c !== void 0 ? _c : true;
39
- const shadowMapSize = (_d = properties.shadowMapSize) !== null && _d !== void 0 ? _d : 1024;
40
- const shadowCameraNear = (_e = properties.shadowCameraNear) !== null && _e !== void 0 ? _e : 0.1;
41
- const shadowCameraFar = (_f = properties.shadowCameraFar) !== null && _f !== void 0 ? _f : 100;
42
- const shadowCameraTop = (_g = properties.shadowCameraTop) !== null && _g !== void 0 ? _g : 30;
43
- const shadowCameraBottom = (_h = properties.shadowCameraBottom) !== null && _h !== void 0 ? _h : -30;
44
- const shadowCameraLeft = (_j = properties.shadowCameraLeft) !== null && _j !== void 0 ? _j : -30;
45
- const shadowCameraRight = (_k = properties.shadowCameraRight) !== null && _k !== void 0 ? _k : 30;
46
- const targetOffset = (_l = properties.targetOffset) !== null && _l !== void 0 ? _l : [0, -5, 0];
52
+ const merged = Object.assign(Object.assign({}, directionalLightDefaults), properties);
53
+ const color = merged.color;
54
+ const intensity = merged.intensity;
55
+ const castShadow = merged.castShadow;
56
+ const shadowMapSize = merged.shadowMapSize;
57
+ const shadowCameraNear = merged.shadowCameraNear;
58
+ const shadowCameraFar = merged.shadowCameraFar;
59
+ const shadowCameraTop = merged.shadowCameraTop;
60
+ const shadowCameraBottom = merged.shadowCameraBottom;
61
+ const shadowCameraLeft = merged.shadowCameraLeft;
62
+ const shadowCameraRight = merged.shadowCameraRight;
63
+ const targetOffset = merged.targetOffset;
47
64
  const directionalLightRef = useRef(null);
48
65
  const targetRef = useRef(null);
49
- // Set up light target reference when both refs are ready
66
+ const [shadowCamera, setShadowCamera] = useState(null);
67
+ const shadowCameraHelper = useMemo(() => shadowCamera ? new CameraHelper(shadowCamera) : null, [shadowCamera]);
68
+ useEffect(() => {
69
+ return () => {
70
+ shadowCameraHelper === null || shadowCameraHelper === void 0 ? void 0 : shadowCameraHelper.dispose();
71
+ };
72
+ }, [shadowCameraHelper]);
73
+ // Use a local target object so node transforms rotate the light direction naturally.
50
74
  useEffect(() => {
51
75
  if (directionalLightRef.current && targetRef.current) {
52
76
  directionalLightRef.current.target = targetRef.current;
77
+ setShadowCamera(directionalLightRef.current.shadow.camera);
53
78
  }
54
79
  }, []);
55
- // Update target world position based on light position + offset
56
80
  useFrame(() => {
57
81
  if (!directionalLightRef.current || !targetRef.current)
58
82
  return;
59
- const lightWorldPos = new Vector3();
60
- directionalLightRef.current.getWorldPosition(lightWorldPos);
61
- // Target is positioned relative to light's world position
62
- targetRef.current.position.set(lightWorldPos.x + targetOffset[0], lightWorldPos.y + targetOffset[1], lightWorldPos.z + targetOffset[2]);
83
+ directionalLightRef.current.target.updateMatrixWorld();
84
+ if (shadowCamera && shadowCameraHelper && castShadow) {
85
+ shadowCamera.updateProjectionMatrix();
86
+ shadowCamera.updateMatrixWorld();
87
+ shadowCameraHelper.update();
88
+ }
63
89
  });
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) => {
90
+ 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, position: targetOffset }), editMode && isSelected && castShadow && shadowCameraHelper && (_jsx("primitive", { object: shadowCameraHelper })), 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
91
  const points = [
66
92
  new Vector3(0, 0, 0),
67
93
  new Vector3(targetOffset[0], targetOffset[1], targetOffset[2])
@@ -73,6 +99,6 @@ const DirectionalLightComponent = {
73
99
  name: 'DirectionalLight',
74
100
  Editor: DirectionalLightComponentEditor,
75
101
  View: DirectionalLightView,
76
- defaultProperties: {}
102
+ defaultProperties: directionalLightDefaults
77
103
  };
78
104
  export default DirectionalLightComponent;
@@ -1,5 +1,13 @@
1
+ import type { ThreeElement } from '@react-three/fiber';
1
2
  import { Component } from './ComponentRegistry';
3
+ import { MeshBasicNodeMaterial, MeshStandardNodeMaterial } from 'three/webgpu';
2
4
  import { MeshBasicMaterialProperties, MeshStandardMaterialProperties } from 'three';
5
+ declare module '@react-three/fiber' {
6
+ interface ThreeElements {
7
+ meshBasicNodeMaterial: ThreeElement<typeof MeshBasicNodeMaterial>;
8
+ meshStandardNodeMaterial: ThreeElement<typeof MeshStandardNodeMaterial>;
9
+ }
10
+ }
3
11
  export interface MaterialProps extends Omit<MeshStandardMaterialProperties & MeshBasicMaterialProperties, 'args' | 'normalScale'> {
4
12
  materialType?: 'standard' | 'basic';
5
13
  transmission?: number;
@@ -11,14 +11,20 @@ var __rest = (this && this.__rest) || function (s, e) {
11
11
  };
12
12
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
13
  import { SingleTextureViewer, TextureListViewer } from '../../assetviewer/page';
14
+ import { extend } from '@react-three/fiber';
14
15
  import { useEffect, useLayoutEffect, useRef, useState } from 'react';
15
16
  import { createPortal } from 'react-dom';
16
17
  import { FieldRenderer, Input } from './Input';
17
18
  import { colors } from '../styles';
18
19
  import { useMemo } from 'react';
20
+ import { MeshBasicNodeMaterial, MeshStandardNodeMaterial } from 'three/webgpu';
19
21
  import { RepeatWrapping, ClampToEdgeWrapping, SRGBColorSpace, LinearSRGBColorSpace, Vector2, NearestFilter, LinearFilter, NearestMipmapNearestFilter, NearestMipmapLinearFilter, LinearMipmapNearestFilter, LinearMipmapLinearFilter, FrontSide, BackSide, DoubleSide, } from 'three';
20
22
  const PICKER_POPUP_WIDTH = 260;
21
23
  const PICKER_POPUP_HEIGHT = 360;
24
+ extend({
25
+ MeshBasicNodeMaterial,
26
+ MeshStandardNodeMaterial,
27
+ });
22
28
  function TexturePicker({ value, onChange, basePath }) {
23
29
  const [textureFiles, setTextureFiles] = useState([]);
24
30
  const [showPicker, setShowPicker] = useState(false);
@@ -193,7 +199,7 @@ function MaterialComponentView({ properties, loadedTextures }) {
193
199
  const normalMapTexture = normalMapTextureName && loadedTextures ? loadedTextures[normalMapTextureName] : undefined;
194
200
  const materialSource = properties !== null && properties !== void 0 ? properties : {};
195
201
  // Destructure all material props and separate custom texture handling props
196
- const { texture: _texture, repeat: _repeat, repeatCount: _repeatCount, generateMipmaps: _generateMipmaps, minFilter: _minFilter, magFilter: _magFilter, map: _map, materialType: _materialType, normalMapTexture: _normalMapTexture, normalScale: _normalScale, normalMap: _normalMap, side: sideProp, metalness: _metalness, roughness: _roughness, transmission: _transmission, thickness: _thickness, ior: _ior } = materialSource, materialProps = __rest(materialSource, ["texture", "repeat", "repeatCount", "generateMipmaps", "minFilter", "magFilter", "map", "materialType", "normalMapTexture", "normalScale", "normalMap", "side", "metalness", "roughness", "transmission", "thickness", "ior"]);
202
+ const { texture: _texture, repeat: _repeat, repeatCount: _repeatCount, generateMipmaps: _generateMipmaps, minFilter: _minFilter, magFilter: _magFilter, map: _map, materialType: _materialType, normalMapTexture: _normalMapTexture, normalScale: _normalScale, normalMap: _normalMap, side: sideProp } = materialSource, materialProps = __rest(materialSource, ["texture", "repeat", "repeatCount", "generateMipmaps", "minFilter", "magFilter", "map", "materialType", "normalMapTexture", "normalScale", "normalMap", "side"]);
197
203
  const sideMap = { FrontSide, BackSide, DoubleSide };
198
204
  const resolvedSide = sideProp ? ((_b = sideMap[sideProp]) !== null && _b !== void 0 ? _b : FrontSide) : FrontSide;
199
205
  const minFilterMap = {
@@ -244,14 +250,14 @@ function MaterialComponentView({ properties, loadedTextures }) {
244
250
  return new Vector2((_a = normalScaleProp === null || normalScaleProp === void 0 ? void 0 : normalScaleProp[0]) !== null && _a !== void 0 ? _a : 1, (_b = normalScaleProp === null || normalScaleProp === void 0 ? void 0 : normalScaleProp[1]) !== null && _b !== void 0 ? _b : 1);
245
251
  }, [finalNormalMap, normalScaleProp === null || normalScaleProp === void 0 ? void 0 : normalScaleProp[0], normalScaleProp === null || normalScaleProp === void 0 ? void 0 : normalScaleProp[1]]);
246
252
  if (!properties) {
247
- return _jsx("meshStandardMaterial", { color: "red", wireframe: true });
253
+ return _jsx("meshStandardNodeMaterial", { color: "red", wireframe: true });
248
254
  }
249
- const materialKey = (_c = finalTexture === null || finalTexture === void 0 ? void 0 : finalTexture.uuid) !== null && _c !== void 0 ? _c : 'no-texture';
255
+ const materialKey = `${(_c = finalTexture === null || finalTexture === void 0 ? void 0 : finalTexture.uuid) !== null && _c !== void 0 ? _c : 'no-texture'}:${materialProps.transparent ? 'transparent' : 'opaque'}`;
250
256
  const sharedProps = Object.assign({ map: finalTexture, side: resolvedSide }, materialProps);
251
257
  if (materialType === 'basic') {
252
- return _jsx("meshBasicMaterial", Object.assign({}, sharedProps), materialKey);
258
+ return _jsx("meshBasicNodeMaterial", Object.assign({}, sharedProps), materialKey);
253
259
  }
254
- return (_jsx("meshStandardMaterial", Object.assign({}, sharedProps, { normalMap: finalNormalMap, normalScale: normalScaleVec }), materialKey));
260
+ return (_jsx("meshStandardNodeMaterial", Object.assign({}, sharedProps, { normalMap: finalNormalMap, normalScale: normalScaleVec }), materialKey));
255
261
  }
256
262
  const MaterialComponent = {
257
263
  name: 'Material',