react-three-game 0.0.91 → 0.0.92
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/dist/tools/assetviewer/page.js +38 -10
- package/dist/tools/prefabeditor/PrefabEditor.js +34 -13
- package/dist/tools/prefabeditor/PrefabRoot.d.ts +0 -1
- package/dist/tools/prefabeditor/PrefabRoot.js +5 -16
- package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +1 -1
- package/dist/tools/prefabeditor/components/PhysicsComponent.js +28 -13
- package/package.json +1 -1
|
@@ -12,6 +12,34 @@ const styles = {
|
|
|
12
12
|
textLight: { color: '#f9fafb' },
|
|
13
13
|
iconLarge: { fontSize: 20 }
|
|
14
14
|
};
|
|
15
|
+
const assetViewerColors = {
|
|
16
|
+
panelBg: '#111827',
|
|
17
|
+
controlBg: '#1f2937',
|
|
18
|
+
text: '#f9fafb',
|
|
19
|
+
border: 'rgba(255,255,255,0.12)',
|
|
20
|
+
accentBorder: 'rgba(34, 211, 238, 0.3)',
|
|
21
|
+
};
|
|
22
|
+
const assetPickerPopupBaseStyle = {
|
|
23
|
+
background: assetViewerColors.panelBg,
|
|
24
|
+
border: `1px solid ${assetViewerColors.border}`,
|
|
25
|
+
borderRadius: 0,
|
|
26
|
+
boxShadow: '0 4px 16px rgba(0,0,0,0.6)',
|
|
27
|
+
};
|
|
28
|
+
const assetPickerButtonBaseStyle = {
|
|
29
|
+
backgroundColor: assetViewerColors.controlBg,
|
|
30
|
+
color: 'inherit',
|
|
31
|
+
fontSize: 10,
|
|
32
|
+
cursor: 'pointer',
|
|
33
|
+
border: `1px solid ${assetViewerColors.border}`,
|
|
34
|
+
borderRadius: 0,
|
|
35
|
+
};
|
|
36
|
+
const assetPickerWideButtonStyle = Object.assign(Object.assign({}, assetPickerButtonBaseStyle), { width: '100%', padding: '6px 8px' });
|
|
37
|
+
const assetPickerSmallButtonStyle = Object.assign(Object.assign({}, assetPickerButtonBaseStyle), { padding: '4px 8px' });
|
|
38
|
+
const assetPickerEmptyPreviewStyle = {
|
|
39
|
+
backgroundColor: assetViewerColors.controlBg,
|
|
40
|
+
border: `1px dashed ${assetViewerColors.border}`,
|
|
41
|
+
borderRadius: 0,
|
|
42
|
+
};
|
|
15
43
|
function getItemsInPath(files, currentPath) {
|
|
16
44
|
// Remove the leading category folder (e.g., /textures/, /models/, /sounds/)
|
|
17
45
|
const filesWithoutCategory = files.map(file => {
|
|
@@ -39,8 +67,8 @@ function FolderTile({ name, onClick }) {
|
|
|
39
67
|
return (_jsxs("div", { onClick: onClick, style: {
|
|
40
68
|
maxWidth: 60,
|
|
41
69
|
aspectRatio: '1 / 1',
|
|
42
|
-
backgroundColor:
|
|
43
|
-
color:
|
|
70
|
+
backgroundColor: assetViewerColors.controlBg,
|
|
71
|
+
color: assetViewerColors.text,
|
|
44
72
|
cursor: 'pointer',
|
|
45
73
|
display: 'flex',
|
|
46
74
|
flexDirection: 'column',
|
|
@@ -73,7 +101,7 @@ function AssetListViewer({ files, selected, onSelect, renderCard }) {
|
|
|
73
101
|
const pathParts = currentPath.split('/').filter(Boolean);
|
|
74
102
|
pathParts.pop();
|
|
75
103
|
setCurrentPath(pathParts.join('/'));
|
|
76
|
-
}, style: {
|
|
104
|
+
}, style: Object.assign(Object.assign({}, assetPickerSmallButtonStyle), { marginBottom: 4, fontSize: 12, border: 'none' }), children: "\u2190 Back" })), _jsxs("div", { style: { display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 4 }, children: [folders.map((folder) => (_jsx(FolderTile, { name: folder, onClick: () => setCurrentPath(currentPath ? `${currentPath}/${folder}` : folder) }, folder))), filesInCurrentPath.map((file) => (_jsx("div", { children: renderCard(file, onSelect) }, file)))] })] }));
|
|
77
105
|
}
|
|
78
106
|
export function TextureListViewer({ files, selected, onSelect, basePath = "" }) {
|
|
79
107
|
return (_jsxs("div", { style: { position: 'relative', width: '100%', height: '100%' }, children: [_jsx("div", { style: { width: '100%', height: '100%', overflowY: 'auto', overflowX: 'hidden', paddingRight: 4 }, children: _jsx(AssetListViewer, { files: files, selected: selected, onSelect: onSelect, renderCard: (file, onSelectHandler) => (_jsx(TextureCard, { file: file, basePath: basePath, onSelect: onSelectHandler })) }) }), _jsx(SharedCanvas, {})] }));
|
|
@@ -172,8 +200,8 @@ function AssetPicker({ value, onChange, basePath, manifestFolder, preview, rende
|
|
|
172
200
|
const fitsLeft = preferredLeft >= 8;
|
|
173
201
|
const left = fitsLeft ? preferredLeft : Math.min(fallbackLeft, window.innerWidth - PICKER_POPUP_WIDTH - 8);
|
|
174
202
|
const top = Math.min(Math.max(8, rect.top), window.innerHeight - PICKER_POPUP_HEIGHT - 8);
|
|
175
|
-
setResolvedPopupStyle(Object.assign({ position: 'fixed', left,
|
|
176
|
-
top, padding: 12, width: PICKER_POPUP_WIDTH, height: PICKER_POPUP_HEIGHT, overflow: 'hidden', zIndex: 1000
|
|
203
|
+
setResolvedPopupStyle(Object.assign(Object.assign({ position: 'fixed', left,
|
|
204
|
+
top, padding: 12, width: PICKER_POPUP_WIDTH, height: PICKER_POPUP_HEIGHT, overflow: 'hidden', zIndex: 1000 }, assetPickerPopupBaseStyle), popupStyle));
|
|
177
205
|
};
|
|
178
206
|
updatePosition();
|
|
179
207
|
window.addEventListener('resize', updatePosition);
|
|
@@ -194,23 +222,23 @@ function AssetPicker({ value, onChange, basePath, manifestFolder, preview, rende
|
|
|
194
222
|
}) }), document.body)] }));
|
|
195
223
|
}
|
|
196
224
|
export function TexturePicker({ value, onChange, basePath = "" }) {
|
|
197
|
-
return (_jsx(AssetPicker, { value: value, onChange: onChange, basePath: basePath, manifestFolder: "textures", rootStyle: { maxHeight: 128, overflow: 'visible', position: 'relative', display: 'flex', alignItems: 'center' }, changeButtonStyle: {
|
|
225
|
+
return (_jsx(AssetPicker, { value: value, onChange: onChange, basePath: basePath, manifestFolder: "textures", rootStyle: { maxHeight: 128, overflow: 'visible', position: 'relative', display: 'flex', alignItems: 'center' }, changeButtonStyle: Object.assign(Object.assign({}, assetPickerSmallButtonStyle), { marginTop: 4 }), clearButtonStyle: Object.assign(Object.assign({}, assetPickerSmallButtonStyle), { marginTop: 4, marginLeft: 4 }), preview: _jsx(SingleTextureViewer, { file: value, basePath: basePath }), renderList: ({ files, value: selectedValue, onSelect, basePath: currentBasePath }) => (_jsx(TextureListViewer, { files: files, selected: selectedValue || undefined, onSelect: onSelect, basePath: currentBasePath })) }));
|
|
198
226
|
}
|
|
199
227
|
export function ModelPicker({ value, onChange, basePath = "", pickerKey }) {
|
|
200
|
-
return (_jsx(AssetPicker, { value: value, onChange: onChange, basePath: basePath, manifestFolder: "models", rootStyle: { maxHeight: 160, overflow: 'visible', position: 'relative', display: 'flex', gap: 8, alignItems: 'center', justifyContent: 'center' }, controlsStyle: { display: 'flex', flexDirection: 'column', gap: 6, flex: '0 0 84px', minWidth: 84, justifyContent: 'flex-end' }, changeButtonStyle: {
|
|
228
|
+
return (_jsx(AssetPicker, { value: value, onChange: onChange, basePath: basePath, manifestFolder: "models", rootStyle: { maxHeight: 160, overflow: 'visible', position: 'relative', display: 'flex', gap: 8, alignItems: 'center', justifyContent: 'center' }, controlsStyle: { display: 'flex', flexDirection: 'column', gap: 6, flex: '0 0 84px', minWidth: 84, justifyContent: 'flex-end' }, changeButtonStyle: Object.assign(Object.assign({}, assetPickerWideButtonStyle), { border: `1px solid ${assetViewerColors.accentBorder}` }), clearButtonStyle: Object.assign(Object.assign({}, assetPickerWideButtonStyle), { border: `1px solid ${assetViewerColors.accentBorder}` }), popupStyle: { background: 'rgba(0,0,0,0.9)', border: `1px solid ${assetViewerColors.accentBorder}` }, preview: _jsx("div", { style: { flex: '0 0 auto' }, children: _jsx(SingleModelViewer, { file: value ? `/${value}` : undefined, basePath: basePath }) }), renderList: ({ files, value: selectedValue, onSelect, basePath: currentBasePath }) => (_jsx(ModelListViewer, { files: files, selected: selectedValue ? `/${selectedValue}` : undefined, onSelect: (file) => onSelect(file.startsWith('/') ? file.slice(1) : file), basePath: currentBasePath }, pickerKey)) }));
|
|
201
229
|
}
|
|
202
230
|
export function SoundPicker({ value, onChange, basePath = "" }) {
|
|
203
|
-
return (_jsx(AssetPicker, { value: value, onChange: onChange, basePath: basePath, manifestFolder: "sound", rootStyle: { maxHeight: 76, overflow: 'visible', position: 'relative', display: 'flex', gap: 8, alignItems: 'center', justifyContent: 'center' }, controlsStyle: { display: 'flex', flexDirection: 'column', gap: 6, flex: '0 0 84px', minWidth: 84, justifyContent: 'flex-end' }, changeButtonStyle:
|
|
231
|
+
return (_jsx(AssetPicker, { value: value, onChange: onChange, basePath: basePath, manifestFolder: "sound", rootStyle: { maxHeight: 76, overflow: 'visible', position: 'relative', display: 'flex', gap: 8, alignItems: 'center', justifyContent: 'center' }, controlsStyle: { display: 'flex', flexDirection: 'column', gap: 6, flex: '0 0 84px', minWidth: 84, justifyContent: 'flex-end' }, changeButtonStyle: assetPickerWideButtonStyle, clearButtonStyle: assetPickerWideButtonStyle, preview: _jsx("div", { style: { flex: '0 0 auto', minWidth: 84 }, children: value ? _jsx(SingleSoundViewer, { file: value, basePath: basePath }) : _jsx("div", { style: Object.assign({ width: 84, height: 60 }, assetPickerEmptyPreviewStyle) }) }), renderList: ({ files, value: selectedValue, onSelect, basePath: currentBasePath }) => (_jsx(SoundListViewer, { files: files, selected: selectedValue || undefined, onSelect: onSelect, basePath: currentBasePath })) }));
|
|
204
232
|
}
|
|
205
233
|
// Single Asset Viewer Components - display only one selected asset
|
|
206
234
|
export function SingleTextureViewer({ file, basePath = "" }) {
|
|
207
235
|
if (!file)
|
|
208
|
-
return _jsx("div", { style: { width: 60, aspectRatio: '1 / 1'
|
|
236
|
+
return _jsx("div", { style: Object.assign({ width: 60, aspectRatio: '1 / 1' }, assetPickerEmptyPreviewStyle) });
|
|
209
237
|
return (_jsxs(_Fragment, { children: [_jsx(TextureCard, { file: file, basePath: basePath, onSelect: () => { } }), _jsx(SharedCanvas, {})] }));
|
|
210
238
|
}
|
|
211
239
|
export function SingleModelViewer({ file, basePath = "" }) {
|
|
212
240
|
if (!file)
|
|
213
|
-
return _jsx("div", { style: { width: 112, aspectRatio: '1 / 1'
|
|
241
|
+
return _jsx("div", { style: Object.assign({ width: 112, aspectRatio: '1 / 1' }, assetPickerEmptyPreviewStyle) });
|
|
214
242
|
return (_jsxs(_Fragment, { children: [_jsx(ModelCard, { file: file, basePath: basePath, onSelect: () => { }, size: 112 }), _jsx(SharedCanvas, {})] }));
|
|
215
243
|
}
|
|
216
244
|
export function SingleSoundViewer({ file, basePath = "" }) {
|
|
@@ -8,9 +8,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
11
|
-
import { MapControls, TransformControls } from "@react-three/drei";
|
|
11
|
+
import { MapControls, TransformControls, useHelper } from "@react-three/drei";
|
|
12
12
|
import GameCanvas from "../../shared/GameCanvas";
|
|
13
|
-
import { useCallback, useEffect,
|
|
13
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState, forwardRef, useImperativeHandle, createContext, useContext } from "react";
|
|
14
|
+
import { BoxHelper } from "three";
|
|
14
15
|
import { findComponentEntry } from "./types";
|
|
15
16
|
import PrefabRoot from "./PrefabRoot";
|
|
16
17
|
import { Physics } from "@react-three/rapier";
|
|
@@ -31,6 +32,12 @@ function isObjectAttachedToRoot(root, object) {
|
|
|
31
32
|
}
|
|
32
33
|
return false;
|
|
33
34
|
}
|
|
35
|
+
function SelectionHelper({ object }) {
|
|
36
|
+
const objectRef = useRef(null);
|
|
37
|
+
objectRef.current = object;
|
|
38
|
+
useHelper(object ? objectRef : null, BoxHelper, "cyan");
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
34
41
|
export var PrefabEditorMode;
|
|
35
42
|
(function (PrefabEditorMode) {
|
|
36
43
|
PrefabEditorMode["Edit"] = "edit";
|
|
@@ -52,6 +59,7 @@ const DEFAULT_PREFAB = {
|
|
|
52
59
|
root: createNode('Root', {}, { id: 'root' })
|
|
53
60
|
};
|
|
54
61
|
const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode: initialMode = PrefabEditorMode.Edit, onChange, showUI = true, enableWindowDrop = true, canvasProps, uiPlugins, children }, ref) => {
|
|
62
|
+
var _a, _b;
|
|
55
63
|
const [mode, setMode] = useState(initialMode);
|
|
56
64
|
const [selectedId, setSelectedId] = useState(null);
|
|
57
65
|
const [transformMode, setTransformMode] = useState("translate");
|
|
@@ -64,21 +72,17 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode
|
|
|
64
72
|
const [historyIndex, setHistoryIndex] = useState(0);
|
|
65
73
|
const changeOriginRef = useRef(null);
|
|
66
74
|
const historyIndexRef = useRef(0);
|
|
67
|
-
const [, bumpSelectedObjectVersion] = useReducer((value) => value + 1, 0);
|
|
68
75
|
const prefabRootRef = useRef(null);
|
|
69
76
|
const canvasRef = useRef(null);
|
|
70
77
|
const controlsRef = useRef(null);
|
|
78
|
+
const transformControlsRef = useRef(null);
|
|
79
|
+
const transformProxyRef = useRef(null);
|
|
71
80
|
const onChangeRef = useRef(onChange);
|
|
72
81
|
const isEditMode = mode === PrefabEditorMode.Edit;
|
|
73
82
|
const getPrefab = useCallback(() => denormalizePrefab(prefabStore.getState()), [prefabStore]);
|
|
74
83
|
const getRootObject = useCallback(() => { var _a, _b; return (_b = (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.root) !== null && _b !== void 0 ? _b : null; }, []);
|
|
75
84
|
const getObject = useCallback((nodeId) => { var _a, _b; return (_b = (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.getObject(nodeId)) !== null && _b !== void 0 ? _b : null; }, []);
|
|
76
85
|
const getRigidBody = useCallback((nodeId) => { var _a, _b; return (_b = (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.getRigidBody(nodeId)) !== null && _b !== void 0 ? _b : null; }, []);
|
|
77
|
-
const handleObjectRefChange = useCallback((nodeId) => {
|
|
78
|
-
if (nodeId !== selectedId)
|
|
79
|
-
return;
|
|
80
|
-
bumpSelectedObjectVersion();
|
|
81
|
-
}, [selectedId]);
|
|
82
86
|
onChangeRef.current = onChange;
|
|
83
87
|
const setSelection = useCallback((nodeId) => {
|
|
84
88
|
const nextNode = nodeId ? prefabStore.getState().nodesById[nodeId] : null;
|
|
@@ -104,7 +108,9 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode
|
|
|
104
108
|
updateMode(initialMode);
|
|
105
109
|
}, [initialMode, updateMode]);
|
|
106
110
|
const loadPrefab = useCallback((prefab, options) => {
|
|
111
|
+
var _a;
|
|
107
112
|
changeOriginRef.current = (options === null || options === void 0 ? void 0 : options.notifyChange) === false ? "replace-silent" : "replace";
|
|
113
|
+
(_a = transformControlsRef.current) === null || _a === void 0 ? void 0 : _a.detach();
|
|
108
114
|
prefabStore.getState().replacePrefab(prefab);
|
|
109
115
|
if (options === null || options === void 0 ? void 0 : options.resetHistory) {
|
|
110
116
|
setSelectedId(null);
|
|
@@ -171,10 +177,23 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode
|
|
|
171
177
|
});
|
|
172
178
|
return () => unsubscribe();
|
|
173
179
|
}, [prefabStore, selectedId]);
|
|
180
|
+
const selectedNode = selectedId ? (_a = prefabStore.getState().nodesById[selectedId]) !== null && _a !== void 0 ? _a : null : null;
|
|
174
181
|
const selectedObject = selectedId ? getObject(selectedId) : null;
|
|
175
|
-
const
|
|
176
|
-
|
|
182
|
+
const selectedHasPhysics = Object.values((_b = selectedNode === null || selectedNode === void 0 ? void 0 : selectedNode.components) !== null && _b !== void 0 ? _b : {}).some(component => (component === null || component === void 0 ? void 0 : component.type) === "Physics");
|
|
183
|
+
const transformObject = isEditMode && (selectedHasPhysics ? transformProxyRef.current : selectedObject)
|
|
184
|
+
&& isObjectAttachedToRoot(getRootObject(), selectedObject)
|
|
185
|
+
? (selectedHasPhysics ? transformProxyRef.current : selectedObject)
|
|
177
186
|
: null;
|
|
187
|
+
useLayoutEffect(() => {
|
|
188
|
+
if (!isEditMode || !selectedHasPhysics || !selectedObject || !transformProxyRef.current) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
selectedObject.updateMatrixWorld(true);
|
|
192
|
+
transformProxyRef.current.matrixAutoUpdate = true;
|
|
193
|
+
selectedObject.matrixWorld.decompose(transformProxyRef.current.position, transformProxyRef.current.quaternion, transformProxyRef.current.scale);
|
|
194
|
+
transformProxyRef.current.updateMatrix();
|
|
195
|
+
transformProxyRef.current.updateMatrixWorld(true);
|
|
196
|
+
}, [isEditMode, selectedHasPhysics, selectedId, selectedObject]);
|
|
178
197
|
const addNode = useCallback((node, options) => {
|
|
179
198
|
var _a;
|
|
180
199
|
const { addChild, rootId } = prefabStore.getState();
|
|
@@ -202,7 +221,9 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode
|
|
|
202
221
|
return node;
|
|
203
222
|
}, [addNode]);
|
|
204
223
|
const applyHistory = (index) => {
|
|
224
|
+
var _a;
|
|
205
225
|
changeOriginRef.current = "history";
|
|
226
|
+
(_a = transformControlsRef.current) === null || _a === void 0 ? void 0 : _a.detach();
|
|
206
227
|
prefabStore.getState().replacePrefab(history[index]);
|
|
207
228
|
historyIndexRef.current = index;
|
|
208
229
|
setHistoryIndex(index);
|
|
@@ -274,7 +295,7 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode
|
|
|
274
295
|
const handleTransformChange = () => {
|
|
275
296
|
if (!selectedId)
|
|
276
297
|
return;
|
|
277
|
-
const object = getObject(selectedId);
|
|
298
|
+
const object = selectedHasPhysics ? transformProxyRef.current : getObject(selectedId);
|
|
278
299
|
if (!object)
|
|
279
300
|
return;
|
|
280
301
|
const parentWorld = computeParentWorldMatrix(prefabStore.getState(), selectedId);
|
|
@@ -341,7 +362,7 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode
|
|
|
341
362
|
addModel,
|
|
342
363
|
addTexture
|
|
343
364
|
}), [addModel, addNode, addTexture, clearSelection, getObject, getPrefab, getRigidBody, getRootObject, handleExportGLB, handleExportGLBData, handleScreenshot, loadPrefab, prefabStore]);
|
|
344
|
-
const content = (_jsxs(_Fragment, { children: [isEditMode ? _jsx("gridHelper", { args: [10, 10], position: [0, -1, 0] }) : null, _jsx(PrefabRoot, { ref: prefabRootRef, store: prefabStore, editMode: isEditMode, selectedId: selectedId, onSelect: setSelection,
|
|
365
|
+
const content = (_jsxs(_Fragment, { children: [isEditMode ? _jsx("gridHelper", { args: [10, 10], position: [0, -1, 0] }) : null, _jsx(PrefabRoot, { ref: prefabRootRef, store: prefabStore, editMode: isEditMode, selectedId: selectedId, onSelect: setSelection, basePath: basePath }), children] }));
|
|
345
366
|
const handleCanvasCreated = useCallback((state) => {
|
|
346
367
|
var _a;
|
|
347
368
|
canvasRef.current = state.gl.domElement;
|
|
@@ -370,7 +391,7 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode
|
|
|
370
391
|
}
|
|
371
392
|
(_d = canvasProps === null || canvasProps === void 0 ? void 0 : canvasProps.onPointerMissed) === null || _d === void 0 ? void 0 : _d.call(canvasProps, event);
|
|
372
393
|
}
|
|
373
|
-
: canvasProps === null || canvasProps === void 0 ? void 0 : canvasProps.onPointerMissed, children: [physics ? (_jsx(Physics, { colliders: false, debug: isEditMode, paused: isEditMode, children: content })) : content, isEditMode && (_jsxs(_Fragment, { children: [_jsx(MapControls, { ref: controlsRef, enableDamping: false, makeDefault: true }), transformObject && (_jsx(TransformControls, { object: transformObject, mode: transformMode, space: "local", onObjectChange: handleTransformChange, translationSnap: positionSnap > 0 ? positionSnap : undefined, rotationSnap: rotationSnap > 0 ? rotationSnap : undefined, scaleSnap: scaleSnap > 0 ? scaleSnap : undefined }, `transform-${selectedId}-${transformMode}-${positionSnap}-${rotationSnap}-${scaleSnap}`))] }))] })), showUI && (_jsxs(_Fragment, { children: [_jsxs("div", { style: toolbar.panel, children: [_jsx("button", { style: base.btn, onClick: toggleMode, children: isEditMode ? "▶" : "⏸" }), uiPlugins] }), isEditMode && (_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 }))] }))] }) });
|
|
394
|
+
: canvasProps === null || canvasProps === void 0 ? void 0 : canvasProps.onPointerMissed, children: [physics ? (_jsx(Physics, { colliders: false, debug: isEditMode, paused: isEditMode, children: content })) : content, _jsx("group", { ref: transformProxyRef, visible: false }), isEditMode ? _jsx(SelectionHelper, { object: transformObject }) : null, isEditMode && (_jsxs(_Fragment, { children: [_jsx(MapControls, { ref: controlsRef, enableDamping: false, makeDefault: true }), transformObject && (_jsx(TransformControls, { ref: transformControlsRef, object: transformObject, mode: transformMode, space: "local", onObjectChange: handleTransformChange, translationSnap: positionSnap > 0 ? positionSnap : undefined, rotationSnap: rotationSnap > 0 ? rotationSnap : undefined, scaleSnap: scaleSnap > 0 ? scaleSnap : undefined }, `transform-${selectedId}-${transformMode}-${positionSnap}-${rotationSnap}-${scaleSnap}`))] }))] })), showUI && (_jsxs(_Fragment, { children: [_jsxs("div", { style: toolbar.panel, children: [_jsx("button", { style: base.btn, onClick: toggleMode, children: isEditMode ? "▶" : "⏸" }), uiPlugins] }), isEditMode && (_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 }))] }))] }) });
|
|
374
395
|
});
|
|
375
396
|
PrefabEditor.displayName = "PrefabEditor";
|
|
376
397
|
export default PrefabEditor;
|
|
@@ -18,7 +18,6 @@ export interface PrefabRootProps {
|
|
|
18
18
|
selectedId?: string | null;
|
|
19
19
|
onSelect?: (id: string | null) => void;
|
|
20
20
|
onClick?: (event: ThreeEvent<PointerEvent>, entity: GameObjectType) => void;
|
|
21
|
-
onObjectRefChange?: (id: string, object: Object3D | null) => void;
|
|
22
21
|
basePath?: string;
|
|
23
22
|
}
|
|
24
23
|
export declare const PrefabRoot: import("react").ForwardRefExoticComponent<PrefabRootProps & import("react").RefAttributes<PrefabRootRef>>;
|
|
@@ -10,9 +10,8 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
12
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
-
import { useHelper } from "@react-three/drei";
|
|
14
13
|
import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
|
|
15
|
-
import {
|
|
14
|
+
import { Euler, Matrix4, } from "three";
|
|
16
15
|
import { useStore } from "zustand";
|
|
17
16
|
import { useClickValid } from "./useClickValid";
|
|
18
17
|
import { findComponent, getNodeUserData } from "./types";
|
|
@@ -69,7 +68,7 @@ function getNodeMetadataProps(node) {
|
|
|
69
68
|
userData: Object.assign(Object.assign({ prefabNodeId: node.id }, (nodeName ? { prefabNodeName: nodeName } : {})), getNodeUserData(node)),
|
|
70
69
|
};
|
|
71
70
|
}
|
|
72
|
-
export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSelect, onClick,
|
|
71
|
+
export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSelect, onClick, basePath = "" }, ref) => {
|
|
73
72
|
var _a;
|
|
74
73
|
const [models, setModels] = useState({});
|
|
75
74
|
const [textures, setTextures] = useState({});
|
|
@@ -120,8 +119,7 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
120
119
|
}), [getObject]);
|
|
121
120
|
const registerRef = useCallback((id, obj) => {
|
|
122
121
|
objectRefs.current[id] = obj;
|
|
123
|
-
|
|
124
|
-
}, [onObjectRefChange]);
|
|
122
|
+
}, []);
|
|
125
123
|
const registerRigidBodyRef = useCallback((id, rb) => {
|
|
126
124
|
rigidBodyRefs.current.set(id, rb);
|
|
127
125
|
}, []);
|
|
@@ -293,18 +291,10 @@ function StandardNode({ nodeId, selectedId, onSelect, onClick, registerRef, load
|
|
|
293
291
|
const clickEventName = getNodeClickEventName(gameObject);
|
|
294
292
|
const metadataProps = getNodeMetadataProps(gameObject);
|
|
295
293
|
const groupRef = useRef(null);
|
|
296
|
-
const helperRef = useRef(null);
|
|
297
294
|
const handleGroupRef = useCallback((object) => {
|
|
298
295
|
groupRef.current = object;
|
|
299
296
|
registerRef(nodeId, object);
|
|
300
297
|
}, [nodeId, registerRef]);
|
|
301
|
-
const handleHelperRef = useCallback((object) => {
|
|
302
|
-
helperRef.current = object;
|
|
303
|
-
}, []);
|
|
304
|
-
const handleEditGroupRef = useCallback((object) => {
|
|
305
|
-
handleGroupRef(object);
|
|
306
|
-
handleHelperRef(object);
|
|
307
|
-
}, [handleGroupRef, handleHelperRef]);
|
|
308
298
|
const editClickHandlers = useClickValid(!!editMode && !isLocked, (event) => {
|
|
309
299
|
onSelect === null || onSelect === void 0 ? void 0 : onSelect(nodeId);
|
|
310
300
|
onClick === null || onClick === void 0 ? void 0 : onClick(event, gameObject);
|
|
@@ -318,7 +308,6 @@ function StandardNode({ nodeId, selectedId, onSelect, onClick, registerRef, load
|
|
|
318
308
|
},
|
|
319
309
|
}
|
|
320
310
|
: undefined;
|
|
321
|
-
useHelper(editMode && isSelected ? helperRef : null, BoxHelper, "cyan");
|
|
322
311
|
const world = parentMatrix.clone().multiply(compose(gameObject));
|
|
323
312
|
const physics = findComponent(gameObject, "Physics");
|
|
324
313
|
const ready = isNodeReady(gameObject, loadedModels);
|
|
@@ -337,8 +326,8 @@ function StandardNode({ nodeId, selectedId, onSelect, onClick, registerRef, load
|
|
|
337
326
|
const childNodes = _jsx(ChildNodes, { childIds: childIds, parentMatrix: world, selectedId: selectedId, onSelect: onSelect, onClick: onClick, registerRef: registerRef, loadedModels: loadedModels, editMode: editMode });
|
|
338
327
|
const inner = renderCompositionNode(gameObject, renderCtx, primaryClickHandlers, childNodes);
|
|
339
328
|
const editAnchor = editMode ? (_jsx("mesh", { visible: false, children: _jsx("boxGeometry", { args: [0.01, 0.01, 0.01] }) })) : null;
|
|
340
|
-
const standardNode = (_jsxs("group", Object.assign({ ref:
|
|
341
|
-
const physicsNode = hasPhysics && (physicsDef === null || physicsDef === void 0 ? void 0 : physicsDef.View) ? (_jsx(physicsDef.View, Object.assign({ properties: physics.properties }, transformProps, { children: _jsxs("group", Object.assign({ ref:
|
|
329
|
+
const standardNode = (_jsxs("group", Object.assign({ ref: handleGroupRef }, groupProps, (editMode ? editClickHandlers : undefined), { children: [editAnchor, inner] })));
|
|
330
|
+
const physicsNode = hasPhysics && (physicsDef === null || physicsDef === void 0 ? void 0 : physicsDef.View) ? (_jsx(physicsDef.View, Object.assign({ properties: physics.properties }, transformProps, { children: _jsxs("group", Object.assign({ ref: handleGroupRef }, metadataProps, (editMode ? editClickHandlers : undefined), { children: [editAnchor, inner] })) }), physicsKey)) : null;
|
|
342
331
|
return (_jsx(EntityRuntimeScope, { nodeId: nodeId, editMode: editMode, isSelected: isSelected, children: physicsNode !== null && physicsNode !== void 0 ? physicsNode : standardNode }));
|
|
343
332
|
}
|
|
344
333
|
function isRendererHandledComponent(componentType) {
|
|
@@ -11,7 +11,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
11
11
|
};
|
|
12
12
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
13
13
|
import { BallCollider, CapsuleCollider, CuboidCollider, RigidBody, useRapier } from "@react-three/rapier";
|
|
14
|
-
import { useCallback, useEffect, useRef } from 'react';
|
|
14
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
15
15
|
import { useAssetRuntime, useEntityRuntime } from "../assetRuntime";
|
|
16
16
|
import { gameEvents, getEntityIdFromRigidBody } from "../GameEvents";
|
|
17
17
|
import { usePrefabNode, usePrefabStore } from "../prefabStore";
|
|
@@ -27,6 +27,7 @@ const colliderSizeFallback = [1, 1, 1];
|
|
|
27
27
|
const colliderRadiusFallback = 0.5;
|
|
28
28
|
const capsuleRadiusFallback = 0.35;
|
|
29
29
|
const capsuleHalfHeightFallback = 0.45;
|
|
30
|
+
const EDIT_MODE_DEBUG_REFRESH_THROTTLE_MS = 120;
|
|
30
31
|
function isManualColliderShape(value) {
|
|
31
32
|
return value === 'cuboid' || value === 'ball' || value === 'capsule';
|
|
32
33
|
}
|
|
@@ -146,11 +147,12 @@ function PhysicsComponentView({ properties, children, position, rotation, scale
|
|
|
146
147
|
? 'capsule'
|
|
147
148
|
: resolvedManualColliderShape;
|
|
148
149
|
const rigidBodyRef = useRef(null);
|
|
150
|
+
const [editRefreshVersion, setEditRefreshVersion] = useState(0);
|
|
151
|
+
const lastEditRefreshAtRef = useRef(0);
|
|
149
152
|
const linearVelocityKey = linearVelocity.join(',');
|
|
150
153
|
const angularVelocityKey = angularVelocity.join(',');
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
: `${type || 'dynamic'}_${colliderType}_${resolvedManualColliderShape}_${colliderSize.join(',')}_${colliderRadius}_${capsuleRadius}_${capsuleHalfHeight}`;
|
|
154
|
+
const transformSignature = `${position === null || position === void 0 ? void 0 : position.join(',')}_${rotation === null || rotation === void 0 ? void 0 : rotation.join(',')}_${scale === null || scale === void 0 ? void 0 : scale.join(',')}`;
|
|
155
|
+
const rbKey = `${type || 'dynamic'}_${colliderType}_${resolvedManualColliderShape}_${colliderSize.join(',')}_${colliderRadius}_${capsuleRadius}_${capsuleHalfHeight}_${editMode ? editRefreshVersion : 0}`;
|
|
154
156
|
const handleRigidBodyRef = useCallback((rigidBody) => {
|
|
155
157
|
rigidBodyRef.current = rigidBody;
|
|
156
158
|
if (!nodeId)
|
|
@@ -179,6 +181,23 @@ function PhysicsComponentView({ properties, children, position, rotation, scale
|
|
|
179
181
|
}
|
|
180
182
|
}
|
|
181
183
|
}, [activeCollisionTypes, rapier, type, colliders]);
|
|
184
|
+
useEffect(() => {
|
|
185
|
+
if (!editMode) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const now = Date.now();
|
|
189
|
+
const delay = Math.max(0, EDIT_MODE_DEBUG_REFRESH_THROTTLE_MS - (now - lastEditRefreshAtRef.current));
|
|
190
|
+
if (delay === 0) {
|
|
191
|
+
lastEditRefreshAtRef.current = now;
|
|
192
|
+
setEditRefreshVersion(version => version + 1);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const timeoutId = setTimeout(() => {
|
|
196
|
+
lastEditRefreshAtRef.current = Date.now();
|
|
197
|
+
setEditRefreshVersion(version => version + 1);
|
|
198
|
+
}, delay);
|
|
199
|
+
return () => clearTimeout(timeoutId);
|
|
200
|
+
}, [editMode, transformSignature]);
|
|
182
201
|
// Seed authored velocities when the body instance changes or the authored values change.
|
|
183
202
|
useEffect(() => {
|
|
184
203
|
if (!rigidBodyRef.current)
|
|
@@ -238,14 +257,10 @@ function PhysicsComponentView({ properties, children, position, rotation, scale
|
|
|
238
257
|
return;
|
|
239
258
|
dispatchPhysicsEvent(collisionExitEventName, payload);
|
|
240
259
|
}, [collisionExitEventName, dispatchPhysicsEvent, emitCollisionExitEvent]);
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
scale,
|
|
246
|
-
}
|
|
247
|
-
: undefined;
|
|
248
|
-
const rigidBodyProps = Object.assign({ ref: handleRigidBodyRef, type, colliders: usesAutomaticColliderSource ? colliderType : false, position: editMode ? undefined : position, rotation: editMode ? undefined : rotation, scale: editMode ? undefined : scale, sensor,
|
|
260
|
+
const rigidBodyProps = Object.assign({ ref: handleRigidBodyRef, type, colliders: usesAutomaticColliderSource ? colliderType : false, position,
|
|
261
|
+
rotation,
|
|
262
|
+
scale,
|
|
263
|
+
sensor,
|
|
249
264
|
enabledTranslations,
|
|
250
265
|
enabledRotations, name: nodeName, userData: Object.assign({ entityId: nodeId }, userData), onIntersectionEnter: emitSensorEnterEvent ? handleIntersectionEnter : undefined, onIntersectionExit: emitSensorExitEvent ? handleIntersectionExit : undefined, onCollisionEnter: emitCollisionEnterEvent ? handleCollisionEnter : undefined, onCollisionExit: emitCollisionExitEvent ? handleCollisionExit : undefined }, otherProps);
|
|
251
266
|
const rigidBodyContent = (_jsxs(_Fragment, { children: [!usesAutomaticColliderSource ? renderManualCollider({
|
|
@@ -256,7 +271,7 @@ function PhysicsComponentView({ properties, children, position, rotation, scale
|
|
|
256
271
|
capsuleRadius,
|
|
257
272
|
capsuleHalfHeight,
|
|
258
273
|
}) : null, children] }));
|
|
259
|
-
return (_jsx(RigidBody, Object.assign({}, rigidBodyProps, { children:
|
|
274
|
+
return (_jsx(RigidBody, Object.assign({}, rigidBodyProps, { children: rigidBodyContent }), rbKey));
|
|
260
275
|
}
|
|
261
276
|
const PhysicsComponent = {
|
|
262
277
|
name: 'Physics',
|