react-three-game 0.0.67 → 0.0.69
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.
- package/README.md +109 -304
- package/dist/index.d.ts +15 -8
- package/dist/index.js +11 -8
- package/dist/shared/GameCanvas.d.ts +1 -2
- package/dist/tools/prefabeditor/EditorContext.d.ts +2 -2
- package/dist/tools/prefabeditor/EditorTree.d.ts +6 -6
- package/dist/tools/prefabeditor/EditorTree.js +92 -142
- package/dist/tools/prefabeditor/EditorTreeMenus.d.ts +4 -11
- package/dist/tools/prefabeditor/EditorTreeMenus.js +16 -25
- package/dist/tools/prefabeditor/EditorUI.d.ts +5 -5
- package/dist/tools/prefabeditor/EditorUI.js +14 -11
- package/dist/tools/prefabeditor/GameEvents.d.ts +0 -30
- package/dist/tools/prefabeditor/GameEvents.js +0 -7
- package/dist/tools/prefabeditor/PrefabEditor.d.ts +12 -13
- package/dist/tools/prefabeditor/PrefabEditor.js +168 -138
- package/dist/tools/prefabeditor/PrefabRoot.d.ts +8 -5
- package/dist/tools/prefabeditor/PrefabRoot.js +141 -123
- package/dist/tools/prefabeditor/components/AmbientLightComponent.js +3 -3
- package/dist/tools/prefabeditor/components/CameraComponent.js +2 -2
- package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +2 -2
- package/dist/tools/prefabeditor/components/ModelComponent.js +0 -1
- package/dist/tools/prefabeditor/components/SpotLightComponent.js +2 -2
- package/dist/tools/prefabeditor/components/TextComponent.js +2 -3
- package/dist/tools/prefabeditor/components/TransformComponent.js +9 -14
- package/dist/tools/prefabeditor/prefabStore.d.ts +42 -0
- package/dist/tools/prefabeditor/prefabStore.js +347 -0
- package/dist/tools/prefabeditor/sceneApi.d.ts +44 -0
- package/dist/tools/prefabeditor/sceneApi.js +161 -0
- package/dist/tools/prefabeditor/styles.d.ts +2 -1
- package/dist/tools/prefabeditor/styles.js +2 -12
- package/dist/tools/prefabeditor/utils.d.ts +15 -36
- package/dist/tools/prefabeditor/utils.js +36 -162
- package/package.json +4 -3
- package/dist/tools/prefabeditor/EventSystem.d.ts +0 -7
- package/dist/tools/prefabeditor/EventSystem.js +0 -23
|
@@ -10,14 +10,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
11
11
|
import { MapControls, TransformControls } from "@react-three/drei";
|
|
12
12
|
import GameCanvas from "../../shared/GameCanvas";
|
|
13
|
-
import {
|
|
13
|
+
import { useCallback, useEffect, useMemo, useRef, useState, forwardRef, useImperativeHandle } from "react";
|
|
14
14
|
import PrefabRoot from "./PrefabRoot";
|
|
15
15
|
import { Physics } from "@react-three/rapier";
|
|
16
16
|
import EditorUI from "./EditorUI";
|
|
17
17
|
import { base, toolbar } from "./styles";
|
|
18
18
|
import { EditorContext } from "./EditorContext";
|
|
19
|
-
import {
|
|
19
|
+
import { createImageNode, createModelNode, computeParentWorldMatrix, decompose, exportGLB as exportGLBFile, exportGLBData, focusCameraOnObject, regenerateIds } from "./utils";
|
|
20
20
|
import { loadFiles } from "../dragdrop";
|
|
21
|
+
import { createPrefabStore, PrefabStoreProvider, prefabStoreToPrefab } from "./prefabStore";
|
|
22
|
+
import { createScene } from "./sceneApi";
|
|
21
23
|
const DEFAULT_PREFAB = {
|
|
22
24
|
id: "prefab-default",
|
|
23
25
|
name: "New Prefab",
|
|
@@ -31,36 +33,35 @@ const DEFAULT_PREFAB = {
|
|
|
31
33
|
}
|
|
32
34
|
}
|
|
33
35
|
};
|
|
34
|
-
const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true,
|
|
36
|
+
const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, onChange, showUI = true, enableWindowDrop = true, canvasProps, uiPlugins, children }, ref) => {
|
|
35
37
|
const [editMode, setEditMode] = useState(true);
|
|
36
|
-
const [loadedPrefab, setLoadedPrefab] = useState(initialPrefab !== null && initialPrefab !== void 0 ? initialPrefab : DEFAULT_PREFAB);
|
|
37
38
|
const [selectedId, setSelectedId] = useState(null);
|
|
38
39
|
const [transformMode, setTransformMode] = useState("translate");
|
|
39
|
-
const [
|
|
40
|
+
const [scaleSnap, setScaleSnap] = useState(0);
|
|
40
41
|
const [positionSnap, setPositionSnap] = useState(0.5);
|
|
41
42
|
const [rotationSnap, setRotationSnap] = useState(Math.PI / 4);
|
|
42
|
-
const
|
|
43
|
+
const startingPrefab = initialPrefab !== null && initialPrefab !== void 0 ? initialPrefab : DEFAULT_PREFAB;
|
|
44
|
+
const [prefabStore] = useState(() => createPrefabStore(startingPrefab));
|
|
45
|
+
const [history, setHistory] = useState([startingPrefab]);
|
|
43
46
|
const [historyIndex, setHistoryIndex] = useState(0);
|
|
47
|
+
const changeOriginRef = useRef(null);
|
|
48
|
+
const historyIndexRef = useRef(0);
|
|
44
49
|
const [selectedObject, setSelectedObject] = useState(null);
|
|
45
|
-
const throttleRef = useRef(null);
|
|
46
|
-
const lastDataRef = useRef(JSON.stringify(loadedPrefab));
|
|
47
50
|
const prefabRootRef = useRef(null);
|
|
48
51
|
const canvasRef = useRef(null);
|
|
49
52
|
const controlsRef = useRef(null);
|
|
50
|
-
const
|
|
51
|
-
const pendingPrefabChangeRef = useRef(null);
|
|
53
|
+
const onChangeRef = useRef(onChange);
|
|
52
54
|
const [injectedModels, setInjectedModels] = useState({});
|
|
53
55
|
const [injectedTextures, setInjectedTextures] = useState({});
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const nextNode = nodeId ?
|
|
56
|
+
const getPrefab = useCallback(() => prefabStoreToPrefab(prefabStore.getState()), [prefabStore]);
|
|
57
|
+
onChangeRef.current = onChange;
|
|
58
|
+
const setSelection = useCallback((nodeId) => {
|
|
59
|
+
const nextNode = nodeId ? prefabStore.getState().nodesById[nodeId] : null;
|
|
58
60
|
if (nextNode === null || nextNode === void 0 ? void 0 : nextNode.locked) {
|
|
59
61
|
return;
|
|
60
62
|
}
|
|
61
63
|
setSelectedId(nodeId);
|
|
62
|
-
|
|
63
|
-
};
|
|
64
|
+
}, [prefabStore]);
|
|
64
65
|
const toggleEditMode = () => {
|
|
65
66
|
setEditMode(prev => {
|
|
66
67
|
const next = !prev;
|
|
@@ -71,75 +72,117 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, onPr
|
|
|
71
72
|
return next;
|
|
72
73
|
});
|
|
73
74
|
};
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
setHistory([prefab]);
|
|
86
|
-
setHistoryIndex(0);
|
|
87
|
-
setLoadedPrefab(prefab);
|
|
88
|
-
};
|
|
89
|
-
const setPrefab = (prefab) => {
|
|
90
|
-
if (selectedId && !findNode(prefab.root, selectedId)) {
|
|
91
|
-
setSelection(null);
|
|
75
|
+
const loadPrefab = useCallback((prefab, options) => {
|
|
76
|
+
changeOriginRef.current = (options === null || options === void 0 ? void 0 : options.notifyChange) === false ? "replace-silent" : "replace";
|
|
77
|
+
prefabStore.getState().replacePrefab(prefab);
|
|
78
|
+
setSelectedObject(null);
|
|
79
|
+
if (options === null || options === void 0 ? void 0 : options.resetHistory) {
|
|
80
|
+
setSelectedId(null);
|
|
81
|
+
setInjectedModels({});
|
|
82
|
+
setInjectedTextures({});
|
|
83
|
+
setHistory([prefab]);
|
|
84
|
+
historyIndexRef.current = 0;
|
|
85
|
+
setHistoryIndex(0);
|
|
92
86
|
}
|
|
93
|
-
|
|
94
|
-
|
|
87
|
+
else {
|
|
88
|
+
setSelectedId(prev => prev && prefabStore.getState().nodesById[prev] ? prev : null);
|
|
89
|
+
}
|
|
90
|
+
}, [prefabStore]);
|
|
95
91
|
useEffect(() => {
|
|
96
92
|
if (initialPrefab)
|
|
97
|
-
|
|
98
|
-
}, [initialPrefab]);
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
93
|
+
loadPrefab(initialPrefab, { resetHistory: true, notifyChange: false });
|
|
94
|
+
}, [initialPrefab, loadPrefab]);
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
let historyTimeout = null;
|
|
97
|
+
let lastRevision = prefabStore.getState().revision;
|
|
98
|
+
const unsubscribe = prefabStore.subscribe((state) => {
|
|
99
|
+
var _a;
|
|
100
|
+
if (state.revision === lastRevision) {
|
|
101
|
+
return;
|
|
105
102
|
}
|
|
106
|
-
|
|
107
|
-
|
|
103
|
+
lastRevision = state.revision;
|
|
104
|
+
const nextPrefab = prefabStoreToPrefab(state);
|
|
105
|
+
const changeOrigin = changeOriginRef.current;
|
|
106
|
+
if (changeOrigin !== "replace-silent") {
|
|
107
|
+
(_a = onChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onChangeRef, nextPrefab);
|
|
108
|
+
}
|
|
109
|
+
if (historyTimeout) {
|
|
110
|
+
clearTimeout(historyTimeout);
|
|
111
|
+
historyTimeout = null;
|
|
112
|
+
}
|
|
113
|
+
if (changeOrigin || !editMode) {
|
|
114
|
+
changeOriginRef.current = null;
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
historyTimeout = setTimeout(() => {
|
|
118
|
+
const currentHistoryIndex = historyIndexRef.current;
|
|
119
|
+
setHistory(prev => {
|
|
120
|
+
const nextHistory = [...prev.slice(0, currentHistoryIndex + 1), nextPrefab];
|
|
121
|
+
return nextHistory.length > 50 ? nextHistory.slice(1) : nextHistory;
|
|
122
|
+
});
|
|
123
|
+
const nextHistoryIndex = Math.min(currentHistoryIndex + 1, 49);
|
|
124
|
+
historyIndexRef.current = nextHistoryIndex;
|
|
125
|
+
setHistoryIndex(nextHistoryIndex);
|
|
126
|
+
historyTimeout = null;
|
|
127
|
+
}, 500);
|
|
108
128
|
});
|
|
109
|
-
|
|
129
|
+
return () => {
|
|
130
|
+
if (historyTimeout) {
|
|
131
|
+
clearTimeout(historyTimeout);
|
|
132
|
+
}
|
|
133
|
+
unsubscribe();
|
|
134
|
+
};
|
|
135
|
+
}, [editMode, prefabStore]);
|
|
110
136
|
useEffect(() => {
|
|
111
|
-
|
|
112
|
-
if (pendingPrefabChangeRef.current !== loadedPrefab)
|
|
137
|
+
if (!selectedId)
|
|
113
138
|
return;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
return Object.assign(Object.assign({}, prev), { root: insertNode(prev.root, node, options === null || options === void 0 ? void 0 : options.parentId) });
|
|
139
|
+
const unsubscribe = prefabStore.subscribe((state) => {
|
|
140
|
+
if (state.nodesById[selectedId])
|
|
141
|
+
return;
|
|
142
|
+
setSelectedId(null);
|
|
143
|
+
setSelectedObject(null);
|
|
120
144
|
});
|
|
145
|
+
return () => unsubscribe();
|
|
146
|
+
}, [prefabStore, selectedId]);
|
|
147
|
+
useEffect(() => {
|
|
148
|
+
var _a, _b;
|
|
149
|
+
if (!selectedId) {
|
|
150
|
+
setSelectedObject(null);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
setSelectedObject((_b = (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.getObject(selectedId)) !== null && _b !== void 0 ? _b : null);
|
|
154
|
+
}, [selectedId]);
|
|
155
|
+
const addNode = useCallback((node, options) => {
|
|
156
|
+
var _a;
|
|
157
|
+
const { addChild, rootId } = prefabStore.getState();
|
|
158
|
+
addChild((_a = options === null || options === void 0 ? void 0 : options.parentId) !== null && _a !== void 0 ? _a : rootId, node);
|
|
121
159
|
if ((options === null || options === void 0 ? void 0 : options.select) !== false) {
|
|
122
160
|
setSelection(node.id);
|
|
123
161
|
}
|
|
124
162
|
return node;
|
|
125
|
-
};
|
|
126
|
-
const
|
|
163
|
+
}, [prefabStore, setSelection]);
|
|
164
|
+
const importPrefab = useCallback((prefab) => {
|
|
165
|
+
addNode(regenerateIds(prefab.root), { select: false });
|
|
166
|
+
}, [addNode]);
|
|
167
|
+
const addModel = useCallback((path, model, options) => {
|
|
127
168
|
const node = createModelNode(path, options === null || options === void 0 ? void 0 : options.name);
|
|
128
|
-
|
|
169
|
+
addNode(node, options);
|
|
129
170
|
setInjectedModels(prev => (Object.assign(Object.assign({}, prev), { [path]: model })));
|
|
130
171
|
return node;
|
|
131
|
-
};
|
|
132
|
-
const addTexture = (path, texture, options) => {
|
|
172
|
+
}, [addNode]);
|
|
173
|
+
const addTexture = useCallback((path, texture, options) => {
|
|
133
174
|
const node = createImageNode(path, options === null || options === void 0 ? void 0 : options.name);
|
|
134
|
-
|
|
175
|
+
addNode(node, options);
|
|
135
176
|
setInjectedTextures(prev => (Object.assign(Object.assign({}, prev), { [path]: texture })));
|
|
136
177
|
return node;
|
|
137
|
-
};
|
|
178
|
+
}, [addNode]);
|
|
138
179
|
const applyHistory = (index) => {
|
|
180
|
+
changeOriginRef.current = "history";
|
|
181
|
+
prefabStore.getState().replacePrefab(history[index]);
|
|
182
|
+
historyIndexRef.current = index;
|
|
139
183
|
setHistoryIndex(index);
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
setLoadedPrefab(history[index]);
|
|
184
|
+
setSelectedObject(null);
|
|
185
|
+
setSelectedId(prev => prev && prefabStore.getState().nodesById[prev] ? prev : null);
|
|
143
186
|
};
|
|
144
187
|
const undo = () => historyIndex > 0 && applyHistory(historyIndex - 1);
|
|
145
188
|
const redo = () => historyIndex < history.length - 1 && applyHistory(historyIndex + 1);
|
|
@@ -161,24 +204,7 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, onPr
|
|
|
161
204
|
window.addEventListener('keydown', handleKeyDown);
|
|
162
205
|
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
163
206
|
}, [editMode, historyIndex, history]);
|
|
164
|
-
|
|
165
|
-
const currentStr = JSON.stringify(loadedPrefab);
|
|
166
|
-
if (currentStr === lastDataRef.current)
|
|
167
|
-
return;
|
|
168
|
-
if (throttleRef.current)
|
|
169
|
-
clearTimeout(throttleRef.current);
|
|
170
|
-
throttleRef.current = setTimeout(() => {
|
|
171
|
-
lastDataRef.current = currentStr;
|
|
172
|
-
setHistory(prev => {
|
|
173
|
-
const newHistory = [...prev.slice(0, historyIndex + 1), loadedPrefab];
|
|
174
|
-
return newHistory.length > 50 ? newHistory.slice(1) : newHistory;
|
|
175
|
-
});
|
|
176
|
-
setHistoryIndex(prev => Math.min(prev + 1, 49));
|
|
177
|
-
}, 500);
|
|
178
|
-
return () => { if (throttleRef.current)
|
|
179
|
-
clearTimeout(throttleRef.current); };
|
|
180
|
-
}, [loadedPrefab]);
|
|
181
|
-
const handleScreenshot = () => {
|
|
207
|
+
const handleScreenshot = useCallback(() => {
|
|
182
208
|
const canvas = canvasRef.current;
|
|
183
209
|
if (!canvas)
|
|
184
210
|
return;
|
|
@@ -188,38 +214,34 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, onPr
|
|
|
188
214
|
const url = URL.createObjectURL(blob);
|
|
189
215
|
const a = document.createElement('a');
|
|
190
216
|
a.href = url;
|
|
191
|
-
a.download = `${
|
|
217
|
+
a.download = `${prefabStore.getState().prefabName || 'screenshot'}.png`;
|
|
192
218
|
a.click();
|
|
193
219
|
URL.revokeObjectURL(url);
|
|
194
220
|
});
|
|
195
|
-
};
|
|
196
|
-
const clearSelection = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
197
|
-
if (!selectedId)
|
|
198
|
-
return;
|
|
221
|
+
}, [prefabStore]);
|
|
222
|
+
const clearSelection = useCallback(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
199
223
|
setSelection(null);
|
|
200
224
|
yield new Promise(resolve => {
|
|
201
|
-
requestAnimationFrame(() =>
|
|
202
|
-
requestAnimationFrame(() => resolve());
|
|
203
|
-
});
|
|
225
|
+
requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
|
|
204
226
|
});
|
|
205
|
-
});
|
|
206
|
-
const handleExportGLB = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (options = {}) {
|
|
227
|
+
}), [setSelection]);
|
|
228
|
+
const handleExportGLB = useCallback((...args_1) => __awaiter(void 0, [...args_1], void 0, function* (options = {}) {
|
|
207
229
|
var _a;
|
|
208
230
|
yield clearSelection();
|
|
209
|
-
const
|
|
210
|
-
if (!
|
|
231
|
+
const rootObject = (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.root;
|
|
232
|
+
if (!rootObject)
|
|
211
233
|
return;
|
|
212
|
-
return
|
|
213
|
-
});
|
|
214
|
-
const handleExportGLBData = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
234
|
+
return exportGLBFile(rootObject, Object.assign({ filename: `${prefabStore.getState().prefabName || 'prefab'}.glb` }, options));
|
|
235
|
+
}), [clearSelection, prefabStore]);
|
|
236
|
+
const handleExportGLBData = useCallback(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
215
237
|
var _a;
|
|
216
238
|
yield clearSelection();
|
|
217
|
-
const
|
|
218
|
-
if (!
|
|
239
|
+
const rootObject = (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.root;
|
|
240
|
+
if (!rootObject)
|
|
219
241
|
return;
|
|
220
|
-
return exportGLBData(
|
|
221
|
-
});
|
|
222
|
-
const handleFocusNode = (nodeId) => {
|
|
242
|
+
return exportGLBData(rootObject);
|
|
243
|
+
}), [clearSelection]);
|
|
244
|
+
const handleFocusNode = useCallback((nodeId) => {
|
|
223
245
|
var _a;
|
|
224
246
|
const object = (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.getObject(nodeId);
|
|
225
247
|
const controls = controlsRef.current;
|
|
@@ -227,7 +249,15 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, onPr
|
|
|
227
249
|
if (!object || !controls || !camera)
|
|
228
250
|
return;
|
|
229
251
|
focusCameraOnObject(object, camera, controls.target, () => { var _a; return (_a = controls.update) === null || _a === void 0 ? void 0 : _a.call(controls); });
|
|
230
|
-
};
|
|
252
|
+
}, []);
|
|
253
|
+
const scene = useMemo(() => createScene({
|
|
254
|
+
getRootId: () => prefabStore.getState().rootId,
|
|
255
|
+
getNode: (id) => { var _a; return (_a = prefabStore.getState().nodesById[id]) !== null && _a !== void 0 ? _a : null; },
|
|
256
|
+
updateNode: (id, update) => prefabStore.getState().updateNode(id, update),
|
|
257
|
+
updateNodes: (updates) => prefabStore.getState().updateNodes(Object.entries(updates).map(([id, update]) => ({ id, update }))),
|
|
258
|
+
addNode: (node, options) => addNode(node, options).id,
|
|
259
|
+
removeNode: (id) => prefabStore.getState().deleteNode(id),
|
|
260
|
+
}), [addNode, prefabStore]);
|
|
231
261
|
const handleTransformChange = () => {
|
|
232
262
|
var _a;
|
|
233
263
|
if (!selectedId)
|
|
@@ -235,13 +265,13 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, onPr
|
|
|
235
265
|
const object = (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.getObject(selectedId);
|
|
236
266
|
if (!object)
|
|
237
267
|
return;
|
|
238
|
-
const parentWorld = computeParentWorldMatrix(
|
|
268
|
+
const parentWorld = computeParentWorldMatrix(prefabStore.getState(), selectedId);
|
|
239
269
|
const local = parentWorld.clone().invert().multiply(object.matrixWorld);
|
|
240
270
|
const { position, rotation, scale } = decompose(local);
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
271
|
+
prefabStore.getState().updateNode(selectedId, node => (Object.assign(Object.assign({}, node), { components: Object.assign(Object.assign({}, node.components), { transform: {
|
|
272
|
+
type: "Transform",
|
|
273
|
+
properties: { position, rotation, scale },
|
|
274
|
+
} }) })));
|
|
245
275
|
};
|
|
246
276
|
// --- Drag & drop files to add nodes ---
|
|
247
277
|
useEffect(() => {
|
|
@@ -278,43 +308,43 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, onPr
|
|
|
278
308
|
window.removeEventListener('dragover', handleDragOver);
|
|
279
309
|
window.removeEventListener('drop', handleDrop);
|
|
280
310
|
};
|
|
281
|
-
}, [editMode, enableWindowDrop]);
|
|
311
|
+
}, [addModel, addTexture, editMode, enableWindowDrop]);
|
|
282
312
|
useImperativeHandle(ref, () => ({
|
|
283
313
|
screenshot: handleScreenshot,
|
|
284
314
|
exportGLB: handleExportGLB,
|
|
285
315
|
exportGLBData: handleExportGLBData,
|
|
286
316
|
clearSelection,
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
317
|
+
save: getPrefab,
|
|
318
|
+
scene,
|
|
319
|
+
load: loadPrefab,
|
|
290
320
|
addModel,
|
|
291
321
|
addTexture,
|
|
292
|
-
|
|
293
|
-
}));
|
|
294
|
-
const content = (_jsxs(_Fragment, { children: [_jsx("ambientLight", { intensity: 1.5 }), _jsx("gridHelper", { args: [10, 10], position: [0, -1, 0] }), _jsx(PrefabRoot, { ref: prefabRootRef,
|
|
295
|
-
return _jsxs(EditorContext.Provider, { value: {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
322
|
+
viewRef: prefabRootRef
|
|
323
|
+
}), [addModel, addTexture, clearSelection, getPrefab, handleExportGLB, handleExportGLBData, handleScreenshot, loadPrefab, scene]);
|
|
324
|
+
const content = (_jsxs(_Fragment, { children: [_jsx("ambientLight", { intensity: 1.5 }), _jsx("gridHelper", { args: [10, 10], position: [0, -1, 0] }), _jsx(PrefabRoot, { ref: prefabRootRef, store: prefabStore, editMode: editMode, selectedId: selectedId, onSelect: setSelection, onSelectedObjectChange: editMode ? setSelectedObject : undefined, onFocusNode: editMode ? handleFocusNode : undefined, basePath: basePath, injectedModels: injectedModels, injectedTextures: injectedTextures }), children] }));
|
|
325
|
+
return _jsx(PrefabStoreProvider, { store: prefabStore, children: _jsxs(EditorContext.Provider, { value: {
|
|
326
|
+
editMode,
|
|
327
|
+
transformMode,
|
|
328
|
+
setTransformMode,
|
|
329
|
+
scaleSnap,
|
|
330
|
+
setScaleSnap,
|
|
331
|
+
positionSnap,
|
|
332
|
+
setPositionSnap,
|
|
333
|
+
rotationSnap,
|
|
334
|
+
setRotationSnap,
|
|
335
|
+
onFocusNode: editMode ? handleFocusNode : undefined,
|
|
336
|
+
onScreenshot: handleScreenshot,
|
|
337
|
+
onExportGLB: handleExportGLB
|
|
338
|
+
}, children: [_jsxs(GameCanvas, Object.assign({ camera: { position: [0, 5, 15] }, canvasRef: canvasRef }, canvasProps, { onPointerMissed: editMode
|
|
339
|
+
? (event) => {
|
|
340
|
+
var _a, _b, _c, _d;
|
|
341
|
+
const button = (_c = (_a = event.button) !== null && _a !== void 0 ? _a : (_b = event.sourceEvent) === null || _b === void 0 ? void 0 : _b.button) !== null && _c !== void 0 ? _c : 0;
|
|
342
|
+
if (button === 0 && selectedId) {
|
|
343
|
+
setSelection(null);
|
|
344
|
+
}
|
|
345
|
+
(_d = canvasProps === null || canvasProps === void 0 ? void 0 : canvasProps.onPointerMissed) === null || _d === void 0 ? void 0 : _d.call(canvasProps, event);
|
|
314
346
|
}
|
|
315
|
-
|
|
316
|
-
}
|
|
317
|
-
: canvasProps === null || canvasProps === void 0 ? void 0 : canvasProps.onPointerMissed, children: [physics ? (_jsx(Physics, { debug: editMode, paused: editMode, children: content })) : content, editMode && (_jsxs(_Fragment, { children: [_jsx(MapControls, { ref: controlsRef, makeDefault: true }), selectedObject && (_jsx(TransformControls, { object: selectedObject, mode: transformMode, space: "local", onObjectChange: handleTransformChange, translationSnap: positionSnap > 0 ? positionSnap : undefined, rotationSnap: rotationSnap > 0 ? rotationSnap : undefined, scaleSnap: snapResolution > 0 ? snapResolution : undefined }, `transform-${transformMode}-${positionSnap}-${rotationSnap}-${snapResolution}`))] }))] })), showUI && (_jsxs(_Fragment, { children: [_jsxs("div", { style: toolbar.panel, children: [_jsx("button", { style: base.btn, onClick: toggleEditMode, children: editMode ? "▶" : "⏸" }), uiPlugins] }), editMode && (_jsx(EditorUI, { prefabData: loadedPrefab, setPrefabData: updatePrefab, selectedId: selectedId, setSelectedId: setSelectedIdState, basePath: basePath, onUndo: undo, onRedo: redo, canUndo: historyIndex > 0, canRedo: historyIndex < history.length - 1 }))] }))] });
|
|
347
|
+
: canvasProps === null || canvasProps === void 0 ? void 0 : canvasProps.onPointerMissed, children: [physics ? (_jsx(Physics, { debug: editMode, paused: editMode, children: content })) : content, editMode && (_jsxs(_Fragment, { children: [_jsx(MapControls, { ref: controlsRef, makeDefault: true }), selectedObject && (_jsx(TransformControls, { object: selectedObject, mode: transformMode, space: "local", onObjectChange: handleTransformChange, translationSnap: positionSnap > 0 ? positionSnap : undefined, rotationSnap: rotationSnap > 0 ? rotationSnap : undefined, scaleSnap: scaleSnap > 0 ? scaleSnap : undefined }, `transform-${transformMode}-${positionSnap}-${rotationSnap}-${scaleSnap}`))] }))] })), showUI && (_jsxs(_Fragment, { children: [_jsxs("div", { style: toolbar.panel, children: [_jsx("button", { style: base.btn, onClick: toggleEditMode, children: editMode ? "▶" : "⏸" }), uiPlugins] }), editMode && (_jsx(EditorUI, { selectedId: selectedId, setSelectedId: setSelection, getPrefab: getPrefab, onReplacePrefab: (prefab) => loadPrefab(prefab, { resetHistory: true }), onImportPrefab: importPrefab, basePath: basePath, onUndo: undo, onRedo: redo, canUndo: historyIndex > 0, canRedo: historyIndex < history.length - 1 }))] }))] }) });
|
|
318
348
|
});
|
|
319
349
|
PrefabEditor.displayName = "PrefabEditor";
|
|
320
350
|
export default PrefabEditor;
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { Group, Matrix4, Object3D } from "three";
|
|
2
2
|
import { ThreeEvent } from "@react-three/fiber";
|
|
3
|
-
import {
|
|
3
|
+
import { GameObject as GameObjectType, Prefab } from "./types";
|
|
4
4
|
import { LoadedModels, LoadedTextures } from "../dragdrop";
|
|
5
|
+
import { PrefabStoreApi } from "./prefabStore";
|
|
5
6
|
export interface PrefabRootRef {
|
|
6
7
|
root: Group | null;
|
|
7
8
|
rigidBodyRefs: Map<string, any>;
|
|
8
9
|
getObject: (nodeId: string) => Object3D | null;
|
|
9
10
|
focusNode: (nodeId: string) => void;
|
|
10
11
|
}
|
|
11
|
-
export
|
|
12
|
+
export interface PrefabRootProps {
|
|
12
13
|
editMode?: boolean;
|
|
13
|
-
data
|
|
14
|
+
data?: Prefab;
|
|
15
|
+
store?: PrefabStoreApi;
|
|
14
16
|
selectedId?: string | null;
|
|
15
17
|
onSelect?: (id: string | null) => void;
|
|
16
18
|
onClick?: (event: ThreeEvent<PointerEvent>, entity: GameObjectType) => void;
|
|
@@ -19,10 +21,11 @@ export declare const PrefabRoot: import("react").ForwardRefExoticComponent<{
|
|
|
19
21
|
basePath?: string;
|
|
20
22
|
injectedModels?: LoadedModels;
|
|
21
23
|
injectedTextures?: LoadedTextures;
|
|
22
|
-
}
|
|
24
|
+
}
|
|
25
|
+
export declare const PrefabRoot: import("react").ForwardRefExoticComponent<PrefabRootProps & import("react").RefAttributes<PrefabRootRef>>;
|
|
23
26
|
export declare function GameObjectRenderer(props: RendererProps): import("react/jsx-runtime").JSX.Element | null;
|
|
24
27
|
interface RendererProps {
|
|
25
|
-
|
|
28
|
+
nodeId: string;
|
|
26
29
|
selectedId?: string | null;
|
|
27
30
|
onSelect?: (id: string) => void;
|
|
28
31
|
onClick?: (event: ThreeEvent<PointerEvent>, entity: GameObjectType) => void;
|