react-three-game 0.0.93 → 0.0.94
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 +35 -77
- package/dist/index.d.ts +9 -6
- package/dist/index.js +5 -3
- package/dist/tools/prefabeditor/EditorTree.js +8 -12
- package/dist/tools/prefabeditor/EditorUI.js +4 -4
- package/dist/tools/prefabeditor/PrefabEditor.d.ts +12 -33
- package/dist/tools/prefabeditor/PrefabEditor.js +137 -161
- package/dist/tools/prefabeditor/PrefabRoot.d.ts +24 -12
- package/dist/tools/prefabeditor/PrefabRoot.js +60 -41
- package/dist/tools/prefabeditor/assetRuntime.d.ts +11 -17
- package/dist/tools/prefabeditor/assetRuntime.js +15 -15
- package/dist/tools/prefabeditor/components/CameraComponent.js +2 -2
- package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +2 -2
- package/dist/tools/prefabeditor/components/Input.js +5 -9
- package/dist/tools/prefabeditor/components/ModelComponent.js +3 -5
- package/dist/tools/prefabeditor/components/PointLightComponent.js +2 -2
- package/dist/tools/prefabeditor/components/SoundComponent.js +2 -2
- package/dist/tools/prefabeditor/components/SpotLightComponent.js +2 -2
- package/dist/tools/prefabeditor/prefab.d.ts +1 -2
- package/dist/tools/prefabeditor/prefab.js +2 -3
- package/dist/tools/prefabeditor/prefabStore.d.ts +0 -6
- package/dist/tools/prefabeditor/prefabStore.js +1 -33
- package/package.json +49 -49
|
@@ -9,8 +9,8 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
9
9
|
}
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
|
-
import { jsx as _jsx,
|
|
13
|
-
import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
|
|
12
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
13
|
+
import { createContext, forwardRef, useCallback, useContext, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
|
|
14
14
|
import { Euler, Matrix4, } from "three";
|
|
15
15
|
import { useStore } from "zustand";
|
|
16
16
|
import { useClickValid } from "./useClickValid";
|
|
@@ -20,8 +20,8 @@ import { builtinComponents } from "./components";
|
|
|
20
20
|
import { loadModel, loadSound, loadTexture } from "../dragdrop";
|
|
21
21
|
import { GameInstance, GameInstanceProvider, getRepeatAxesFromModelProperties, useInstanceCheck } from "./InstanceProvider";
|
|
22
22
|
import { composeTransform, decompose } from "./utils";
|
|
23
|
-
import { createPrefabStore, PrefabStoreProvider,
|
|
24
|
-
import { AssetRuntimeContext,
|
|
23
|
+
import { createPrefabStore, PrefabStoreProvider, usePrefabChildIds, usePrefabNode, usePrefabRootId } from "./prefabStore";
|
|
24
|
+
import { AssetRuntimeContext, NodeScope } from "./assetRuntime";
|
|
25
25
|
import { gameEvents } from "./GameEvents";
|
|
26
26
|
import { sound as soundManager } from "../../helpers/SoundManager";
|
|
27
27
|
builtinComponents.forEach(registerComponent);
|
|
@@ -50,8 +50,20 @@ function getNodeMetadataProps(node) {
|
|
|
50
50
|
userData: Object.assign(Object.assign({ prefabNodeId: node.id }, (nodeName ? { prefabNodeName: nodeName } : {})), getNodeUserData(node)),
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
|
-
export
|
|
54
|
-
|
|
53
|
+
export var PrefabEditorMode;
|
|
54
|
+
(function (PrefabEditorMode) {
|
|
55
|
+
PrefabEditorMode["Edit"] = "edit";
|
|
56
|
+
PrefabEditorMode["Play"] = "play";
|
|
57
|
+
})(PrefabEditorMode || (PrefabEditorMode = {}));
|
|
58
|
+
export const SceneContext = createContext(null);
|
|
59
|
+
export function useScene() {
|
|
60
|
+
const scene = useContext(SceneContext);
|
|
61
|
+
if (!scene) {
|
|
62
|
+
throw new Error("useScene must be used within a PrefabRoot or PrefabEditor scene provider");
|
|
63
|
+
}
|
|
64
|
+
return scene;
|
|
65
|
+
}
|
|
66
|
+
export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSelect, onClick, onEditNodeClick, basePath = "", children }, ref) => {
|
|
55
67
|
const [models, setModels] = useState({});
|
|
56
68
|
const [textures, setTextures] = useState({});
|
|
57
69
|
const [sounds, setSounds] = useState({});
|
|
@@ -64,31 +76,33 @@ export const PrefabRootInternal = forwardRef(({ editMode, data, store, selectedI
|
|
|
64
76
|
const failedSounds = useRef(new Set());
|
|
65
77
|
const objectRefs = useRef({});
|
|
66
78
|
const nodeHandles = useRef(new Map());
|
|
67
|
-
const parentStore = useOptionalPrefabStoreApi();
|
|
68
79
|
const [ownedStore] = useState(() => {
|
|
80
|
+
if (store)
|
|
81
|
+
return null;
|
|
69
82
|
if (data)
|
|
70
83
|
return createPrefabStore(data);
|
|
71
|
-
if (store || parentStore)
|
|
72
|
-
return null;
|
|
73
84
|
throw new Error("PrefabRoot requires either a `data` or `store` prop");
|
|
74
85
|
});
|
|
75
|
-
const resolvedStore =
|
|
86
|
+
const resolvedStore = store !== null && store !== void 0 ? store : ownedStore;
|
|
76
87
|
const usesOwnedStore = resolvedStore === ownedStore;
|
|
77
|
-
const shouldProvideStoreContext = !parentStore || parentStore !== resolvedStore;
|
|
78
88
|
const rootId = useStore(resolvedStore, state => state.rootId);
|
|
79
89
|
const assetManifestKey = useStore(resolvedStore, state => state.assetManifestKey);
|
|
80
90
|
const availableModels = useMemo(() => (Object.assign(Object.assign({}, models), injectedModels)), [models, injectedModels]);
|
|
81
91
|
const availableTextures = useMemo(() => (Object.assign(Object.assign({}, textures), injectedTextures)), [textures, injectedTextures]);
|
|
82
92
|
const availableSounds = useMemo(() => (Object.assign(Object.assign({}, sounds), injectedSounds)), [sounds, injectedSounds]);
|
|
83
|
-
const
|
|
93
|
+
const getObject = useCallback((id) => {
|
|
84
94
|
var _a;
|
|
85
95
|
return (_a = objectRefs.current[id]) !== null && _a !== void 0 ? _a : null;
|
|
86
96
|
}, []);
|
|
87
|
-
const
|
|
97
|
+
const getHandle = useCallback((id, kind) => {
|
|
88
98
|
var _a, _b;
|
|
89
99
|
return (_b = (_a = nodeHandles.current.get(id)) === null || _a === void 0 ? void 0 : _a.get(kind)) !== null && _b !== void 0 ? _b : null;
|
|
90
100
|
}, []);
|
|
91
|
-
const
|
|
101
|
+
const getNode = useCallback((nodeId) => {
|
|
102
|
+
var _a;
|
|
103
|
+
return (_a = resolvedStore.getState().nodesById[nodeId]) !== null && _a !== void 0 ? _a : null;
|
|
104
|
+
}, [resolvedStore]);
|
|
105
|
+
const registerHandle = useCallback((id, kind, handle) => {
|
|
92
106
|
const current = nodeHandles.current.get(id);
|
|
93
107
|
if (handle == null) {
|
|
94
108
|
if (!current)
|
|
@@ -105,20 +119,33 @@ export const PrefabRootInternal = forwardRef(({ editMode, data, store, selectedI
|
|
|
105
119
|
}
|
|
106
120
|
nodeHandles.current.set(id, new Map([[kind, handle]]));
|
|
107
121
|
}, []);
|
|
108
|
-
|
|
122
|
+
const sceneValue = useMemo(() => ({
|
|
109
123
|
get root() {
|
|
110
124
|
var _a;
|
|
111
125
|
return (_a = objectRefs.current[rootId]) !== null && _a !== void 0 ? _a : null;
|
|
112
126
|
},
|
|
113
|
-
|
|
114
|
-
|
|
127
|
+
mode: editMode ? PrefabEditorMode.Edit : PrefabEditorMode.Play,
|
|
128
|
+
get: getNode,
|
|
129
|
+
getObject,
|
|
130
|
+
getHandle,
|
|
131
|
+
add: (node, parentId) => {
|
|
132
|
+
const state = resolvedStore.getState();
|
|
133
|
+
state.addChild(parentId !== null && parentId !== void 0 ? parentId : state.rootId, node);
|
|
134
|
+
return node;
|
|
135
|
+
},
|
|
136
|
+
update: (id, fn) => resolvedStore.getState().updateNode(id, fn),
|
|
137
|
+
remove: (id) => resolvedStore.getState().deleteNode(id),
|
|
138
|
+
duplicate: (id) => resolvedStore.getState().duplicateNode(id),
|
|
139
|
+
move: (draggedId, targetId, position) => resolvedStore.getState().moveNode(draggedId, targetId, position),
|
|
140
|
+
replace: (prefab) => resolvedStore.getState().replacePrefab(prefab),
|
|
115
141
|
addModel: (path, model) => setInjectedModels(prev => (Object.assign(Object.assign({}, prev), { [path]: model }))),
|
|
116
142
|
addTexture: (path, texture) => setInjectedTextures(prev => (Object.assign(Object.assign({}, prev), { [path]: texture }))),
|
|
117
143
|
addSound: (path, sound) => {
|
|
118
144
|
soundManager.setBuffer(path, sound);
|
|
119
145
|
setInjectedSounds(prev => (Object.assign(Object.assign({}, prev), { [path]: sound })));
|
|
120
146
|
},
|
|
121
|
-
}), [
|
|
147
|
+
}), [editMode, getHandle, getNode, getObject, resolvedStore, rootId]);
|
|
148
|
+
useImperativeHandle(ref, () => sceneValue, [sceneValue]);
|
|
122
149
|
const registerRef = useCallback((id, obj) => {
|
|
123
150
|
objectRefs.current[id] = obj;
|
|
124
151
|
}, []);
|
|
@@ -153,31 +180,27 @@ export const PrefabRootInternal = forwardRef(({ editMode, data, store, selectedI
|
|
|
153
180
|
return;
|
|
154
181
|
loading.current.add(file);
|
|
155
182
|
void loader(resolveAssetPath(basePath, file)).then(result => {
|
|
183
|
+
loading.current.delete(file);
|
|
156
184
|
if (!result.success) {
|
|
157
185
|
console.warn(`Failed to load asset: ${file}`, result.error);
|
|
158
|
-
loading.current.delete(file);
|
|
159
186
|
failed.add(file);
|
|
160
187
|
}
|
|
161
188
|
});
|
|
162
189
|
};
|
|
163
|
-
modelsToLoad.forEach(file => loadAsset(file, models, injectedModels, failedModels.current,
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
setModels(m => (Object.assign(Object.assign({}, m), { [file]: model })));
|
|
167
|
-
}
|
|
190
|
+
modelsToLoad.forEach(file => loadAsset(file, models, injectedModels, failedModels.current, path => loadModel(path).then(result => {
|
|
191
|
+
if (result.success && result.model)
|
|
192
|
+
setModels(m => (Object.assign(Object.assign({}, m), { [file]: result.model })));
|
|
168
193
|
return result;
|
|
169
194
|
})));
|
|
170
|
-
texturesToLoad.forEach(file => loadAsset(file, textures, injectedTextures, failedTextures.current,
|
|
171
|
-
if (result.success && result.texture)
|
|
195
|
+
texturesToLoad.forEach(file => loadAsset(file, textures, injectedTextures, failedTextures.current, path => loadTexture(path).then(result => {
|
|
196
|
+
if (result.success && result.texture)
|
|
172
197
|
setTextures(t => (Object.assign(Object.assign({}, t), { [file]: result.texture })));
|
|
173
|
-
}
|
|
174
198
|
return result;
|
|
175
199
|
})));
|
|
176
|
-
soundsToLoad.forEach(file => loadAsset(file, sounds, injectedSounds, failedSounds.current,
|
|
200
|
+
soundsToLoad.forEach(file => loadAsset(file, sounds, injectedSounds, failedSounds.current, path => loadSound(path).then(result => {
|
|
177
201
|
if (result.success && result.sound) {
|
|
178
202
|
soundManager.setBuffer(file, result.sound);
|
|
179
|
-
setSounds(
|
|
180
|
-
loading.current.delete(file);
|
|
203
|
+
setSounds(s => (Object.assign(Object.assign({}, s), { [file]: result.sound })));
|
|
181
204
|
}
|
|
182
205
|
return result;
|
|
183
206
|
})));
|
|
@@ -185,14 +208,14 @@ export const PrefabRootInternal = forwardRef(({ editMode, data, store, selectedI
|
|
|
185
208
|
syncAssets();
|
|
186
209
|
}, [resolvedStore, assetManifestKey, basePath, injectedModels, injectedSounds, injectedTextures, models, sounds, textures]);
|
|
187
210
|
const assetRuntime = useMemo(() => ({
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
211
|
+
registerHandle,
|
|
212
|
+
getHandle,
|
|
213
|
+
getObject,
|
|
191
214
|
getModel: (path) => { var _a; return (_a = availableModels[path]) !== null && _a !== void 0 ? _a : null; },
|
|
192
215
|
getTexture: (path) => { var _a; return (_a = availableTextures[path]) !== null && _a !== void 0 ? _a : null; },
|
|
193
216
|
getSound: (path) => { var _a; return (_a = availableSounds[path]) !== null && _a !== void 0 ? _a : null; },
|
|
194
217
|
getAssetRevision: () => `${Object.keys(availableTextures).sort().join('|')}::${Object.keys(availableModels).sort().join('|')}`,
|
|
195
|
-
}), [
|
|
218
|
+
}), [registerHandle, getHandle, getObject, availableModels, availableTextures, availableSounds]);
|
|
196
219
|
const handleNodeClick = useCallback((event, nodeId, fallbackObject) => {
|
|
197
220
|
const node = resolvedStore.getState().nodesById[nodeId];
|
|
198
221
|
if (!node)
|
|
@@ -201,14 +224,10 @@ export const PrefabRootInternal = forwardRef(({ editMode, data, store, selectedI
|
|
|
201
224
|
emitNodePointerEvent(clickEventName, event, nodeId, node, fallbackObject);
|
|
202
225
|
onClick === null || onClick === void 0 ? void 0 : onClick(event, node);
|
|
203
226
|
}, [onClick, resolvedStore]);
|
|
204
|
-
const content = (
|
|
205
|
-
const runtimeContent = _jsx(AssetRuntimeContext.Provider, { value: assetRuntime, children: content });
|
|
206
|
-
if (!shouldProvideStoreContext) {
|
|
207
|
-
return runtimeContent;
|
|
208
|
-
}
|
|
227
|
+
const content = (_jsxs(GameInstanceProvider, { models: availableModels, selectedId: selectedId, editMode: editMode, onSelect: editMode ? onSelect : undefined, onClick: editMode ? undefined : handleNodeClick, registerRef: registerRef, children: [_jsx(StoreRootNode, { selectedId: selectedId, onSelect: editMode ? onSelect : undefined, onClick: editMode ? undefined : handleNodeClick, onEditNodeClick: editMode ? onEditNodeClick : undefined, registerRef: registerRef, loadedModels: availableModels, editMode: editMode, parentMatrix: IDENTITY }), children] }));
|
|
228
|
+
const runtimeContent = (_jsx(SceneContext.Provider, { value: sceneValue, children: _jsx(AssetRuntimeContext.Provider, { value: assetRuntime, children: content }) }));
|
|
209
229
|
return _jsx(PrefabStoreProvider, { store: resolvedStore, children: runtimeContent });
|
|
210
230
|
});
|
|
211
|
-
export const PrefabRoot = PrefabRootInternal;
|
|
212
231
|
function StoreRootNode(props) {
|
|
213
232
|
const rootId = usePrefabRootId();
|
|
214
233
|
return _jsx(GameObjectRenderer, Object.assign({}, props, { nodeId: rootId }));
|
|
@@ -379,7 +398,7 @@ function StandardNode({ nodeId, selectedId, onSelect, onClick, onEditNodeClick,
|
|
|
379
398
|
const inner = renderNodeContent(analyzedComponents, loadedModels, primaryClickHandlers, childNodes);
|
|
380
399
|
const editAnchor = editMode ? (_jsx("mesh", { visible: false, children: _jsx("boxGeometry", { args: [0.01, 0.01, 0.01] }) })) : null;
|
|
381
400
|
const standardNode = (_jsxs("group", Object.assign({ ref: handleGroupRef }, groupProps, { visible: nodeVisible }, (editMode ? editClickHandlers : undefined), { children: [editAnchor, inner] })));
|
|
382
|
-
return (_jsx(
|
|
401
|
+
return (_jsx(NodeScope, { nodeId: nodeId, editMode: editMode, isSelected: isSelected, children: standardNode }));
|
|
383
402
|
}
|
|
384
403
|
function ChildNodes(_a) {
|
|
385
404
|
var { childIds, parentMatrix } = _a, props = __rest(_a, ["childIds", "parentMatrix"]);
|
|
@@ -1,36 +1,30 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
2
|
import type { Object3D, Texture } from "three";
|
|
3
3
|
export interface AssetRuntime {
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
registerHandle: (id: string, kind: string, handle: unknown) => void;
|
|
5
|
+
getHandle: <T = unknown>(id: string, kind: string) => T | null;
|
|
6
6
|
getModel: (path: string) => Object3D | null;
|
|
7
7
|
getTexture: (path: string) => Texture | null;
|
|
8
8
|
getSound: (path: string) => AudioBuffer | null;
|
|
9
9
|
getAssetRevision: () => string;
|
|
10
|
-
|
|
10
|
+
getObject: (id: string) => Object3D | null;
|
|
11
11
|
}
|
|
12
|
-
export interface
|
|
13
|
-
}
|
|
14
|
-
export interface CurrentNodeRuntime {
|
|
12
|
+
export interface NodeApi {
|
|
15
13
|
nodeId: string;
|
|
16
14
|
editMode?: boolean;
|
|
17
15
|
isSelected?: boolean;
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
getObject: <T extends Object3D = Object3D>() => T | null;
|
|
17
|
+
getHandle: <T = unknown>(kind: string) => T | null;
|
|
20
18
|
}
|
|
21
19
|
export interface LiveRef<T> {
|
|
22
20
|
readonly current: T | null;
|
|
23
21
|
}
|
|
24
|
-
export
|
|
25
|
-
export type LiveHandleRef<T = unknown> = LiveRef<T>;
|
|
26
|
-
export type CurrentNodeObjectRef<T extends Object3D = Object3D> = LiveObjectRef<T>;
|
|
27
|
-
export type CurrentNodeHandleRef<T = unknown> = LiveHandleRef<T>;
|
|
28
|
-
export declare const AssetRuntimeContext: import("react").Context<AssetRuntimeContextValue | null>;
|
|
22
|
+
export declare const AssetRuntimeContext: import("react").Context<AssetRuntime | null>;
|
|
29
23
|
export declare function useAssetRuntime(): AssetRuntime;
|
|
30
|
-
export declare function
|
|
31
|
-
export declare function
|
|
32
|
-
export declare function
|
|
33
|
-
export declare function
|
|
24
|
+
export declare function useNode(): NodeApi;
|
|
25
|
+
export declare function useNodeObject<T extends Object3D = Object3D>(): LiveRef<T>;
|
|
26
|
+
export declare function useNodeHandle<T = unknown>(kind: string): LiveRef<T>;
|
|
27
|
+
export declare function NodeScope({ nodeId, editMode, isSelected, children, }: {
|
|
34
28
|
nodeId: string;
|
|
35
29
|
editMode?: boolean;
|
|
36
30
|
isSelected?: boolean;
|
|
@@ -1,37 +1,37 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { createContext, useContext, useMemo } from "react";
|
|
3
3
|
export const AssetRuntimeContext = createContext(null);
|
|
4
|
-
const
|
|
4
|
+
const NodeContext = createContext(null);
|
|
5
5
|
export function useAssetRuntime() {
|
|
6
6
|
const ctx = useContext(AssetRuntimeContext);
|
|
7
7
|
if (!ctx)
|
|
8
8
|
throw new Error("useAssetRuntime must be used inside <PrefabRoot>");
|
|
9
9
|
return ctx;
|
|
10
10
|
}
|
|
11
|
-
export function
|
|
12
|
-
const ctx = useContext(
|
|
11
|
+
export function useNode() {
|
|
12
|
+
const ctx = useContext(NodeContext);
|
|
13
13
|
if (!ctx)
|
|
14
|
-
throw new Error("
|
|
14
|
+
throw new Error("useNode must be used inside a component View rendered by <PrefabRoot>");
|
|
15
15
|
return ctx;
|
|
16
16
|
}
|
|
17
|
-
export function
|
|
18
|
-
const {
|
|
19
|
-
return useMemo(() => ({ get current() { return
|
|
17
|
+
export function useNodeObject() {
|
|
18
|
+
const { getObject } = useNode();
|
|
19
|
+
return useMemo(() => ({ get current() { return getObject(); } }), [getObject]);
|
|
20
20
|
}
|
|
21
|
-
export function
|
|
22
|
-
const {
|
|
23
|
-
return useMemo(() => ({ get current() { return
|
|
21
|
+
export function useNodeHandle(kind) {
|
|
22
|
+
const { getHandle } = useNode();
|
|
23
|
+
return useMemo(() => ({ get current() { return getHandle(kind); } }), [getHandle, kind]);
|
|
24
24
|
}
|
|
25
|
-
export function
|
|
25
|
+
export function NodeScope({ nodeId, editMode, isSelected, children, }) {
|
|
26
26
|
const asset = useContext(AssetRuntimeContext);
|
|
27
27
|
if (!asset)
|
|
28
|
-
throw new Error("
|
|
28
|
+
throw new Error("NodeScope must be used inside <PrefabRoot>");
|
|
29
29
|
const value = useMemo(() => ({
|
|
30
30
|
nodeId,
|
|
31
31
|
editMode,
|
|
32
32
|
isSelected,
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
getObject: () => asset.getObject(nodeId),
|
|
34
|
+
getHandle: (kind) => asset.getHandle(nodeId, kind),
|
|
35
35
|
}), [asset, editMode, isSelected, nodeId]);
|
|
36
|
-
return _jsx(
|
|
36
|
+
return _jsx(NodeContext.Provider, { value: value, children: children });
|
|
37
37
|
}
|
|
@@ -3,7 +3,7 @@ import { OrthographicCamera, PerspectiveCamera, useHelper } from '@react-three/d
|
|
|
3
3
|
import { useRef } from 'react';
|
|
4
4
|
import { CameraHelper } from 'three';
|
|
5
5
|
import { useFrame, useThree } from '@react-three/fiber';
|
|
6
|
-
import {
|
|
6
|
+
import { useNode } from '../assetRuntime';
|
|
7
7
|
import { FieldGroup, NumberField, SelectField } from './Input';
|
|
8
8
|
const CAMERA_PROJECTION_OPTIONS = [
|
|
9
9
|
{ value: 'perspective', label: 'Perspective' },
|
|
@@ -25,7 +25,7 @@ function CameraComponentEditor({ component, onUpdate }) {
|
|
|
25
25
|
}
|
|
26
26
|
function CameraComponentView({ properties, children }) {
|
|
27
27
|
var _a;
|
|
28
|
-
const { editMode, isSelected } =
|
|
28
|
+
const { editMode, isSelected } = useNode();
|
|
29
29
|
const { size } = useThree();
|
|
30
30
|
const merged = Object.assign(Object.assign({}, cameraDefaults), properties);
|
|
31
31
|
const projection = (_a = merged.projection) !== null && _a !== void 0 ? _a : cameraDefaults.projection;
|
|
@@ -3,7 +3,7 @@ import { useHelper } from "@react-three/drei";
|
|
|
3
3
|
import { useRef, useEffect, useState } from "react";
|
|
4
4
|
import { useFrame } from "@react-three/fiber";
|
|
5
5
|
import { CameraHelper } from "three";
|
|
6
|
-
import {
|
|
6
|
+
import { useNode } from "../assetRuntime";
|
|
7
7
|
import { BooleanField, ColorField, NumberField, NumberInput, Vector3Input } from "./Input";
|
|
8
8
|
import { LightSection, ShadowBiasField, mergeWithDefaults } from "./lightUtils";
|
|
9
9
|
import { colors } from "../styles";
|
|
@@ -102,7 +102,7 @@ function DirectionalLightComponentEditor({ component, onUpdate }) {
|
|
|
102
102
|
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: [_jsxs(LightSection, { title: "Light", children: [_jsx(ColorField, { name: "color", label: "Color", values: values, onChange: onUpdate }), _jsx(NumberField, { name: "intensity", label: "Intensity", values: values, onChange: onUpdate, min: 0, step: 0.1, fallback: 1 }), _jsx(Vector3Input, { label: "Target Offset", value: values.targetOffset, onChange: targetOffset => onUpdate({ targetOffset }), snap: 0.5 })] }), _jsxs(LightSection, { title: "Shadow", children: [_jsx(BooleanField, { name: "castShadow", label: "Cast Shadow", values: values, onChange: onUpdate, fallback: false }), values.castShadow ? (_jsxs(_Fragment, { children: [_jsx(BooleanField, { name: "shadowAutoUpdate", label: "Auto Update", values: values, onChange: onUpdate, fallback: true }), _jsx(NumberField, { name: "shadowMapSize", label: "Map Size", values: values, onChange: onUpdate, min: 128, step: 128, fallback: 512 }), _jsx(ShadowBiasField, { name: "shadowBias", label: "Bias", values: values, onChange: onUpdate, fallback: 0 }), _jsx(ShadowBiasField, { name: "shadowNormalBias", label: "Normal Bias", values: values, onChange: onUpdate, fallback: 0 }), _jsx(NumberField, { name: "shadowCameraNear", label: "Near", values: values, onChange: onUpdate, min: 0.001, step: 0.1, fallback: 0.5 }), _jsx(NumberField, { name: "shadowCameraFar", label: "Far", values: values, onChange: onUpdate, min: 0.1, step: 1, fallback: 500 }), _jsx(ShadowFrustumField, { values: values, onChange: onUpdate })] })) : null] })] }));
|
|
103
103
|
}
|
|
104
104
|
function DirectionalLightView({ properties, children }) {
|
|
105
|
-
const { editMode, isSelected } =
|
|
105
|
+
const { editMode, isSelected } = useNode();
|
|
106
106
|
const merged = mergeWithDefaults(directionalLightDefaults, properties);
|
|
107
107
|
const color = merged.color;
|
|
108
108
|
const intensity = merged.intensity;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { colors, ui } from '../styles';
|
|
4
|
-
import {
|
|
4
|
+
import { usePrefabStoreApi } from '../prefabStore';
|
|
5
5
|
// ============================================================================
|
|
6
6
|
// Shared Styles (derived from shared color tokens)
|
|
7
7
|
// ============================================================================
|
|
@@ -278,14 +278,10 @@ export function ColorInput({ label, value, onChange }) {
|
|
|
278
278
|
export function StringInput({ label, value, onChange, placeholder }) {
|
|
279
279
|
return (_jsxs("div", { children: [label && _jsx(Label, { children: label }), _jsx("input", { type: "text", style: styles.input, value: value, onChange: e => onChange(e.target.value), placeholder: placeholder })] }));
|
|
280
280
|
}
|
|
281
|
-
function
|
|
282
|
-
const store =
|
|
283
|
-
const [state, setState] = useState(() =>
|
|
281
|
+
function usePrefabSnapshot() {
|
|
282
|
+
const store = usePrefabStoreApi();
|
|
283
|
+
const [state, setState] = useState(() => store.getState());
|
|
284
284
|
useEffect(() => {
|
|
285
|
-
if (!store) {
|
|
286
|
-
setState(null);
|
|
287
|
-
return;
|
|
288
|
-
}
|
|
289
285
|
setState(store.getState());
|
|
290
286
|
return store.subscribe(nextState => setState(nextState));
|
|
291
287
|
}, [store]);
|
|
@@ -323,7 +319,7 @@ function SearchSuggestionList({ query, options, onSelect, emptyMessage, }) {
|
|
|
323
319
|
}, children: [_jsx("span", { style: { fontSize: 11, fontWeight: 500 }, children: option.label }), option.description ? (_jsx("span", { style: { fontSize: 10, color: colors.textMuted, fontFamily: 'monospace' }, children: option.description })) : null] }, option.value))) })] }));
|
|
324
320
|
}
|
|
325
321
|
export function NodeInput({ label, value, onChange, placeholder, includeRoot = true, }) {
|
|
326
|
-
const prefabState =
|
|
322
|
+
const prefabState = usePrefabSnapshot();
|
|
327
323
|
const [query, setQuery] = useState('');
|
|
328
324
|
const options = useMemo(() => {
|
|
329
325
|
var _a;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { ModelPicker } from '../../assetviewer/page';
|
|
3
|
-
import {
|
|
3
|
+
import { useMemo } from 'react';
|
|
4
4
|
import { BooleanField, FieldGroup, Label, ListEditor, NumberInput, SelectInput, StringField } from './Input';
|
|
5
5
|
import { useAssetRuntime } from '../assetRuntime';
|
|
6
|
-
import {
|
|
6
|
+
import { useEditorContext } from '../PrefabEditor';
|
|
7
7
|
import { getRepeatAxesFromModelProperties, normalizeRepeatAxes } from '../InstanceProvider';
|
|
8
8
|
import { colors, ui } from '../styles';
|
|
9
9
|
const AXIS_OPTIONS = [
|
|
@@ -39,9 +39,7 @@ function RepeatAxisEditor({ axes, onChange, positionSnap, }) {
|
|
|
39
39
|
} }));
|
|
40
40
|
}
|
|
41
41
|
function ModelComponentEditor({ component, node, onUpdate, basePath = "" }) {
|
|
42
|
-
|
|
43
|
-
const editorContext = useContext(EditorContext);
|
|
44
|
-
const positionSnap = (_a = editorContext === null || editorContext === void 0 ? void 0 : editorContext.positionSnap) !== null && _a !== void 0 ? _a : 0.5;
|
|
42
|
+
const { positionSnap } = useEditorContext();
|
|
45
43
|
const repeatAxes = getRepeatAxesFromModelProperties(component.properties);
|
|
46
44
|
return (_jsxs(FieldGroup, { children: [_jsx(ModelPicker, { value: component.properties.filename, onChange: (filename) => onUpdate({ filename }), basePath: basePath, pickerKey: node === null || node === void 0 ? void 0 : node.id }), _jsx(BooleanField, { name: "instanced", label: "Instanced", values: component.properties, onChange: onUpdate, fallback: false }), !component.properties.instanced ? (_jsxs(_Fragment, { children: [_jsx(BooleanField, { name: "emitClickEvent", label: "Emit Click Event", values: component.properties, onChange: onUpdate, fallback: false }), component.properties.emitClickEvent ? (_jsx(StringField, { name: "clickEventName", label: "Click Event Name", values: component.properties, onChange: onUpdate, placeholder: "node:click" })) : null] })) : null, component.properties.instanced && (_jsxs(_Fragment, { children: [_jsx(BooleanField, { name: "repeat", label: "Repeat", values: component.properties, onChange: onUpdate, fallback: false }), component.properties.repeat && (_jsx(RepeatAxisEditor, { axes: repeatAxes, onChange: (nextAxes) => onUpdate({ repeatAxes: nextAxes }), positionSnap: positionSnap }))] }))] }));
|
|
47
45
|
}
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { useEffect, useRef } from 'react';
|
|
3
3
|
import { useHelper } from '@react-three/drei';
|
|
4
4
|
import { PointLightHelper } from 'three';
|
|
5
|
-
import {
|
|
5
|
+
import { useNode } from '../assetRuntime';
|
|
6
6
|
import { BooleanField, ColorField, NumberField } from './Input';
|
|
7
7
|
import { LightSection, ShadowBiasField, mergeWithDefaults } from './lightUtils';
|
|
8
8
|
const pointLightDefaults = {
|
|
@@ -23,7 +23,7 @@ function PointLightComponentEditor({ component, onUpdate }) {
|
|
|
23
23
|
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: [_jsxs(LightSection, { title: "Light", children: [_jsx(ColorField, { name: "color", label: "Color", values: values, onChange: onUpdate }), _jsx(NumberField, { name: "intensity", label: "Intensity", values: values, onChange: onUpdate, min: 0, step: 0.1, fallback: 1 }), _jsx(NumberField, { name: "distance", label: "Distance", values: values, onChange: onUpdate, min: 0, step: 1, fallback: 0 }), _jsx(NumberField, { name: "decay", label: "Decay", values: values, onChange: onUpdate, min: 0, step: 0.1, fallback: 2 })] }), _jsxs(LightSection, { title: "Shadow", children: [_jsx(BooleanField, { name: "castShadow", label: "Cast Shadow", values: values, onChange: onUpdate, fallback: false }), values.castShadow ? (_jsxs(_Fragment, { children: [_jsx(BooleanField, { name: "shadowAutoUpdate", label: "Auto Update", values: values, onChange: onUpdate, fallback: true }), _jsx(NumberField, { name: "shadowMapSize", label: "Map Size", values: values, onChange: onUpdate, min: 128, step: 128, fallback: 512 }), _jsx(ShadowBiasField, { name: "shadowBias", label: "Bias", values: values, onChange: onUpdate, fallback: 0 }), _jsx(ShadowBiasField, { name: "shadowNormalBias", label: "Normal Bias", values: values, onChange: onUpdate, fallback: 0 }), _jsx(NumberField, { name: "shadowCameraNear", label: "Near", values: values, onChange: onUpdate, min: 0.001, step: 0.1, fallback: 0.5 }), _jsx(NumberField, { name: "shadowCameraFar", label: "Far", values: values, onChange: onUpdate, min: 0.1, step: 1, fallback: 500 })] })) : null] })] }));
|
|
24
24
|
}
|
|
25
25
|
function PointLightView({ properties, children }) {
|
|
26
|
-
const { editMode, isSelected } =
|
|
26
|
+
const { editMode, isSelected } = useNode();
|
|
27
27
|
const merged = mergeWithDefaults(pointLightDefaults, properties);
|
|
28
28
|
const color = merged.color;
|
|
29
29
|
const intensity = merged.intensity;
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { useEffect, useRef } from 'react';
|
|
3
3
|
import { useThree } from '@react-three/fiber';
|
|
4
4
|
import { SoundPicker } from '../../assetviewer/page';
|
|
5
|
-
import { useAssetRuntime,
|
|
5
|
+
import { useAssetRuntime, useNode } from '../assetRuntime';
|
|
6
6
|
import { gameEvents } from '../GameEvents';
|
|
7
7
|
import { BooleanField, FieldGroup, FieldRenderer, ListEditor, NumberField, SelectField, StringField } from './Input';
|
|
8
8
|
import { colors, ui } from '../styles';
|
|
@@ -124,7 +124,7 @@ function SoundComponentEditor({ component, onUpdate, basePath = '' }) {
|
|
|
124
124
|
}
|
|
125
125
|
function SoundComponentView({ properties, children }) {
|
|
126
126
|
const { getSound } = useAssetRuntime();
|
|
127
|
-
const { editMode, nodeId } =
|
|
127
|
+
const { editMode, nodeId } = useNode();
|
|
128
128
|
const { camera } = useThree();
|
|
129
129
|
const { eventName, autoplay = false, positional = false, refDistance = 1, maxDistance = 24, rolloffFactor = 1, distanceModel = 'inverse' } = properties;
|
|
130
130
|
const sequenceIndexRef = useRef(0);
|
|
@@ -3,7 +3,7 @@ import { useHelper } from "@react-three/drei";
|
|
|
3
3
|
import { useRef, useEffect } from "react";
|
|
4
4
|
import { BooleanField, ColorField, Label, NumberField, Vector3Input } from "./Input";
|
|
5
5
|
import { SpotLightHelper } from "three";
|
|
6
|
-
import { useAssetRuntime,
|
|
6
|
+
import { useAssetRuntime, useNode } from "../assetRuntime";
|
|
7
7
|
import { useFrame } from "@react-three/fiber";
|
|
8
8
|
import { TexturePicker } from "../../assetviewer/page";
|
|
9
9
|
import { LightSection, ShadowBiasField, mergeWithDefaults } from "./lightUtils";
|
|
@@ -31,7 +31,7 @@ function SpotLightComponentEditor({ component, onUpdate, basePath = "" }) {
|
|
|
31
31
|
function SpotLightView({ properties, children }) {
|
|
32
32
|
var _a;
|
|
33
33
|
const { getTexture } = useAssetRuntime();
|
|
34
|
-
const { editMode, isSelected } =
|
|
34
|
+
const { editMode, isSelected } = useNode();
|
|
35
35
|
const merged = mergeWithDefaults(spotLightDefaults, properties);
|
|
36
36
|
const color = merged.color;
|
|
37
37
|
const intensity = merged.intensity;
|
|
@@ -8,7 +8,6 @@ export interface PrefabState {
|
|
|
8
8
|
nodesById: Record<string, PrefabNodeRecord>;
|
|
9
9
|
childIdsById: Record<string, string[]>;
|
|
10
10
|
parentIdById: Record<string, string | null>;
|
|
11
|
-
revision: number;
|
|
12
11
|
assetManifestKey: string;
|
|
13
12
|
assetRefCounts: PrefabAssetRefCounts;
|
|
14
13
|
}
|
|
@@ -25,7 +24,7 @@ export declare function createEmptyNode(name?: string): GameObject;
|
|
|
25
24
|
export declare function createEmptyPrefab(): Prefab;
|
|
26
25
|
export declare function createModelNode(filename: string, name?: string): GameObject;
|
|
27
26
|
export declare function createImageNode(texturePath: string, name?: string): GameObject;
|
|
28
|
-
export declare function normalizePrefab(prefab: Prefab
|
|
27
|
+
export declare function normalizePrefab(prefab: Prefab): PrefabState;
|
|
29
28
|
export declare function createPrefabPatch(state: PrefabState, patch: Partial<PrefabState>, nextAssetRefCounts?: PrefabAssetRefCounts): Partial<PrefabState>;
|
|
30
29
|
export declare function denormalizePrefab(state: Pick<PrefabState, 'prefabId' | 'prefabName' | 'rootId' | 'nodesById' | 'childIdsById'>): Prefab;
|
|
31
30
|
export declare function collectSubtreeIds(id: string, childIdsById: Record<string, string[]>): string[];
|
|
@@ -132,7 +132,7 @@ export function createImageNode(texturePath, name) {
|
|
|
132
132
|
},
|
|
133
133
|
});
|
|
134
134
|
}
|
|
135
|
-
export function normalizePrefab(prefab
|
|
135
|
+
export function normalizePrefab(prefab) {
|
|
136
136
|
const nodesById = {};
|
|
137
137
|
const childIdsById = {};
|
|
138
138
|
const parentIdById = {};
|
|
@@ -145,14 +145,13 @@ export function normalizePrefab(prefab, revision = 0) {
|
|
|
145
145
|
nodesById,
|
|
146
146
|
childIdsById,
|
|
147
147
|
parentIdById,
|
|
148
|
-
revision,
|
|
149
148
|
assetManifestKey: getAssetManifestKey(assetRefCounts),
|
|
150
149
|
assetRefCounts,
|
|
151
150
|
};
|
|
152
151
|
}
|
|
153
152
|
export function createPrefabPatch(state, patch, nextAssetRefCounts = state.assetRefCounts) {
|
|
154
153
|
const assetRefsChanged = nextAssetRefCounts !== state.assetRefCounts;
|
|
155
|
-
return Object.assign(Object.assign(
|
|
154
|
+
return Object.assign(Object.assign({}, patch), (assetRefsChanged ? {
|
|
156
155
|
assetRefCounts: nextAssetRefCounts,
|
|
157
156
|
assetManifestKey: getAssetManifestKey(nextAssetRefCounts),
|
|
158
157
|
} : null));
|
|
@@ -5,14 +5,9 @@ import { denormalizePrefab, PrefabState, PrefabNodeRecord } from "./prefab";
|
|
|
5
5
|
export interface PrefabStoreState extends PrefabState {
|
|
6
6
|
replacePrefab: (prefab: Prefab) => void;
|
|
7
7
|
updateNode: (id: string, update: (node: PrefabNodeRecord) => PrefabNodeRecord) => void;
|
|
8
|
-
updateNodes: (updates: Array<{
|
|
9
|
-
id: string;
|
|
10
|
-
update: (node: PrefabNodeRecord) => PrefabNodeRecord;
|
|
11
|
-
}>) => void;
|
|
12
8
|
addChild: (parentId: string, node: GameObject) => void;
|
|
13
9
|
deleteNode: (id: string) => void;
|
|
14
10
|
duplicateNode: (id: string) => string | null;
|
|
15
|
-
toggleNodeFlag: (id: string, key: "disabled" | "locked") => void;
|
|
16
11
|
moveNode: (draggedId: string, targetId: string, position: "before" | "inside") => void;
|
|
17
12
|
}
|
|
18
13
|
export type PrefabStoreApi = StoreApi<PrefabStoreState>;
|
|
@@ -21,7 +16,6 @@ export declare function PrefabStoreProvider({ store, children, }: {
|
|
|
21
16
|
children: ReactNode;
|
|
22
17
|
}): import("react").FunctionComponentElement<import("react").ProviderProps<PrefabStoreApi | null>>;
|
|
23
18
|
export declare function usePrefabStoreApi(): PrefabStoreApi;
|
|
24
|
-
export declare function useOptionalPrefabStoreApi(): PrefabStoreApi | null;
|
|
25
19
|
export declare function usePrefabStore<T>(selector: (state: PrefabStoreState) => T): T;
|
|
26
20
|
export declare function usePrefabRootId(): string;
|
|
27
21
|
export declare function usePrefabNode(nodeId: string | null | undefined): PrefabNodeRecord | null;
|
|
@@ -15,9 +15,6 @@ export function usePrefabStoreApi() {
|
|
|
15
15
|
}
|
|
16
16
|
return store;
|
|
17
17
|
}
|
|
18
|
-
export function useOptionalPrefabStoreApi() {
|
|
19
|
-
return useContext(PrefabStoreContext);
|
|
20
|
-
}
|
|
21
18
|
export function usePrefabStore(selector) {
|
|
22
19
|
return useStore(usePrefabStoreApi(), selector);
|
|
23
20
|
}
|
|
@@ -32,7 +29,7 @@ export function usePrefabChildIds(nodeId) {
|
|
|
32
29
|
}
|
|
33
30
|
export function createPrefabStore(prefab) {
|
|
34
31
|
return createStore()(subscribeWithSelector((set, get) => (Object.assign(Object.assign({}, normalizePrefab(prefab)), { replacePrefab: (nextPrefab) => {
|
|
35
|
-
set(normalizePrefab(nextPrefab
|
|
32
|
+
set(normalizePrefab(nextPrefab));
|
|
36
33
|
}, updateNode: (id, update) => {
|
|
37
34
|
const state = get();
|
|
38
35
|
const node = state.nodesById[id];
|
|
@@ -45,26 +42,6 @@ export function createPrefabStore(prefab) {
|
|
|
45
42
|
set(createPrefabPatch(state, {
|
|
46
43
|
nodesById: Object.assign(Object.assign({}, state.nodesById), { [id]: nextNode }),
|
|
47
44
|
}, nextAssetRefCounts));
|
|
48
|
-
}, updateNodes: (updates) => {
|
|
49
|
-
if (updates.length === 0)
|
|
50
|
-
return;
|
|
51
|
-
const state = get();
|
|
52
|
-
let nextNodesById = null;
|
|
53
|
-
let nextAssetRefCounts = state.assetRefCounts;
|
|
54
|
-
for (const { id, update } of updates) {
|
|
55
|
-
const currentNode = (nextNodesById !== null && nextNodesById !== void 0 ? nextNodesById : state.nodesById)[id];
|
|
56
|
-
if (!currentNode)
|
|
57
|
-
continue;
|
|
58
|
-
const nextNode = update(currentNode);
|
|
59
|
-
if (nextNode === currentNode)
|
|
60
|
-
continue;
|
|
61
|
-
nextNodesById !== null && nextNodesById !== void 0 ? nextNodesById : (nextNodesById = Object.assign({}, state.nodesById));
|
|
62
|
-
nextNodesById[id] = nextNode;
|
|
63
|
-
nextAssetRefCounts = updateAssetRefsForNodeChange(nextAssetRefCounts, currentNode, nextNode);
|
|
64
|
-
}
|
|
65
|
-
if (!nextNodesById)
|
|
66
|
-
return;
|
|
67
|
-
set(createPrefabPatch(state, { nodesById: nextNodesById }, nextAssetRefCounts));
|
|
68
45
|
}, addChild: (parentId, node) => {
|
|
69
46
|
var _a;
|
|
70
47
|
const state = get();
|
|
@@ -152,15 +129,6 @@ export function createPrefabStore(prefab) {
|
|
|
152
129
|
parentIdById: nextParentIdById,
|
|
153
130
|
}, nextAssetRefCounts));
|
|
154
131
|
return duplicatedRootId;
|
|
155
|
-
}, toggleNodeFlag: (id, key) => {
|
|
156
|
-
const state = get();
|
|
157
|
-
const node = state.nodesById[id];
|
|
158
|
-
if (!node)
|
|
159
|
-
return;
|
|
160
|
-
const nextNode = Object.assign(Object.assign({}, node), { [key]: !node[key] });
|
|
161
|
-
set(createPrefabPatch(state, {
|
|
162
|
-
nodesById: Object.assign(Object.assign({}, state.nodesById), { [id]: nextNode }),
|
|
163
|
-
}));
|
|
164
132
|
}, moveNode: (draggedId, targetId, position) => {
|
|
165
133
|
var _a, _b, _c;
|
|
166
134
|
const state = get();
|