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
|
@@ -7,19 +7,33 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
11
|
+
var t = {};
|
|
12
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
13
|
+
t[p] = s[p];
|
|
14
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
15
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
16
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
17
|
+
t[p[i]] = s[p[i]];
|
|
18
|
+
}
|
|
19
|
+
return t;
|
|
20
|
+
};
|
|
10
21
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
11
22
|
import { useHelper } from "@react-three/drei";
|
|
12
23
|
import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
|
|
13
24
|
import { BoxHelper, Euler, Matrix4, Quaternion, Vector3, } from "three";
|
|
25
|
+
import { useStore } from "zustand";
|
|
14
26
|
import { getComponent, registerComponent } from "./components/ComponentRegistry";
|
|
15
27
|
import components from "./components";
|
|
16
28
|
import { loadModel, loadTexture } from "../dragdrop";
|
|
17
29
|
import { GameInstance, GameInstanceProvider, getRepeatAxesFromModelProperties, useInstanceCheck } from "./InstanceProvider";
|
|
18
30
|
import { decompose } from "./utils";
|
|
31
|
+
import { createPrefabStore, PrefabStoreProvider, prefabStoreToPrefab, usePrefabChildIds, usePrefabNode, usePrefabRootId } from "./prefabStore";
|
|
19
32
|
components.forEach(registerComponent);
|
|
20
33
|
const IDENTITY = new Matrix4();
|
|
21
|
-
|
|
22
|
-
|
|
34
|
+
const EMPTY_MODELS = {};
|
|
35
|
+
const EMPTY_TEXTURES = {};
|
|
36
|
+
export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSelect, onClick, onSelectedObjectChange, onFocusNode, basePath = "", injectedModels = EMPTY_MODELS, injectedTextures = EMPTY_TEXTURES }, ref) => {
|
|
23
37
|
const [models, setModels] = useState({});
|
|
24
38
|
const [textures, setTextures] = useState({});
|
|
25
39
|
const loading = useRef(new Set());
|
|
@@ -27,6 +41,9 @@ export const PrefabRoot = forwardRef(({ editMode, data, selectedId, onSelect, on
|
|
|
27
41
|
const objectRefs = useRef({});
|
|
28
42
|
const rigidBodyRefs = useRef(new Map());
|
|
29
43
|
const rootRef = useRef(null);
|
|
44
|
+
const [internalStore] = useState(() => { var _a; return createPrefabStore(data !== null && data !== void 0 ? data : prefabStoreToPrefab((_a = store === null || store === void 0 ? void 0 : store.getState()) !== null && _a !== void 0 ? _a : missingStoreState())); });
|
|
45
|
+
const prefabStore = store !== null && store !== void 0 ? store : internalStore;
|
|
46
|
+
const assetManifestKey = useStore(prefabStore, state => state.assetManifestKey);
|
|
30
47
|
const availableModels = useMemo(() => (Object.assign(Object.assign({}, models), injectedModels)), [models, injectedModels]);
|
|
31
48
|
const availableTextures = useMemo(() => (Object.assign(Object.assign({}, textures), injectedTextures)), [textures, injectedTextures]);
|
|
32
49
|
useImperativeHandle(ref, () => ({
|
|
@@ -54,68 +71,78 @@ export const PrefabRoot = forwardRef(({ editMode, data, selectedId, onSelect, on
|
|
|
54
71
|
return () => { console.error = originalError; };
|
|
55
72
|
}, []);
|
|
56
73
|
useEffect(() => {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
((
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
if (!store && data) {
|
|
75
|
+
prefabStore.getState().replacePrefab(data);
|
|
76
|
+
}
|
|
77
|
+
}, [data, prefabStore, store]);
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
const syncAssets = (snapshot = prefabStore.getState()) => {
|
|
80
|
+
const modelsToLoad = new Set();
|
|
81
|
+
const texturesToLoad = new Set();
|
|
82
|
+
Object.values(snapshot.nodesById).forEach(node => {
|
|
83
|
+
var _a;
|
|
84
|
+
Object.values((_a = node.components) !== null && _a !== void 0 ? _a : {}).forEach(component => {
|
|
85
|
+
var _a, _b, _c, _d;
|
|
86
|
+
if (!(component === null || component === void 0 ? void 0 : component.type))
|
|
87
|
+
return;
|
|
88
|
+
if (component.type === 'Model' && ((_a = component.properties) === null || _a === void 0 ? void 0 : _a.filename)) {
|
|
89
|
+
modelsToLoad.add(component.properties.filename);
|
|
90
|
+
}
|
|
91
|
+
if (component.type === 'Material') {
|
|
92
|
+
((_b = component.properties) === null || _b === void 0 ? void 0 : _b.texture) && texturesToLoad.add(component.properties.texture);
|
|
93
|
+
((_c = component.properties) === null || _c === void 0 ? void 0 : _c.normalMapTexture) && texturesToLoad.add(component.properties.normalMapTexture);
|
|
94
|
+
}
|
|
95
|
+
if (component.type === 'SpotLight' && ((_d = component.properties) === null || _d === void 0 ? void 0 : _d.map)) {
|
|
96
|
+
texturesToLoad.add(component.properties.map);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
75
99
|
});
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const path = file.startsWith("/")
|
|
82
|
-
? `${basePath}${file}`
|
|
83
|
-
: `${basePath}/${file}`;
|
|
84
|
-
const res = yield loadModel(path);
|
|
85
|
-
const model = res.model;
|
|
86
|
-
if (res.success && model) {
|
|
87
|
-
setModels(m => (Object.assign(Object.assign({}, m), { [file]: model })));
|
|
88
|
-
}
|
|
89
|
-
}));
|
|
90
|
-
texturesToLoad.forEach(file => {
|
|
91
|
-
if (availableTextures[file] || loading.current.has(file) || failedTextures.current.has(file))
|
|
92
|
-
return;
|
|
93
|
-
loading.current.add(file);
|
|
94
|
-
// Handle full URLs (http/https) or regular paths
|
|
95
|
-
const path = file.startsWith("http://") || file.startsWith("https://")
|
|
96
|
-
? file
|
|
97
|
-
: file.startsWith("/")
|
|
100
|
+
modelsToLoad.forEach((file) => __awaiter(void 0, void 0, void 0, function* () {
|
|
101
|
+
if (models[file] || injectedModels[file] || loading.current.has(file))
|
|
102
|
+
return;
|
|
103
|
+
loading.current.add(file);
|
|
104
|
+
const path = file.startsWith("/")
|
|
98
105
|
? `${basePath}${file}`
|
|
99
106
|
: `${basePath}/${file}`;
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
107
|
+
const res = yield loadModel(path);
|
|
108
|
+
const model = res.model;
|
|
109
|
+
if (res.success && model) {
|
|
110
|
+
setModels(m => (Object.assign(Object.assign({}, m), { [file]: model })));
|
|
104
111
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
failedTextures.current.
|
|
112
|
+
}));
|
|
113
|
+
texturesToLoad.forEach(file => {
|
|
114
|
+
if (textures[file] || injectedTextures[file] || loading.current.has(file) || failedTextures.current.has(file))
|
|
115
|
+
return;
|
|
116
|
+
loading.current.add(file);
|
|
117
|
+
// Handle full URLs (http/https) or regular paths
|
|
118
|
+
const path = file.startsWith("http://") || file.startsWith("https://")
|
|
119
|
+
? file
|
|
120
|
+
: file.startsWith("/")
|
|
121
|
+
? `${basePath}${file}`
|
|
122
|
+
: `${basePath}/${file}`;
|
|
123
|
+
void loadTexture(path).then(result => {
|
|
124
|
+
if (result.success && result.texture) {
|
|
125
|
+
setTextures(t => (Object.assign(Object.assign({}, t), { [file]: result.texture })));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
console.warn(`Failed to load texture: ${path}`, result.error);
|
|
129
|
+
loading.current.delete(file);
|
|
130
|
+
failedTextures.current.add(file);
|
|
131
|
+
});
|
|
108
132
|
});
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
|
|
133
|
+
};
|
|
134
|
+
syncAssets();
|
|
135
|
+
}, [prefabStore, assetManifestKey, basePath, injectedModels, injectedTextures]);
|
|
136
|
+
return (_jsx(PrefabStoreProvider, { store: prefabStore, children: _jsx("group", { ref: rootRef, children: _jsx(GameInstanceProvider, { models: availableModels, selectedId: selectedId, editMode: editMode, onSelect: editMode ? onSelect : undefined, registerRef: registerRef, children: _jsx(StoreRootNode, { selectedId: selectedId, onSelect: editMode ? onSelect : undefined, onClick: onClick, registerRef: registerRef, registerRigidBodyRef: registerRigidBodyRef, loadedModels: availableModels, loadedTextures: availableTextures, editMode: editMode, parentMatrix: IDENTITY }) }) }) }));
|
|
112
137
|
});
|
|
138
|
+
function StoreRootNode(props) {
|
|
139
|
+
const rootId = usePrefabRootId();
|
|
140
|
+
return _jsx(GameObjectRenderer, Object.assign({}, props, { nodeId: rootId }));
|
|
141
|
+
}
|
|
113
142
|
export function GameObjectRenderer(props) {
|
|
114
143
|
var _a, _b, _c;
|
|
115
|
-
const node = props.
|
|
116
|
-
|
|
117
|
-
return null;
|
|
118
|
-
const isInstanced = (_c = (_b = (_a = node.components) === null || _a === void 0 ? void 0 : _a.model) === null || _b === void 0 ? void 0 : _b.properties) === null || _c === void 0 ? void 0 : _c.instanced;
|
|
144
|
+
const node = usePrefabNode(props.nodeId);
|
|
145
|
+
const isInstanced = (_c = (_b = (_a = node === null || node === void 0 ? void 0 : node.components) === null || _a === void 0 ? void 0 : _a.model) === null || _b === void 0 ? void 0 : _b.properties) === null || _c === void 0 ? void 0 : _c.instanced;
|
|
119
146
|
const prevInstancedRef = useRef(undefined);
|
|
120
147
|
const [isTransitioning, setIsTransitioning] = useState(false);
|
|
121
148
|
useEffect(() => {
|
|
@@ -126,9 +153,9 @@ export function GameObjectRenderer(props) {
|
|
|
126
153
|
}
|
|
127
154
|
prevInstancedRef.current = isInstanced;
|
|
128
155
|
}, [isInstanced]);
|
|
129
|
-
if (isTransitioning)
|
|
156
|
+
if (!node || node.disabled || isTransitioning)
|
|
130
157
|
return null;
|
|
131
|
-
const key = `${
|
|
158
|
+
const key = `${props.nodeId}_${isInstanced ? 'instanced' : 'standard'}`;
|
|
132
159
|
return isInstanced
|
|
133
160
|
? _jsx(InstancedNode, Object.assign({}, props), key)
|
|
134
161
|
: _jsx(StandardNode, Object.assign({}, props), key);
|
|
@@ -136,8 +163,11 @@ export function GameObjectRenderer(props) {
|
|
|
136
163
|
function isPhysicsProps(v) {
|
|
137
164
|
return (v === null || v === void 0 ? void 0 : v.type) === "fixed" || (v === null || v === void 0 ? void 0 : v.type) === "dynamic" || (v === null || v === void 0 ? void 0 : v.type) === "kinematicPosition" || (v === null || v === void 0 ? void 0 : v.type) === "kinematicVelocity";
|
|
138
165
|
}
|
|
139
|
-
function InstancedNode({
|
|
166
|
+
function InstancedNode({ nodeId, parentMatrix = IDENTITY, editMode, registerRef, onSelect, onClick }) {
|
|
140
167
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
168
|
+
const gameObject = usePrefabNode(nodeId);
|
|
169
|
+
if (!gameObject)
|
|
170
|
+
return null;
|
|
141
171
|
const localTransform = getNodeTransformProps(gameObject);
|
|
142
172
|
const isLocked = Boolean(gameObject.locked);
|
|
143
173
|
const clickable = Object.values((_a = gameObject.components) !== null && _a !== void 0 ? _a : {}).some(component => (component === null || component === void 0 ? void 0 : component.type) === 'Click');
|
|
@@ -150,15 +180,15 @@ function InstancedNode({ gameObject, parentMatrix = IDENTITY, editMode, register
|
|
|
150
180
|
const clickValid = useRef(false);
|
|
151
181
|
useEffect(() => {
|
|
152
182
|
if (editMode) {
|
|
153
|
-
registerRef(
|
|
154
|
-
return () => registerRef(
|
|
183
|
+
registerRef(nodeId, groupRef.current);
|
|
184
|
+
return () => registerRef(nodeId, null);
|
|
155
185
|
}
|
|
156
|
-
}, [
|
|
186
|
+
}, [nodeId, registerRef, editMode]);
|
|
157
187
|
if (editMode) {
|
|
158
188
|
return (_jsxs(_Fragment, { children: [_jsx("group", { ref: groupRef, position: localTransform.position, rotation: localTransform.rotation, scale: localTransform.scale, onPointerDown: isLocked ? undefined : (e) => { e.stopPropagation(); clickValid.current = true; }, onPointerMove: isLocked ? undefined : () => { clickValid.current = false; }, onPointerUp: isLocked ? undefined : (e) => {
|
|
159
189
|
if (clickValid.current) {
|
|
160
190
|
e.stopPropagation();
|
|
161
|
-
onSelect === null || onSelect === void 0 ? void 0 : onSelect(
|
|
191
|
+
onSelect === null || onSelect === void 0 ? void 0 : onSelect(nodeId);
|
|
162
192
|
onClick === null || onClick === void 0 ? void 0 : onClick(e, gameObject);
|
|
163
193
|
}
|
|
164
194
|
clickValid.current = false;
|
|
@@ -166,19 +196,23 @@ function InstancedNode({ gameObject, parentMatrix = IDENTITY, editMode, register
|
|
|
166
196
|
}
|
|
167
197
|
return (_jsx(_Fragment, { children: instances.map(instance => (_jsx(GameInstance, { id: instance.id, sourceId: gameObject.id, clickable: clickable, modelUrl: instance.modelUrl, position: instance.position, rotation: instance.rotation, scale: instance.scale, locked: isLocked, physics: instance.physics }, instance.id))) }));
|
|
168
198
|
}
|
|
169
|
-
function StandardNode({
|
|
199
|
+
function StandardNode({ nodeId, selectedId, onSelect, onClick, registerRef, registerRigidBodyRef, loadedModels, loadedTextures, editMode, parentMatrix = IDENTITY, }) {
|
|
170
200
|
var _a, _b, _c, _d, _e;
|
|
201
|
+
const gameObject = usePrefabNode(nodeId);
|
|
202
|
+
const childIds = usePrefabChildIds(nodeId);
|
|
203
|
+
const isSelected = selectedId === nodeId;
|
|
204
|
+
if (!gameObject)
|
|
205
|
+
return null;
|
|
171
206
|
const groupRef = useRef(null);
|
|
172
207
|
const helperRef = useRef(null);
|
|
173
208
|
const clickValid = useRef(false);
|
|
174
209
|
const isLocked = Boolean(gameObject.locked);
|
|
175
|
-
const
|
|
176
|
-
const stillInstanced = useInstanceCheck(gameObject.id);
|
|
210
|
+
const stillInstanced = useInstanceCheck(nodeId);
|
|
177
211
|
useHelper(editMode && isSelected ? helperRef : null, BoxHelper, "cyan");
|
|
178
212
|
useEffect(() => {
|
|
179
|
-
registerRef(
|
|
180
|
-
return () => registerRef(
|
|
181
|
-
}, [
|
|
213
|
+
registerRef(nodeId, groupRef.current);
|
|
214
|
+
return () => registerRef(nodeId, null);
|
|
215
|
+
}, [nodeId, registerRef]);
|
|
182
216
|
const world = parentMatrix.clone().multiply(compose(gameObject));
|
|
183
217
|
const onDown = (e) => {
|
|
184
218
|
e.stopPropagation();
|
|
@@ -187,7 +221,7 @@ function StandardNode({ gameObject, selectedId, onSelect, onClick, registerRef,
|
|
|
187
221
|
const onUp = (e) => {
|
|
188
222
|
if (clickValid.current) {
|
|
189
223
|
e.stopPropagation();
|
|
190
|
-
onSelect === null || onSelect === void 0 ? void 0 : onSelect(
|
|
224
|
+
onSelect === null || onSelect === void 0 ? void 0 : onSelect(nodeId);
|
|
191
225
|
onClick === null || onClick === void 0 ? void 0 : onClick(e, gameObject);
|
|
192
226
|
}
|
|
193
227
|
clickValid.current = false;
|
|
@@ -199,51 +233,35 @@ function StandardNode({ gameObject, selectedId, onSelect, onClick, registerRef,
|
|
|
199
233
|
const transform = getNodeTransformProps(gameObject);
|
|
200
234
|
const physicsDef = hasPhysics ? getComponent("Physics") : null;
|
|
201
235
|
const isInstanced = (_e = (_d = (_c = gameObject.components) === null || _c === void 0 ? void 0 : _c.model) === null || _d === void 0 ? void 0 : _d.properties) === null || _e === void 0 ? void 0 : _e.instanced;
|
|
202
|
-
const physicsKey = `physics_${
|
|
203
|
-
const renderCtx =
|
|
236
|
+
const physicsKey = `physics_${nodeId}_${isInstanced ? 'instanced' : 'standard'}`;
|
|
237
|
+
const renderCtx = { loadedModels, loadedTextures, editMode, registerRef };
|
|
204
238
|
const childNodes = getChildHostComponents(gameObject).length > 0
|
|
205
|
-
?
|
|
206
|
-
:
|
|
207
|
-
|
|
208
|
-
onSelect,
|
|
209
|
-
onClick,
|
|
210
|
-
registerRef,
|
|
211
|
-
registerRigidBodyRef,
|
|
212
|
-
loadedModels,
|
|
213
|
-
loadedTextures,
|
|
214
|
-
editMode,
|
|
215
|
-
});
|
|
216
|
-
const inner = (_jsx("group", { onPointerDown: editMode && !isLocked ? onDown : undefined, onPointerMove: editMode && !isLocked ? () => (clickValid.current = false) : undefined, onPointerUp: editMode && !isLocked ? onUp : undefined, children: renderCompositionNode(gameObject, renderCtx, parentMatrix, childNodes) }));
|
|
239
|
+
? _jsx(CompositionChildren, { childIds: childIds, selectedId: selectedId, ctx: renderCtx, parentMatrix: world })
|
|
240
|
+
: _jsx(ChildNodes, { childIds: childIds, parentMatrix: world, selectedId: selectedId, onSelect: onSelect, onClick: onClick, registerRef: registerRef, registerRigidBodyRef: registerRigidBodyRef, loadedModels: loadedModels, loadedTextures: loadedTextures, editMode: editMode });
|
|
241
|
+
const inner = (_jsx("group", { onPointerDown: editMode && !isLocked ? onDown : undefined, onPointerMove: editMode && !isLocked ? () => (clickValid.current = false) : undefined, onPointerUp: editMode && !isLocked ? onUp : undefined, children: renderCompositionNode(gameObject, renderCtx, isSelected, parentMatrix, childNodes) }));
|
|
217
242
|
if (editMode) {
|
|
218
|
-
return (_jsxs(_Fragment, { children: [_jsx("group", { ref: groupRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: _jsx("mesh", { visible: false, children: _jsx("boxGeometry", { args: [0.01, 0.01, 0.01] }) }) }), _jsx("group", { ref: helperRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: inner }), hasPhysics && (physicsDef === null || physicsDef === void 0 ? void 0 : physicsDef.View) ? (_jsx(physicsDef.View, { properties: physics.properties, position: transform.position, rotation: transform.rotation, scale: transform.scale, editMode: editMode, nodeId:
|
|
243
|
+
return (_jsxs(_Fragment, { children: [_jsx("group", { ref: groupRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: _jsx("mesh", { visible: false, children: _jsx("boxGeometry", { args: [0.01, 0.01, 0.01] }) }) }), _jsx("group", { ref: helperRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: inner }), hasPhysics && (physicsDef === null || physicsDef === void 0 ? void 0 : physicsDef.View) ? (_jsx(physicsDef.View, { properties: physics.properties, position: transform.position, rotation: transform.rotation, scale: transform.scale, editMode: editMode, nodeId: nodeId, registerRigidBodyRef: registerRigidBodyRef, children: inner }, physicsKey)) : null] }));
|
|
219
244
|
}
|
|
220
245
|
if (hasPhysics && (physicsDef === null || physicsDef === void 0 ? void 0 : physicsDef.View)) {
|
|
221
|
-
return (_jsx(physicsDef.View, { properties: physics.properties, position: transform.position, rotation: transform.rotation, scale: transform.scale, editMode: editMode, nodeId:
|
|
246
|
+
return (_jsx(physicsDef.View, { properties: physics.properties, position: transform.position, rotation: transform.rotation, scale: transform.scale, editMode: editMode, nodeId: nodeId, registerRigidBodyRef: registerRigidBodyRef, children: inner }, physicsKey));
|
|
222
247
|
}
|
|
223
248
|
return (_jsx("group", { ref: groupRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: inner }));
|
|
224
249
|
}
|
|
250
|
+
const LEAF_COMPONENT_TYPES = new Set(["Geometry", "Material", "Physics"]);
|
|
225
251
|
function getChildHostComponents(gameObject) {
|
|
226
252
|
var _a;
|
|
227
253
|
return Object.entries((_a = gameObject.components) !== null && _a !== void 0 ? _a : {}).flatMap(([key, comp]) => {
|
|
228
254
|
if (!(comp === null || comp === void 0 ? void 0 : comp.type))
|
|
229
255
|
return [];
|
|
230
256
|
const def = getComponent(comp.type);
|
|
231
|
-
if (!(def === null || def === void 0 ? void 0 : def.View) ||
|
|
257
|
+
if (!(def === null || def === void 0 ? void 0 : def.View) || LEAF_COMPONENT_TYPES.has(comp.type))
|
|
232
258
|
return [];
|
|
233
259
|
return { key, View: def.View, properties: comp.properties };
|
|
234
260
|
});
|
|
235
261
|
}
|
|
236
|
-
function
|
|
237
|
-
|
|
238
|
-
}
|
|
239
|
-
function renderSceneChildren(gameObject, parentMatrix, props) {
|
|
240
|
-
var _a;
|
|
241
|
-
return (_a = gameObject.children) === null || _a === void 0 ? void 0 : _a.map(child => _jsx(GameObjectRenderer, { gameObject: child, selectedId: props.selectedId, onSelect: props.onSelect, onClick: props.onClick, registerRef: props.registerRef, registerRigidBodyRef: props.registerRigidBodyRef, loadedModels: props.loadedModels, loadedTextures: props.loadedTextures, editMode: props.editMode, parentMatrix: parentMatrix }, child.id));
|
|
242
|
-
}
|
|
243
|
-
function walk(node, fn) {
|
|
244
|
-
var _a;
|
|
245
|
-
fn(node);
|
|
246
|
-
(_a = node.children) === null || _a === void 0 ? void 0 : _a.forEach(c => walk(c, fn));
|
|
262
|
+
function ChildNodes(_a) {
|
|
263
|
+
var { childIds, parentMatrix } = _a, props = __rest(_a, ["childIds", "parentMatrix"]);
|
|
264
|
+
return childIds.map(childId => _jsx(GameObjectRenderer, Object.assign({ nodeId: childId, parentMatrix: parentMatrix }, props), childId));
|
|
247
265
|
}
|
|
248
266
|
function compose(node) {
|
|
249
267
|
const { position, rotation, scale } = getNodeTransformProps(node);
|
|
@@ -310,62 +328,59 @@ function getNodeTransformProps(node) {
|
|
|
310
328
|
scale: (_e = t === null || t === void 0 ? void 0 : t.scale) !== null && _e !== void 0 ? _e : [1, 1, 1],
|
|
311
329
|
};
|
|
312
330
|
}
|
|
313
|
-
function renderCompositionSubtree(gameObject, ctx, parentMatrix = IDENTITY) {
|
|
331
|
+
function renderCompositionSubtree(gameObject, ctx, isSelected, childIds, parentMatrix = IDENTITY) {
|
|
314
332
|
if (!gameObject || gameObject.disabled)
|
|
315
333
|
return null;
|
|
316
334
|
const transform = getNodeTransformProps(gameObject);
|
|
317
335
|
const world = parentMatrix.clone().multiply(compose(gameObject));
|
|
318
|
-
const childNodes =
|
|
319
|
-
return (_jsx("group", { position: transform.position, rotation: transform.rotation, scale: transform.scale, children: renderCompositionNode(gameObject, ctx, parentMatrix, childNodes) }, gameObject.id));
|
|
336
|
+
const childNodes = _jsx(CompositionChildren, { childIds: childIds, ctx: ctx, parentMatrix: world });
|
|
337
|
+
return (_jsx("group", { position: transform.position, rotation: transform.rotation, scale: transform.scale, children: renderCompositionNode(gameObject, ctx, isSelected, parentMatrix, childNodes) }, gameObject.id));
|
|
320
338
|
}
|
|
321
|
-
function
|
|
322
|
-
|
|
323
|
-
|
|
339
|
+
function CompositionChildren({ childIds, selectedId, ctx, parentMatrix, }) {
|
|
340
|
+
return childIds.map(childId => (_jsx(CompositionSubtree, { nodeId: childId, selectedId: selectedId, ctx: ctx, parentMatrix: parentMatrix }, childId)));
|
|
341
|
+
}
|
|
342
|
+
function CompositionSubtree({ nodeId, selectedId, ctx, parentMatrix, }) {
|
|
343
|
+
const gameObject = usePrefabNode(nodeId);
|
|
344
|
+
const childIds = usePrefabChildIds(nodeId);
|
|
345
|
+
const isSelected = selectedId === nodeId;
|
|
346
|
+
if (!gameObject)
|
|
347
|
+
return null;
|
|
348
|
+
return renderCompositionSubtree(gameObject, ctx, isSelected, childIds, parentMatrix);
|
|
324
349
|
}
|
|
325
|
-
function renderCompositionNode(gameObject, ctx, parentMatrix, childNodes) {
|
|
326
|
-
const ownContent = renderNodeOwnContent(gameObject, ctx, parentMatrix);
|
|
327
|
-
return wrapWithChildHosts(gameObject, ctx, parentMatrix, _jsxs(_Fragment, { children: [ownContent, childNodes] }));
|
|
350
|
+
function renderCompositionNode(gameObject, ctx, isSelected, parentMatrix, childNodes) {
|
|
351
|
+
const ownContent = renderNodeOwnContent(gameObject, ctx, isSelected, parentMatrix);
|
|
352
|
+
return wrapWithChildHosts(gameObject, ctx, isSelected, parentMatrix, _jsxs(_Fragment, { children: [ownContent, childNodes] }));
|
|
328
353
|
}
|
|
329
|
-
function renderNodeOwnContent(gameObject, ctx, parentMatrix) {
|
|
330
|
-
var _a, _b
|
|
354
|
+
function renderNodeOwnContent(gameObject, ctx, isSelected, parentMatrix) {
|
|
355
|
+
var _a, _b;
|
|
331
356
|
const geometry = (_a = gameObject.components) === null || _a === void 0 ? void 0 : _a.geometry;
|
|
332
357
|
const material = (_b = gameObject.components) === null || _b === void 0 ? void 0 : _b.material;
|
|
333
|
-
const model = (_c = gameObject.components) === null || _c === void 0 ? void 0 : _c.model;
|
|
334
|
-
const text = (_d = gameObject.components) === null || _d === void 0 ? void 0 : _d.text;
|
|
335
358
|
const geometryDef = geometry && getComponent("Geometry");
|
|
336
359
|
const materialDef = material && getComponent("Material");
|
|
337
|
-
const modelDef = model && getComponent("Model");
|
|
338
|
-
const textDef = text && getComponent("Text");
|
|
339
360
|
const contextProps = {
|
|
340
361
|
loadedModels: ctx.loadedModels,
|
|
341
362
|
loadedTextures: ctx.loadedTextures,
|
|
342
363
|
editMode: ctx.editMode,
|
|
343
|
-
isSelected
|
|
364
|
+
isSelected,
|
|
344
365
|
nodeId: gameObject.id,
|
|
345
366
|
parentMatrix,
|
|
346
367
|
registerRef: ctx.registerRef,
|
|
347
368
|
};
|
|
348
369
|
let core;
|
|
349
|
-
if (
|
|
350
|
-
core = (_jsx(modelDef.View, Object.assign({ properties: model.properties }, contextProps, { children: material && (materialDef === null || materialDef === void 0 ? void 0 : materialDef.View) && (_jsx(materialDef.View, Object.assign({ properties: material.properties }, contextProps), "material")) })));
|
|
351
|
-
}
|
|
352
|
-
else if (geometry && (geometryDef === null || geometryDef === void 0 ? void 0 : geometryDef.View)) {
|
|
370
|
+
if (geometry && (geometryDef === null || geometryDef === void 0 ? void 0 : geometryDef.View)) {
|
|
353
371
|
core = (_jsxs("mesh", { castShadow: true, receiveShadow: true, children: [_jsx(geometryDef.View, Object.assign({ properties: geometry.properties }, contextProps)), material && (materialDef === null || materialDef === void 0 ? void 0 : materialDef.View) && (_jsx(materialDef.View, Object.assign({ properties: material.properties }, contextProps), "material"))] }));
|
|
354
372
|
}
|
|
355
|
-
else if (text && (textDef === null || textDef === void 0 ? void 0 : textDef.View)) {
|
|
356
|
-
core = (_jsx(_Fragment, { children: _jsx(textDef.View, Object.assign({ properties: text.properties }, contextProps)) }));
|
|
357
|
-
}
|
|
358
373
|
else {
|
|
359
374
|
core = null;
|
|
360
375
|
}
|
|
361
376
|
return core;
|
|
362
377
|
}
|
|
363
|
-
function wrapWithChildHosts(gameObject, ctx, parentMatrix, subtree) {
|
|
378
|
+
function wrapWithChildHosts(gameObject, ctx, isSelected, parentMatrix, subtree) {
|
|
364
379
|
const contextProps = {
|
|
365
380
|
loadedModels: ctx.loadedModels,
|
|
366
381
|
loadedTextures: ctx.loadedTextures,
|
|
367
382
|
editMode: ctx.editMode,
|
|
368
|
-
isSelected
|
|
383
|
+
isSelected,
|
|
369
384
|
nodeId: gameObject.id,
|
|
370
385
|
parentMatrix,
|
|
371
386
|
registerRef: ctx.registerRef,
|
|
@@ -374,3 +389,6 @@ function wrapWithChildHosts(gameObject, ctx, parentMatrix, subtree) {
|
|
|
374
389
|
return childHosts.reduce((acc, { key, View, properties }) => (_jsx(View, Object.assign({ properties: properties }, contextProps, { children: acc }), key)), subtree);
|
|
375
390
|
}
|
|
376
391
|
export default PrefabRoot;
|
|
392
|
+
function missingStoreState() {
|
|
393
|
+
throw new Error("PrefabRoot requires either data or store");
|
|
394
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { ColorField, FieldGroup, NumberField } from "./Input";
|
|
3
3
|
function AmbientLightComponentEditor({ component, onUpdate, }) {
|
|
4
4
|
return (_jsxs(FieldGroup, { children: [_jsx(ColorField, { name: "color", label: "Color", values: component.properties, onChange: onUpdate }), _jsx(NumberField, { name: "intensity", label: "Intensity", values: component.properties, onChange: onUpdate, min: 0, step: 0.1, fallback: 1 })] }));
|
|
5
5
|
}
|
|
6
|
-
function AmbientLightComponentView({ properties }) {
|
|
6
|
+
function AmbientLightComponentView({ properties, children }) {
|
|
7
7
|
const { color = '#ffffff', intensity = 1 } = properties;
|
|
8
|
-
return _jsx("ambientLight", { color: color, intensity: intensity });
|
|
8
|
+
return (_jsxs(_Fragment, { children: [_jsx("ambientLight", { color: color, intensity: intensity }), children] }));
|
|
9
9
|
}
|
|
10
10
|
const AmbientLightComponent = {
|
|
11
11
|
name: 'AmbientLight',
|
|
@@ -14,7 +14,7 @@ function CameraComponentEditor({ component, onUpdate }) {
|
|
|
14
14
|
const values = Object.assign(Object.assign({}, cameraDefaults), component.properties);
|
|
15
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 })] }));
|
|
16
16
|
}
|
|
17
|
-
function CameraComponentView({ properties, editMode, isSelected }) {
|
|
17
|
+
function CameraComponentView({ properties, children, editMode, isSelected }) {
|
|
18
18
|
const merged = Object.assign(Object.assign({}, cameraDefaults), properties);
|
|
19
19
|
const fov = merged.fov;
|
|
20
20
|
const near = merged.near;
|
|
@@ -34,7 +34,7 @@ function CameraComponentView({ properties, editMode, isSelected }) {
|
|
|
34
34
|
cameraHelper.update();
|
|
35
35
|
}
|
|
36
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 ? (_jsxs("group", { children: [_jsxs("mesh", { children: [_jsx("boxGeometry", { args: [0.3, 0.3, 0.5] }), _jsx("meshBasicMaterial", { color: '#22d3ee', wireframe: true })] }), _jsxs("mesh", { position: [0, 0, -0.25], rotation: [Math.PI / 2, 0, 0], children: [_jsx("coneGeometry", { args: [0.08, 0.16, 16] }), _jsx("meshBasicMaterial", { color: '#22d3ee', wireframe: true })] })] })) : null] }));
|
|
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 ? (_jsxs("group", { children: [_jsxs("mesh", { children: [_jsx("boxGeometry", { args: [0.3, 0.3, 0.5] }), _jsx("meshBasicMaterial", { color: '#22d3ee', wireframe: true })] }), _jsxs("mesh", { position: [0, 0, -0.25], rotation: [Math.PI / 2, 0, 0], children: [_jsx("coneGeometry", { args: [0.08, 0.16, 16] }), _jsx("meshBasicMaterial", { color: '#22d3ee', wireframe: true })] })] })) : null, children] }));
|
|
38
38
|
}
|
|
39
39
|
const CameraComponent = {
|
|
40
40
|
name: 'Camera',
|
|
@@ -48,7 +48,7 @@ function DirectionalLightComponentEditor({ component, onUpdate }) {
|
|
|
48
48
|
: directionalLightFields.filter(field => field.name !== '_shadowCamera');
|
|
49
49
|
return (_jsx(FieldRenderer, { fields: fields, values: values, onChange: onUpdate }));
|
|
50
50
|
}
|
|
51
|
-
function DirectionalLightView({ properties, editMode, isSelected }) {
|
|
51
|
+
function DirectionalLightView({ properties, children, editMode, isSelected }) {
|
|
52
52
|
const merged = Object.assign(Object.assign({}, directionalLightDefaults), properties);
|
|
53
53
|
const color = merged.color;
|
|
54
54
|
const intensity = merged.intensity;
|
|
@@ -93,7 +93,7 @@ function DirectionalLightView({ properties, editMode, isSelected }) {
|
|
|
93
93
|
new Vector3(targetOffset[0], targetOffset[1], targetOffset[2])
|
|
94
94
|
];
|
|
95
95
|
geo.setFromPoints(points);
|
|
96
|
-
} }), _jsx("lineBasicMaterial", { color: color, opacity: 0.6, transparent: true })] })] }))] }));
|
|
96
|
+
} }), _jsx("lineBasicMaterial", { color: color, opacity: 0.6, transparent: true })] })] })), children] }));
|
|
97
97
|
}
|
|
98
98
|
const DirectionalLightComponent = {
|
|
99
99
|
name: 'DirectionalLight',
|
|
@@ -16,7 +16,7 @@ function SpotLightComponentEditor({ component, onUpdate, basePath = "" }) {
|
|
|
16
16
|
const values = Object.assign(Object.assign({}, spotLightDefaults), component.properties);
|
|
17
17
|
return (_jsxs(FieldGroup, { children: [_jsx(ColorField, { name: "color", label: "Color", values: values, onChange: onUpdate }), _jsx(NumberField, { name: "intensity", label: "Intensity", values: values, onChange: onUpdate, min: 0, step: 1, fallback: 200 }), _jsx(NumberField, { name: "angle", label: "Angle", values: values, onChange: onUpdate, min: 0, max: Math.PI, step: 0.05, fallback: 0.5 }), _jsx(NumberField, { name: "penumbra", label: "Penumbra", values: values, onChange: onUpdate, min: 0, max: 1, step: 0.05, fallback: 0.5 }), _jsx(NumberField, { name: "distance", label: "Distance", values: values, onChange: onUpdate, min: 0, step: 1, fallback: 100 }), _jsx(BooleanField, { name: "castShadow", label: "Cast Shadow", values: values, onChange: onUpdate, fallback: true }), _jsxs("div", { children: [_jsx(Label, { children: "Texture Map" }), _jsx(TexturePicker, { value: values.map, onChange: (map) => onUpdate({ map }), basePath: basePath })] })] }));
|
|
18
18
|
}
|
|
19
|
-
function SpotLightView({ properties, editMode, isSelected, loadedTextures }) {
|
|
19
|
+
function SpotLightView({ properties, children, editMode, isSelected, loadedTextures }) {
|
|
20
20
|
const merged = Object.assign(Object.assign({}, spotLightDefaults), properties);
|
|
21
21
|
const color = merged.color;
|
|
22
22
|
const intensity = merged.intensity;
|
|
@@ -45,7 +45,7 @@ function SpotLightView({ properties, editMode, isSelected, loadedTextures }) {
|
|
|
45
45
|
spotLightHelper.update();
|
|
46
46
|
}
|
|
47
47
|
});
|
|
48
|
-
return (_jsxs(_Fragment, { children: [_jsx("spotLight", { ref: spotLightRef, color: color, intensity: intensity, angle: angle, penumbra: penumbra, distance: distance, map: textureMap, castShadow: castShadow, "shadow-mapSize-width": 1024, "shadow-mapSize-height": 1024, "shadow-bias": -0.0001, "shadow-normalBias": 0.02 }), editMode && isSelected && spotLightHelper && (_jsx("primitive", { object: spotLightHelper })), _jsx("object3D", { ref: targetRef, position: [0, -5, 0] }), editMode && (_jsxs(_Fragment, { children: [_jsxs("mesh", { children: [_jsx("sphereGeometry", { args: [0.2, 8, 6] }), _jsx("meshBasicMaterial", { color: color, wireframe: true })] }), _jsxs("mesh", { position: [0, -5, 0], children: [_jsx("sphereGeometry", { args: [0.15, 8, 6] }), _jsx("meshBasicMaterial", { color: color, wireframe: true, opacity: 0.5, transparent: true })] })] }))] }));
|
|
48
|
+
return (_jsxs(_Fragment, { children: [_jsx("spotLight", { ref: spotLightRef, color: color, intensity: intensity, angle: angle, penumbra: penumbra, distance: distance, map: textureMap, castShadow: castShadow, "shadow-mapSize-width": 1024, "shadow-mapSize-height": 1024, "shadow-bias": -0.0001, "shadow-normalBias": 0.02 }), editMode && isSelected && spotLightHelper && (_jsx("primitive", { object: spotLightHelper })), _jsx("object3D", { ref: targetRef, position: [0, -5, 0] }), editMode && (_jsxs(_Fragment, { children: [_jsxs("mesh", { children: [_jsx("sphereGeometry", { args: [0.2, 8, 6] }), _jsx("meshBasicMaterial", { color: color, wireframe: true })] }), _jsxs("mesh", { position: [0, -5, 0], children: [_jsx("sphereGeometry", { args: [0.15, 8, 6] }), _jsx("meshBasicMaterial", { color: color, wireframe: true, opacity: 0.5, transparent: true })] })] })), children] }));
|
|
49
49
|
}
|
|
50
50
|
const SpotLightComponent = {
|
|
51
51
|
name: 'SpotLight',
|
|
@@ -11,7 +11,7 @@ function TextComponentEditor({ component, onUpdate, }) {
|
|
|
11
11
|
{ value: 'right', label: 'Right' },
|
|
12
12
|
] })] }));
|
|
13
13
|
}
|
|
14
|
-
function TextComponentView({ properties }) {
|
|
14
|
+
function TextComponentView({ properties, children }) {
|
|
15
15
|
const { text = '', font, size, depth, width, align, color } = properties;
|
|
16
16
|
const textContent = String(text || '');
|
|
17
17
|
const meshRef = useRef(null);
|
|
@@ -37,13 +37,12 @@ function TextComponentView({ properties }) {
|
|
|
37
37
|
}, [align]);
|
|
38
38
|
if (!textContent)
|
|
39
39
|
return null;
|
|
40
|
-
return (
|
|
40
|
+
return (_jsxs("group", { position: offset, children: [_jsx(Text, { ref: meshRef, font: font, size: size, depth: depth, layout: { align, width }, color: color, onLoad: handleLoad, children: textContent }), children] }));
|
|
41
41
|
}
|
|
42
42
|
const TextComponent = {
|
|
43
43
|
name: 'Text',
|
|
44
44
|
Editor: TextComponentEditor,
|
|
45
45
|
View: TextComponentView,
|
|
46
|
-
nonComposable: true,
|
|
47
46
|
defaultProperties: {
|
|
48
47
|
text: 'Hello World',
|
|
49
48
|
color: '#888888',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Label,
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Label, Vector3Input } from "./Input";
|
|
3
3
|
import { useEditorContext } from "../EditorContext";
|
|
4
4
|
import { colors } from "../styles";
|
|
5
5
|
const buttonStyle = {
|
|
@@ -13,8 +13,8 @@ const buttonStyle = {
|
|
|
13
13
|
fontSize: 11,
|
|
14
14
|
flex: 1,
|
|
15
15
|
};
|
|
16
|
-
function TransformModeSelector({ transformMode, setTransformMode
|
|
17
|
-
return (_jsxs("div", { style: { marginBottom: 8 }, children: [
|
|
16
|
+
function TransformModeSelector({ transformMode, setTransformMode }) {
|
|
17
|
+
return (_jsxs("div", { style: { marginBottom: 8 }, children: [_jsx(Label, { children: "Transform Mode" }), _jsx("div", { style: { display: 'flex', gap: 6 }, children: ["translate", "rotate", "scale"].map(mode => {
|
|
18
18
|
const isActive = transformMode === mode;
|
|
19
19
|
return (_jsx("button", { onClick: () => setTransformMode(mode), style: Object.assign(Object.assign({}, buttonStyle), { background: isActive ? colors.accentBg : colors.bgSurface, borderColor: isActive ? colors.accentBorder : colors.border, color: isActive ? colors.accent : colors.text }), onPointerEnter: (e) => {
|
|
20
20
|
if (!isActive)
|
|
@@ -23,13 +23,7 @@ function TransformModeSelector({ transformMode, setTransformMode, snapResolution
|
|
|
23
23
|
if (!isActive)
|
|
24
24
|
e.currentTarget.style.background = colors.bgSurface;
|
|
25
25
|
}, children: mode }, mode));
|
|
26
|
-
}) })
|
|
27
|
-
if (snapResolution === 0)
|
|
28
|
-
e.currentTarget.style.background = colors.bgHover;
|
|
29
|
-
}, onPointerLeave: (e) => {
|
|
30
|
-
if (snapResolution === 0)
|
|
31
|
-
e.currentTarget.style.background = colors.bgSurface;
|
|
32
|
-
}, children: ["Snap: ", snapResolution > 0 ? `ON (${snapResolution})` : 'OFF'] }) })] }));
|
|
26
|
+
}) })] }));
|
|
33
27
|
}
|
|
34
28
|
const snapLockBtnStyle = {
|
|
35
29
|
background: 'none',
|
|
@@ -44,11 +38,12 @@ function SnapLockButton({ locked, onToggle, title }) {
|
|
|
44
38
|
return (_jsx("button", { style: snapLockBtnStyle, onClick: onToggle, title: title, children: locked ? '🔒' : '🔓' }));
|
|
45
39
|
}
|
|
46
40
|
function TransformComponentEditor({ component, onUpdate }) {
|
|
47
|
-
var _a, _b;
|
|
48
|
-
const { transformMode, setTransformMode,
|
|
41
|
+
var _a, _b, _c;
|
|
42
|
+
const { transformMode, setTransformMode, scaleSnap, setScaleSnap, positionSnap, setPositionSnap, rotationSnap, setRotationSnap } = useEditorContext();
|
|
43
|
+
const scaleSnapped = scaleSnap > 0;
|
|
49
44
|
const positionSnapped = positionSnap > 0;
|
|
50
45
|
const rotationSnapped = rotationSnap > 0;
|
|
51
|
-
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column' }, children: [_jsx(TransformModeSelector, { transformMode: transformMode, setTransformMode: setTransformMode
|
|
46
|
+
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column' }, children: [_jsx(TransformModeSelector, { transformMode: transformMode, setTransformMode: setTransformMode }), _jsx(Vector3Input, { label: "Position", value: (_a = component.properties.position) !== null && _a !== void 0 ? _a : [0, 0, 0], onChange: v => onUpdate({ position: v }), snap: positionSnap, labelExtra: _jsx(SnapLockButton, { locked: positionSnapped, onToggle: () => setPositionSnap(positionSnapped ? 0 : 0.5), title: positionSnapped ? `Snap ON (0.5) — click to disable` : `Snap OFF — click to enable (0.5)` }) }), _jsx(Vector3Input, { label: "Rotation", value: (_b = component.properties.rotation) !== null && _b !== void 0 ? _b : [0, 0, 0], onChange: v => onUpdate({ rotation: v }), snap: rotationSnap, labelExtra: _jsx(SnapLockButton, { locked: rotationSnapped, onToggle: () => setRotationSnap(rotationSnapped ? 0 : Math.PI / 4), title: rotationSnapped ? `Snap ON (π/4) — click to disable` : `Snap OFF — click to enable (π/4)` }) }), _jsx(Vector3Input, { label: "Scale", value: (_c = component.properties.scale) !== null && _c !== void 0 ? _c : [1, 1, 1], onChange: v => onUpdate({ scale: v }), snap: scaleSnap, labelExtra: _jsx(SnapLockButton, { locked: scaleSnapped, onToggle: () => setScaleSnap(scaleSnapped ? 0 : 0.1), title: scaleSnapped ? `Snap ON (0.1) — click to disable` : `Snap OFF — click to enable (0.1)` }) })] }));
|
|
52
47
|
}
|
|
53
48
|
const TransformComponent = {
|
|
54
49
|
name: 'Transform',
|