react-three-game 0.0.71 → 0.0.72
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/index.d.ts +3 -4
- package/dist/index.js +1 -3
- package/dist/tools/prefabeditor/PrefabEditor.d.ts +0 -2
- package/dist/tools/prefabeditor/PrefabEditor.js +12 -22
- package/dist/tools/prefabeditor/PrefabRoot.d.ts +23 -10
- package/dist/tools/prefabeditor/PrefabRoot.js +67 -44
- package/dist/tools/prefabeditor/components/CameraComponent.js +3 -1
- package/dist/tools/prefabeditor/components/ClickComponent.js +3 -1
- package/dist/tools/prefabeditor/components/ComponentRegistry.d.ts +0 -6
- package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +3 -1
- package/dist/tools/prefabeditor/components/EnvironmentComponent.js +2 -2
- package/dist/tools/prefabeditor/components/MaterialComponent.js +2 -2
- package/dist/tools/prefabeditor/components/ModelComponent.js +2 -2
- package/dist/tools/prefabeditor/components/PhysicsComponent.js +4 -3
- package/dist/tools/prefabeditor/components/PointLightComponent.js +3 -1
- package/dist/tools/prefabeditor/components/SoundComponent.js +4 -3
- package/dist/tools/prefabeditor/components/SpotLightComponent.js +4 -3
- package/dist/tools/prefabeditor/scene.d.ts +6 -2
- package/dist/tools/prefabeditor/scene.js +10 -5
- package/package.json +1 -1
- package/dist/tools/prefabeditor/RefBridge.d.ts +0 -24
- package/dist/tools/prefabeditor/RefBridge.js +0 -44
package/dist/index.d.ts
CHANGED
|
@@ -19,16 +19,15 @@ export type { ExportGLBOptions } from './tools/prefabeditor/utils';
|
|
|
19
19
|
export { createModelNode, createImageNode, } from './tools/prefabeditor/prefab';
|
|
20
20
|
export type { PrefabEditorProps, PrefabEditorRef, } from './tools/prefabeditor/PrefabEditor';
|
|
21
21
|
export type { SpawnOptions, Scene, Entity, EntityComponent, EntityData, EntityUpdate, PropertyPath, SceneUpdates, } from './tools/prefabeditor/scene';
|
|
22
|
-
export type { PrefabRootProps,
|
|
23
|
-
export {
|
|
22
|
+
export type { PrefabRootProps, AssetRuntime } from './tools/prefabeditor/PrefabRoot';
|
|
23
|
+
export type { EntityRuntime, LiveObjectRef, LiveRigidBodyRef } from './tools/prefabeditor/PrefabRoot';
|
|
24
|
+
export { useAssetRuntime, useEntityRuntime, useEntityObjectRef, useEntityRigidBodyRef } from './tools/prefabeditor/PrefabRoot';
|
|
24
25
|
export type { Component, ComponentViewProps } from './tools/prefabeditor/components/ComponentRegistry';
|
|
25
26
|
export type { FieldDefinition, FieldType } from './tools/prefabeditor/components/Input';
|
|
26
27
|
export type { Prefab, GameObject, ComponentData } from './tools/prefabeditor/types';
|
|
27
28
|
export { findComponent, findComponentEntry, hasComponent } from './tools/prefabeditor/types';
|
|
28
29
|
export { gameEvents, useGameEvent, getEntityIdFromRigidBody } from './tools/prefabeditor/GameEvents';
|
|
29
30
|
export type { GameEventType, GameEventMap, GameEventPayload, PhysicsEventType, InteractionEventType, PhysicsEventPayload, ClickEventPayload } from './tools/prefabeditor/GameEvents';
|
|
30
|
-
export { createRefBridge } from './tools/prefabeditor/RefBridge';
|
|
31
|
-
export type { RefBridge } from './tools/prefabeditor/RefBridge';
|
|
32
31
|
export { loadFiles } from './tools/dragdrop/DragDropLoader';
|
|
33
32
|
export type { AssetLoadOptions } from './tools/dragdrop/DragDropLoader';
|
|
34
33
|
export { loadModel, loadSound, loadTexture } from './tools/dragdrop/modelLoader';
|
package/dist/index.js
CHANGED
|
@@ -19,12 +19,10 @@ export { FieldRenderer, FieldGroup, ListEditor, Label, Vector3Input, Vector3Fiel
|
|
|
19
19
|
// Prefab Editor - Utils
|
|
20
20
|
export { loadJson, saveJson, exportGLB, exportGLBData, regenerateIds, computeParentWorldMatrix, } from './tools/prefabeditor/utils';
|
|
21
21
|
export { createModelNode, createImageNode, } from './tools/prefabeditor/prefab';
|
|
22
|
-
export {
|
|
22
|
+
export { useAssetRuntime, useEntityRuntime, useEntityObjectRef, useEntityRigidBodyRef } from './tools/prefabeditor/PrefabRoot';
|
|
23
23
|
export { findComponent, findComponentEntry, hasComponent } from './tools/prefabeditor/types';
|
|
24
24
|
// Game Events (physics + custom events)
|
|
25
25
|
export { gameEvents, useGameEvent, getEntityIdFromRigidBody } from './tools/prefabeditor/GameEvents';
|
|
26
|
-
// RefBridge & Systems
|
|
27
|
-
export { createRefBridge } from './tools/prefabeditor/RefBridge';
|
|
28
26
|
// Asset Loading
|
|
29
27
|
export { loadFiles } from './tools/dragdrop/DragDropLoader';
|
|
30
28
|
export { loadModel, loadSound, loadTexture } from './tools/dragdrop/modelLoader';
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import GameCanvas from "../../shared/GameCanvas";
|
|
2
2
|
import { Object3D, Texture } from "three";
|
|
3
3
|
import { GameObject, Prefab } from "./types";
|
|
4
|
-
import { PrefabRootRef } from "./PrefabRoot";
|
|
5
4
|
import type { ExportGLBOptions } from "./utils";
|
|
6
5
|
import { type Scene, type SpawnOptions } from "./scene";
|
|
7
6
|
export interface PrefabEditorRef {
|
|
@@ -17,7 +16,6 @@ export interface PrefabEditorRef {
|
|
|
17
16
|
}) => void;
|
|
18
17
|
addModel: (path: string, model: Object3D, options?: SpawnOptions) => GameObject;
|
|
19
18
|
addTexture: (path: string, texture: Texture, options?: SpawnOptions) => GameObject;
|
|
20
|
-
viewRef: React.RefObject<PrefabRootRef | null>;
|
|
21
19
|
}
|
|
22
20
|
export declare enum PrefabEditorMode {
|
|
23
21
|
Edit = "edit",
|
|
@@ -61,6 +61,8 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode
|
|
|
61
61
|
const onChangeRef = useRef(onChange);
|
|
62
62
|
const isEditMode = mode === PrefabEditorMode.Edit;
|
|
63
63
|
const getPrefab = useCallback(() => denormalizePrefab(prefabStore.getState()), [prefabStore]);
|
|
64
|
+
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; }, []);
|
|
65
|
+
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; }, []);
|
|
64
66
|
onChangeRef.current = onChange;
|
|
65
67
|
const setSelection = useCallback((nodeId) => {
|
|
66
68
|
const nextNode = nodeId ? prefabStore.getState().nodesById[nodeId] : null;
|
|
@@ -157,13 +159,12 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode
|
|
|
157
159
|
return () => unsubscribe();
|
|
158
160
|
}, [prefabStore, selectedId]);
|
|
159
161
|
useEffect(() => {
|
|
160
|
-
var _a, _b;
|
|
161
162
|
if (!selectedId) {
|
|
162
163
|
setSelectedObject(null);
|
|
163
164
|
return;
|
|
164
165
|
}
|
|
165
|
-
setSelectedObject(
|
|
166
|
-
}, [selectedId]);
|
|
166
|
+
setSelectedObject(getObject(selectedId));
|
|
167
|
+
}, [getObject, selectedId]);
|
|
167
168
|
const addNode = useCallback((node, options) => {
|
|
168
169
|
var _a;
|
|
169
170
|
const { addChild, rootId } = prefabStore.getState();
|
|
@@ -256,39 +257,29 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode
|
|
|
256
257
|
return exportGLBData(rootObject);
|
|
257
258
|
}), [clearSelection]);
|
|
258
259
|
const handleFocusNode = useCallback((nodeId) => {
|
|
259
|
-
|
|
260
|
-
const object = (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.getObject(nodeId);
|
|
260
|
+
const object = getObject(nodeId);
|
|
261
261
|
const controls = controlsRef.current;
|
|
262
262
|
const camera = controls === null || controls === void 0 ? void 0 : controls.object;
|
|
263
263
|
if (!object || !controls || !camera)
|
|
264
264
|
return;
|
|
265
265
|
focusCameraOnObject(object, camera, controls.target, () => { var _a; return (_a = controls.update) === null || _a === void 0 ? void 0 : _a.call(controls); });
|
|
266
|
-
}, []);
|
|
266
|
+
}, [getObject]);
|
|
267
267
|
const scene = useMemo(() => createScene({
|
|
268
268
|
getRootId: () => prefabStore.getState().rootId,
|
|
269
269
|
getNode: (id) => { var _a; return (_a = prefabStore.getState().nodesById[id]) !== null && _a !== void 0 ? _a : null; },
|
|
270
270
|
getChildIds: (id) => { var _a; return (_a = prefabStore.getState().childIdsById[id]) !== null && _a !== void 0 ? _a : []; },
|
|
271
271
|
getParentId: (id) => { var _a; return (_a = prefabStore.getState().parentIdById[id]) !== null && _a !== void 0 ? _a : null; },
|
|
272
|
-
findByName: (name) => {
|
|
273
|
-
var _a;
|
|
274
|
-
const state = prefabStore.getState();
|
|
275
|
-
const normalized = name.toLowerCase();
|
|
276
|
-
for (const [id, node] of Object.entries(state.nodesById)) {
|
|
277
|
-
if (((_a = node.name) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === normalized)
|
|
278
|
-
return id;
|
|
279
|
-
}
|
|
280
|
-
return null;
|
|
281
|
-
},
|
|
282
272
|
updateNode: (id, update) => prefabStore.getState().updateNode(id, update),
|
|
283
273
|
updateNodes: (updates) => prefabStore.getState().updateNodes(Object.entries(updates).map(([id, update]) => ({ id, update }))),
|
|
284
274
|
addNode: (node, options) => addNode(node, options).id,
|
|
285
275
|
removeNode: (id) => prefabStore.getState().deleteNode(id),
|
|
286
|
-
|
|
276
|
+
getObject,
|
|
277
|
+
getRigidBody,
|
|
278
|
+
}), [addNode, getObject, getRigidBody, prefabStore]);
|
|
287
279
|
const handleTransformChange = () => {
|
|
288
|
-
var _a;
|
|
289
280
|
if (!selectedId)
|
|
290
281
|
return;
|
|
291
|
-
const object =
|
|
282
|
+
const object = getObject(selectedId);
|
|
292
283
|
if (!object)
|
|
293
284
|
return;
|
|
294
285
|
const parentWorld = computeParentWorldMatrix(prefabStore.getState(), selectedId);
|
|
@@ -349,10 +340,9 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, mode
|
|
|
349
340
|
scene,
|
|
350
341
|
load: loadPrefab,
|
|
351
342
|
addModel,
|
|
352
|
-
addTexture
|
|
353
|
-
viewRef: prefabRootRef
|
|
343
|
+
addTexture
|
|
354
344
|
}), [addModel, addTexture, clearSelection, getPrefab, handleExportGLB, handleExportGLBData, handleScreenshot, loadPrefab, scene]);
|
|
355
|
-
const content = (_jsxs(_Fragment, { children: [_jsx("gridHelper", { args: [10, 10], position: [0, -1, 0] }), _jsx(PrefabRoot, { ref: prefabRootRef, store: prefabStore, editMode: isEditMode, selectedId: selectedId, onSelect: setSelection, basePath: basePath }), children] }));
|
|
345
|
+
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] }));
|
|
356
346
|
return _jsx(PrefabStoreProvider, { store: prefabStore, children: _jsxs(EditorContext.Provider, { value: {
|
|
357
347
|
mode,
|
|
358
348
|
setMode: updateMode,
|
|
@@ -3,14 +3,10 @@ import { ThreeEvent } from "@react-three/fiber";
|
|
|
3
3
|
import { GameObject as GameObjectType, Prefab } from "./types";
|
|
4
4
|
import { LoadedModels } from "../dragdrop";
|
|
5
5
|
import { PrefabStoreApi } from "./prefabStore";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export interface SceneRuntime {
|
|
9
|
-
refBridge: RefBridge;
|
|
10
|
-
getRigidBody: (id: string) => any;
|
|
6
|
+
/** Scene-wide shared services available to component Views inside a PrefabRoot. */
|
|
7
|
+
export interface AssetRuntime {
|
|
11
8
|
/** @internal Used by PhysicsComponent. */
|
|
12
9
|
registerRigidBodyRef: (id: string, rb: any) => void;
|
|
13
|
-
editMode?: boolean;
|
|
14
10
|
/** Get a loaded model by asset path. */
|
|
15
11
|
getModel: (path: string) => Object3D | null;
|
|
16
12
|
/** Get a loaded texture by asset path. */
|
|
@@ -20,12 +16,29 @@ export interface SceneRuntime {
|
|
|
20
16
|
/** Get a revision string that changes when loaded assets change (for cache-busting keys). */
|
|
21
17
|
getAssetRevision: () => string;
|
|
22
18
|
}
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
export interface EntityRuntime {
|
|
20
|
+
nodeId: string;
|
|
21
|
+
editMode?: boolean;
|
|
22
|
+
isSelected?: boolean;
|
|
23
|
+
getObject: <T extends Object3D = Object3D>() => T | null;
|
|
24
|
+
getRigidBody: <T = any>() => T | null;
|
|
25
|
+
}
|
|
26
|
+
export interface LiveObjectRef<T extends Object3D = Object3D> {
|
|
27
|
+
readonly current: T | null;
|
|
28
|
+
}
|
|
29
|
+
export interface LiveRigidBodyRef<T = any> {
|
|
30
|
+
readonly current: T | null;
|
|
31
|
+
}
|
|
32
|
+
/** Access scene-wide shared services from within a component View. */
|
|
33
|
+
export declare function useAssetRuntime(): AssetRuntime;
|
|
34
|
+
/** Access the current node's runtime from within a component View. */
|
|
35
|
+
export declare function useEntityRuntime(): EntityRuntime;
|
|
36
|
+
/** Read the current component's Object3D through a live ref-like accessor. */
|
|
37
|
+
export declare function useEntityObjectRef<T extends Object3D = Object3D>(): LiveObjectRef<T>;
|
|
38
|
+
/** Read the current component's rigid body through a live ref-like accessor. */
|
|
39
|
+
export declare function useEntityRigidBodyRef<T = any>(): LiveRigidBodyRef<T>;
|
|
25
40
|
export interface PrefabRootRef {
|
|
26
41
|
root: Group | null;
|
|
27
|
-
refBridge: RefBridge;
|
|
28
|
-
rigidBodyRefs: Map<string, any>;
|
|
29
42
|
getObject: (nodeId: string) => Object3D | null;
|
|
30
43
|
getRigidBody: (nodeId: string) => any;
|
|
31
44
|
addModel: (path: string, model: Object3D) => void;
|
|
@@ -24,21 +24,60 @@ import { composeTransform, decompose } from "./utils";
|
|
|
24
24
|
import { isPhysicsProps } from "./components/PhysicsComponent";
|
|
25
25
|
import { denormalizePrefab } from "./prefab";
|
|
26
26
|
import { createPrefabStore, PrefabStoreProvider, useOptionalPrefabStoreApi, usePrefabChildIds, usePrefabNode, usePrefabRootId } from "./prefabStore";
|
|
27
|
-
import { createRefBridge } from "./RefBridge";
|
|
28
27
|
import { sound as soundManager } from "../../helpers/SoundManager";
|
|
29
28
|
components.forEach(registerComponent);
|
|
30
29
|
const IDENTITY = new Matrix4();
|
|
31
30
|
const EMPTY_MODELS = {};
|
|
32
31
|
const EMPTY_TEXTURES = {};
|
|
33
32
|
const EMPTY_SOUNDS = {};
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
const AssetRuntimeContext = createContext(null);
|
|
34
|
+
const EntityRuntimeContext = createContext(null);
|
|
35
|
+
/** Access scene-wide shared services from within a component View. */
|
|
36
|
+
export function useAssetRuntime() {
|
|
37
|
+
const ctx = useContext(AssetRuntimeContext);
|
|
38
38
|
if (!ctx)
|
|
39
|
-
throw new Error("
|
|
39
|
+
throw new Error("useAssetRuntime must be used inside <PrefabRoot>");
|
|
40
40
|
return ctx;
|
|
41
41
|
}
|
|
42
|
+
/** Access the current node's runtime from within a component View. */
|
|
43
|
+
export function useEntityRuntime() {
|
|
44
|
+
const ctx = useContext(EntityRuntimeContext);
|
|
45
|
+
if (!ctx)
|
|
46
|
+
throw new Error("useEntityRuntime must be used inside a component View rendered by <PrefabRoot>");
|
|
47
|
+
return ctx;
|
|
48
|
+
}
|
|
49
|
+
/** Read the current component's Object3D through a live ref-like accessor. */
|
|
50
|
+
export function useEntityObjectRef() {
|
|
51
|
+
const { getObject } = useEntityRuntime();
|
|
52
|
+
return useMemo(() => ({
|
|
53
|
+
get current() {
|
|
54
|
+
return getObject();
|
|
55
|
+
},
|
|
56
|
+
}), [getObject]);
|
|
57
|
+
}
|
|
58
|
+
/** Read the current component's rigid body through a live ref-like accessor. */
|
|
59
|
+
export function useEntityRigidBodyRef() {
|
|
60
|
+
const { getRigidBody } = useEntityRuntime();
|
|
61
|
+
return useMemo(() => ({
|
|
62
|
+
get current() {
|
|
63
|
+
return getRigidBody();
|
|
64
|
+
},
|
|
65
|
+
}), [getRigidBody]);
|
|
66
|
+
}
|
|
67
|
+
function EntityRuntimeScope({ nodeId, editMode, isSelected, children, }) {
|
|
68
|
+
const assetRuntime = useContext(AssetRuntimeContext);
|
|
69
|
+
if (!assetRuntime)
|
|
70
|
+
throw new Error("EntityRuntimeScope must be used inside <PrefabRoot>");
|
|
71
|
+
const { getObject, getRigidBody } = assetRuntime;
|
|
72
|
+
const value = useMemo(() => ({
|
|
73
|
+
nodeId,
|
|
74
|
+
editMode,
|
|
75
|
+
isSelected,
|
|
76
|
+
getObject: () => getObject(nodeId),
|
|
77
|
+
getRigidBody: () => getRigidBody(nodeId),
|
|
78
|
+
}), [editMode, getObject, getRigidBody, isSelected, nodeId]);
|
|
79
|
+
return _jsx(EntityRuntimeContext.Provider, { value: value, children: children });
|
|
80
|
+
}
|
|
42
81
|
/** Resolve a relative or absolute asset file path against a base path. */
|
|
43
82
|
function resolveAssetPath(basePath, file) {
|
|
44
83
|
if (file.startsWith("http://") || file.startsWith("https://"))
|
|
@@ -68,7 +107,6 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
68
107
|
const objectRefs = useRef({});
|
|
69
108
|
const rigidBodyRefs = useRef(new Map());
|
|
70
109
|
const rootRef = useRef(null);
|
|
71
|
-
const [refBridge] = useState(() => createRefBridge());
|
|
72
110
|
const parentStore = useOptionalPrefabStoreApi();
|
|
73
111
|
const [ownedStore] = useState(() => { var _a, _b; return createPrefabStore(data !== null && data !== void 0 ? data : denormalizePrefab((_b = (_a = store === null || store === void 0 ? void 0 : store.getState()) !== null && _a !== void 0 ? _a : parentStore === null || parentStore === void 0 ? void 0 : parentStore.getState()) !== null && _b !== void 0 ? _b : missingStoreState())); });
|
|
74
112
|
const resolvedStore = (_a = store !== null && store !== void 0 ? store : parentStore) !== null && _a !== void 0 ? _a : ownedStore;
|
|
@@ -78,11 +116,13 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
78
116
|
const availableModels = useMemo(() => (Object.assign(Object.assign({}, models), injectedModels)), [models, injectedModels]);
|
|
79
117
|
const availableTextures = useMemo(() => (Object.assign(Object.assign({}, textures), injectedTextures)), [textures, injectedTextures]);
|
|
80
118
|
const availableSounds = useMemo(() => (Object.assign(Object.assign({}, sounds), injectedSounds)), [sounds, injectedSounds]);
|
|
119
|
+
const getObject = useCallback((id) => {
|
|
120
|
+
var _a;
|
|
121
|
+
return (_a = objectRefs.current[id]) !== null && _a !== void 0 ? _a : null;
|
|
122
|
+
}, []);
|
|
81
123
|
useImperativeHandle(ref, () => ({
|
|
82
124
|
root: rootRef.current,
|
|
83
|
-
|
|
84
|
-
rigidBodyRefs: rigidBodyRefs.current,
|
|
85
|
-
getObject: (nodeId) => { var _a; return (_a = objectRefs.current[nodeId]) !== null && _a !== void 0 ? _a : null; },
|
|
125
|
+
getObject,
|
|
86
126
|
getRigidBody: (nodeId) => { var _a; return (_a = rigidBodyRefs.current.get(nodeId)) !== null && _a !== void 0 ? _a : null; },
|
|
87
127
|
addModel: (path, model) => setInjectedModels(prev => (Object.assign(Object.assign({}, prev), { [path]: model }))),
|
|
88
128
|
addTexture: (path, texture) => setInjectedTextures(prev => (Object.assign(Object.assign({}, prev), { [path]: texture }))),
|
|
@@ -90,11 +130,10 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
90
130
|
soundManager.setBuffer(path, sound);
|
|
91
131
|
setInjectedSounds(prev => (Object.assign(Object.assign({}, prev), { [path]: sound })));
|
|
92
132
|
},
|
|
93
|
-
}), [
|
|
133
|
+
}), [getObject]);
|
|
94
134
|
const registerRef = useCallback((id, obj) => {
|
|
95
135
|
objectRefs.current[id] = obj;
|
|
96
|
-
|
|
97
|
-
}, [refBridge]);
|
|
136
|
+
}, []);
|
|
98
137
|
const registerRigidBodyRef = useCallback((id, rb) => {
|
|
99
138
|
rigidBodyRefs.current.set(id, rb);
|
|
100
139
|
}, []);
|
|
@@ -180,11 +219,10 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
180
219
|
availableTexturesRef.current = availableTextures;
|
|
181
220
|
const availableSoundsRef = useRef(availableSounds);
|
|
182
221
|
availableSoundsRef.current = availableSounds;
|
|
183
|
-
const
|
|
184
|
-
|
|
222
|
+
const assetRuntime = useMemo(() => ({
|
|
223
|
+
getObject,
|
|
185
224
|
getRigidBody,
|
|
186
225
|
registerRigidBodyRef,
|
|
187
|
-
editMode,
|
|
188
226
|
getModel: (path) => { var _a; return (_a = availableModelsRef.current[path]) !== null && _a !== void 0 ? _a : null; },
|
|
189
227
|
getTexture: (path) => { var _a; return (_a = availableTexturesRef.current[path]) !== null && _a !== void 0 ? _a : null; },
|
|
190
228
|
getSound: (path) => { var _a; return (_a = availableSoundsRef.current[path]) !== null && _a !== void 0 ? _a : null; },
|
|
@@ -193,12 +231,12 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
193
231
|
const textureKeys = Object.keys(availableTexturesRef.current).sort().join('|');
|
|
194
232
|
return `${textureKeys}::${modelKeys}`;
|
|
195
233
|
},
|
|
196
|
-
}), [
|
|
234
|
+
}), [getObject, getRigidBody, registerRigidBodyRef]);
|
|
197
235
|
const content = (_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, loadedModels: availableModels, editMode: editMode, parentMatrix: IDENTITY }) }) }));
|
|
198
236
|
if (!shouldProvideStoreContext) {
|
|
199
|
-
return _jsx(
|
|
237
|
+
return _jsx(AssetRuntimeContext.Provider, { value: assetRuntime, children: content });
|
|
200
238
|
}
|
|
201
|
-
return _jsx(PrefabStoreProvider, { store: resolvedStore, children: _jsx(
|
|
239
|
+
return _jsx(PrefabStoreProvider, { store: resolvedStore, children: _jsx(AssetRuntimeContext.Provider, { value: assetRuntime, children: content }) });
|
|
202
240
|
});
|
|
203
241
|
function StoreRootNode(props) {
|
|
204
242
|
const rootId = usePrefabRootId();
|
|
@@ -289,14 +327,8 @@ function StandardNode({ nodeId, selectedId, onSelect, onClick, registerRef, load
|
|
|
289
327
|
const childNodes = getChildHostComponents(gameObject).length > 0
|
|
290
328
|
? _jsx(CompositionChildren, { childIds: childIds, selectedId: selectedId, ctx: renderCtx, parentMatrix: world })
|
|
291
329
|
: _jsx(ChildNodes, { childIds: childIds, parentMatrix: world, selectedId: selectedId, onSelect: onSelect, onClick: onClick, registerRef: registerRef, loadedModels: loadedModels, editMode: editMode });
|
|
292
|
-
const inner = (_jsx("group", Object.assign({}, clickHandlers, { children: renderCompositionNode(gameObject, renderCtx,
|
|
293
|
-
|
|
294
|
-
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, children: inner }, physicsKey)) : null] }));
|
|
295
|
-
}
|
|
296
|
-
if (hasPhysics && (physicsDef === null || physicsDef === void 0 ? void 0 : physicsDef.View)) {
|
|
297
|
-
return (_jsx(physicsDef.View, { properties: physics.properties, position: transform.position, rotation: transform.rotation, scale: transform.scale, editMode: editMode, nodeId: nodeId, children: inner }, physicsKey));
|
|
298
|
-
}
|
|
299
|
-
return (_jsx("group", { ref: groupRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: inner }));
|
|
330
|
+
const inner = (_jsx("group", Object.assign({}, clickHandlers, { children: renderCompositionNode(gameObject, renderCtx, childNodes) })));
|
|
331
|
+
return (_jsx(EntityRuntimeScope, { nodeId: nodeId, editMode: editMode, isSelected: isSelected, children: editMode ? (_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, children: inner }, physicsKey)) : null] })) : 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, children: inner }, physicsKey)) : (_jsx("group", { ref: groupRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: inner })) }));
|
|
300
332
|
}
|
|
301
333
|
function getChildHostComponents(gameObject) {
|
|
302
334
|
var _a;
|
|
@@ -385,7 +417,7 @@ function renderCompositionSubtree(gameObject, ctx, isSelected, childIds, parentM
|
|
|
385
417
|
const transform = getNodeTransformProps(gameObject);
|
|
386
418
|
const world = parentMatrix.clone().multiply(compose(gameObject));
|
|
387
419
|
const childNodes = _jsx(CompositionChildren, { childIds: childIds, ctx: ctx, parentMatrix: world });
|
|
388
|
-
return (_jsx("group", { position: transform.position, rotation: transform.rotation, scale: transform.scale, children: renderCompositionNode(gameObject, ctx,
|
|
420
|
+
return (_jsx(EntityRuntimeScope, { nodeId: gameObject.id, editMode: ctx.editMode, isSelected: isSelected, children: _jsx("group", { position: transform.position, rotation: transform.rotation, scale: transform.scale, children: renderCompositionNode(gameObject, ctx, childNodes) }, gameObject.id) }));
|
|
389
421
|
}
|
|
390
422
|
function CompositionChildren({ childIds, selectedId, ctx, parentMatrix, }) {
|
|
391
423
|
return childIds.map(childId => (_jsx(CompositionSubtree, { nodeId: childId, selectedId: selectedId, ctx: ctx, parentMatrix: parentMatrix }, childId)));
|
|
@@ -398,31 +430,22 @@ function CompositionSubtree({ nodeId, selectedId, ctx, parentMatrix, }) {
|
|
|
398
430
|
return null;
|
|
399
431
|
return renderCompositionSubtree(gameObject, ctx, isSelected, childIds, parentMatrix);
|
|
400
432
|
}
|
|
401
|
-
function renderCompositionNode(gameObject, ctx,
|
|
402
|
-
const ownContent = renderNodeOwnContent(gameObject
|
|
403
|
-
return wrapWithChildHosts(gameObject,
|
|
404
|
-
}
|
|
405
|
-
function buildContextProps(gameObject, _ctx, isSelected) {
|
|
406
|
-
return {
|
|
407
|
-
editMode: _ctx.editMode,
|
|
408
|
-
isSelected,
|
|
409
|
-
nodeId: gameObject.id,
|
|
410
|
-
};
|
|
433
|
+
function renderCompositionNode(gameObject, ctx, childNodes) {
|
|
434
|
+
const ownContent = renderNodeOwnContent(gameObject);
|
|
435
|
+
return wrapWithChildHosts(gameObject, _jsxs(_Fragment, { children: [ownContent, childNodes] }));
|
|
411
436
|
}
|
|
412
|
-
function renderNodeOwnContent(gameObject
|
|
437
|
+
function renderNodeOwnContent(gameObject) {
|
|
413
438
|
const geometry = findComponent(gameObject, "Geometry");
|
|
414
439
|
const material = findComponent(gameObject, "Material");
|
|
415
440
|
const geometryDef = geometry && getComponentDef(geometry.type);
|
|
416
441
|
const materialDef = material && getComponentDef(material.type);
|
|
417
442
|
if (!geometry || !(geometryDef === null || geometryDef === void 0 ? void 0 : geometryDef.View))
|
|
418
443
|
return null;
|
|
419
|
-
|
|
420
|
-
return (_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"))] }));
|
|
444
|
+
return (_jsxs("mesh", { castShadow: true, receiveShadow: true, children: [_jsx(geometryDef.View, { properties: geometry.properties }), material && (materialDef === null || materialDef === void 0 ? void 0 : materialDef.View) && (_jsx(materialDef.View, { properties: material.properties }, "material"))] }));
|
|
421
445
|
}
|
|
422
|
-
function wrapWithChildHosts(gameObject,
|
|
423
|
-
const contextProps = buildContextProps(gameObject, ctx, isSelected);
|
|
446
|
+
function wrapWithChildHosts(gameObject, subtree) {
|
|
424
447
|
const childHosts = getChildHostComponents(gameObject);
|
|
425
|
-
return childHosts.reduce((acc, { key, View, properties }) => (_jsx(View,
|
|
448
|
+
return childHosts.reduce((acc, { key, View, properties }) => (_jsx(View, { properties: properties, children: acc }, key)), subtree);
|
|
426
449
|
}
|
|
427
450
|
export default PrefabRoot;
|
|
428
451
|
function missingStoreState() {
|
|
@@ -3,6 +3,7 @@ import { PerspectiveCamera, useHelper } from '@react-three/drei';
|
|
|
3
3
|
import { useRef } from 'react';
|
|
4
4
|
import { CameraHelper } from 'three';
|
|
5
5
|
import { useFrame } from '@react-three/fiber';
|
|
6
|
+
import { useEntityRuntime } from '../PrefabRoot';
|
|
6
7
|
import { FieldGroup, NumberField } from './Input';
|
|
7
8
|
const cameraDefaults = {
|
|
8
9
|
fov: 50,
|
|
@@ -14,7 +15,8 @@ function CameraComponentEditor({ component, onUpdate }) {
|
|
|
14
15
|
const values = Object.assign(Object.assign({}, cameraDefaults), component.properties);
|
|
15
16
|
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
17
|
}
|
|
17
|
-
function CameraComponentView({ properties, children
|
|
18
|
+
function CameraComponentView({ properties, children }) {
|
|
19
|
+
const { editMode, isSelected } = useEntityRuntime();
|
|
18
20
|
const merged = Object.assign(Object.assign({}, cameraDefaults), properties);
|
|
19
21
|
const fov = merged.fov;
|
|
20
22
|
const near = merged.near;
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useRef } from 'react';
|
|
3
3
|
import { gameEvents } from '../GameEvents';
|
|
4
|
+
import { useEntityRuntime } from '../PrefabRoot';
|
|
4
5
|
import { FieldGroup, StringField } from './Input';
|
|
5
6
|
function ClickComponentEditor({ component, onUpdate }) {
|
|
6
7
|
return (_jsxs(FieldGroup, { children: [_jsx("div", { style: { fontSize: 12, opacity: 0.8 }, children: "Emits a game event in play mode when this entity is clicked." }), _jsx(StringField, { name: "eventName", label: "Emit Event", values: component.properties, onChange: onUpdate, placeholder: "click" })] }));
|
|
7
8
|
}
|
|
8
|
-
function ClickComponentView({ children,
|
|
9
|
+
function ClickComponentView({ children, properties }) {
|
|
9
10
|
const clickValid = useRef(false);
|
|
11
|
+
const { editMode, nodeId } = useEntityRuntime();
|
|
10
12
|
const eventName = (properties === null || properties === void 0 ? void 0 : properties.eventName) || 'click';
|
|
11
13
|
const emitClick = (event) => {
|
|
12
14
|
if (!nodeId)
|
|
@@ -10,12 +10,6 @@ export interface ComponentViewProps<P = Record<string, any>> {
|
|
|
10
10
|
properties: P;
|
|
11
11
|
/** Children to render (for wrapper / child-host components). */
|
|
12
12
|
children?: React.ReactNode;
|
|
13
|
-
/** The entity ID this component belongs to. */
|
|
14
|
-
nodeId?: string;
|
|
15
|
-
/** True when the editor is in edit mode. */
|
|
16
|
-
editMode?: boolean;
|
|
17
|
-
/** True when this entity is selected in the editor. */
|
|
18
|
-
isSelected?: boolean;
|
|
19
13
|
/** Entity local position (passed to wrapper components like Physics). */
|
|
20
14
|
position?: [number, number, number];
|
|
21
15
|
/** Entity local rotation in radians (passed to wrapper components like Physics). */
|
|
@@ -3,6 +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, Vector3 } from "three";
|
|
6
|
+
import { useEntityRuntime } from "../PrefabRoot";
|
|
6
7
|
import { BooleanField, ColorField, NumberField, NumberInput, Vector3Input } from "./Input";
|
|
7
8
|
import { LightSection, ShadowBiasField, mergeWithDefaults } from "./lightUtils";
|
|
8
9
|
import { colors } from "../styles";
|
|
@@ -100,7 +101,8 @@ function DirectionalLightComponentEditor({ component, onUpdate }) {
|
|
|
100
101
|
const values = mergeWithDefaults(directionalLightDefaults, component.properties);
|
|
101
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] })] }));
|
|
102
103
|
}
|
|
103
|
-
function DirectionalLightView({ properties, children
|
|
104
|
+
function DirectionalLightView({ properties, children }) {
|
|
105
|
+
const { editMode, isSelected } = useEntityRuntime();
|
|
104
106
|
const merged = mergeWithDefaults(directionalLightDefaults, properties);
|
|
105
107
|
const color = merged.color;
|
|
106
108
|
const intensity = merged.intensity;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Environment } from '@react-three/drei';
|
|
3
3
|
import { FieldGroup, NumberField } from './Input';
|
|
4
|
-
import {
|
|
4
|
+
import { useAssetRuntime } from '../PrefabRoot';
|
|
5
5
|
function EnvironmentView({ properties, children, }) {
|
|
6
|
-
const { getAssetRevision } =
|
|
6
|
+
const { getAssetRevision } = useAssetRuntime();
|
|
7
7
|
const { intensity = 1, resolution = 256 } = properties;
|
|
8
8
|
const environmentRevision = `${getAssetRevision()}::${intensity}::${resolution}`;
|
|
9
9
|
return (_jsx(Environment, { background: true, environmentIntensity: intensity, resolution: resolution, frames: 1, children: children }, environmentRevision));
|
|
@@ -12,7 +12,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
12
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
13
|
import { extend } from '@react-three/fiber';
|
|
14
14
|
import { FieldRenderer, Label, NumberInput } from './Input';
|
|
15
|
-
import {
|
|
15
|
+
import { useAssetRuntime } from '../PrefabRoot';
|
|
16
16
|
import { useMemo } from 'react';
|
|
17
17
|
import { MeshBasicNodeMaterial, MeshStandardNodeMaterial } from 'three/webgpu';
|
|
18
18
|
import { TexturePicker } from '../../assetviewer/page';
|
|
@@ -122,7 +122,7 @@ function MaterialComponentEditor({ component, onUpdate, basePath = "" }) {
|
|
|
122
122
|
// View for Material component
|
|
123
123
|
function MaterialComponentView({ properties: rawProps }) {
|
|
124
124
|
var _a, _b, _c, _d, _e;
|
|
125
|
-
const { getTexture } =
|
|
125
|
+
const { getTexture } = useAssetRuntime();
|
|
126
126
|
const properties = rawProps;
|
|
127
127
|
const materialType = (_a = properties === null || properties === void 0 ? void 0 : properties.materialType) !== null && _a !== void 0 ? _a : 'standard';
|
|
128
128
|
const textureName = properties === null || properties === void 0 ? void 0 : properties.texture;
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { ModelPicker } from '../../assetviewer/page';
|
|
3
3
|
import { useContext, useMemo } from 'react';
|
|
4
4
|
import { BooleanField, FieldGroup, Label, ListEditor, NumberInput, SelectInput } from './Input';
|
|
5
|
-
import {
|
|
5
|
+
import { useAssetRuntime } from '../PrefabRoot';
|
|
6
6
|
import { EditorContext } from '../PrefabEditor';
|
|
7
7
|
import { DEFAULT_REPEAT_AXES, getRepeatAxesFromModelProperties, normalizeRepeatAxes } from '../InstanceProvider';
|
|
8
8
|
import { colors } from '../styles';
|
|
@@ -65,7 +65,7 @@ function ModelComponentEditor({ component, node, onUpdate, basePath = "" }) {
|
|
|
65
65
|
}
|
|
66
66
|
// View for Model component
|
|
67
67
|
function ModelComponentView({ properties, children }) {
|
|
68
|
-
const { getModel } =
|
|
68
|
+
const { getModel } = useAssetRuntime();
|
|
69
69
|
// Instanced models are handled elsewhere (GameInstance), so only render non-instanced here
|
|
70
70
|
if (!properties.filename || properties.instanced)
|
|
71
71
|
return _jsx(_Fragment, { children: children });
|
|
@@ -12,7 +12,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
12
12
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
13
13
|
import { CapsuleCollider, RigidBody, useRapier } from "@react-three/rapier";
|
|
14
14
|
import { useRef, useEffect, useCallback } from 'react';
|
|
15
|
-
import {
|
|
15
|
+
import { useAssetRuntime, useEntityRuntime } from "../PrefabRoot";
|
|
16
16
|
import { BooleanField, FieldGroup, ListEditor, NumberField, SelectField, SelectInput, StringInput, Vector3Field } from "./Input";
|
|
17
17
|
import { gameEvents, getEntityIdFromRigidBody } from "../GameEvents";
|
|
18
18
|
import { colors } from "../styles";
|
|
@@ -163,8 +163,9 @@ function PhysicsComponentEditor({ component, onUpdate }) {
|
|
|
163
163
|
{ value: 'all', label: 'All (includes kinematic & fixed)' },
|
|
164
164
|
] })] }));
|
|
165
165
|
}
|
|
166
|
-
function PhysicsComponentView({ properties, children, position, rotation, scale
|
|
167
|
-
const { registerRigidBodyRef } =
|
|
166
|
+
function PhysicsComponentView({ properties, children, position, rotation, scale }) {
|
|
167
|
+
const { registerRigidBodyRef } = useAssetRuntime();
|
|
168
|
+
const { editMode, nodeId } = useEntityRuntime();
|
|
168
169
|
const { type, colliders, sensor, activeCollisionTypes, linearVelocity = [0, 0, 0], angularVelocity = [0, 0, 0], capsuleRadius = capsuleRadiusFallback, capsuleHalfHeight = capsuleHalfHeightFallback, sensorEnterEventName, sensorExitEventName, collisionEnterEventName, collisionExitEventName, enabledTranslations = enabledAxesFallback, enabledRotations = enabledAxesFallback } = properties, otherProps = __rest(properties, ["type", "colliders", "sensor", "activeCollisionTypes", "linearVelocity", "angularVelocity", "capsuleRadius", "capsuleHalfHeight", "sensorEnterEventName", "sensorExitEventName", "collisionEnterEventName", "collisionExitEventName", "enabledTranslations", "enabledRotations"]);
|
|
169
170
|
const colliderType = colliders || (type === 'fixed' ? 'trimesh' : 'hull');
|
|
170
171
|
const usesManualCapsuleCollider = colliderType === 'capsule';
|
|
@@ -2,6 +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 { useEntityRuntime } from '../PrefabRoot';
|
|
5
6
|
import { BooleanField, ColorField, NumberField } from './Input';
|
|
6
7
|
import { LightSection, ShadowBiasField, mergeWithDefaults } from './lightUtils';
|
|
7
8
|
const pointLightDefaults = {
|
|
@@ -21,7 +22,8 @@ function PointLightComponentEditor({ component, onUpdate }) {
|
|
|
21
22
|
const values = mergeWithDefaults(pointLightDefaults, component.properties);
|
|
22
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] })] }));
|
|
23
24
|
}
|
|
24
|
-
function PointLightView({ properties, children
|
|
25
|
+
function PointLightView({ properties, children }) {
|
|
26
|
+
const { editMode, isSelected } = useEntityRuntime();
|
|
25
27
|
const merged = mergeWithDefaults(pointLightDefaults, properties);
|
|
26
28
|
const color = merged.color;
|
|
27
29
|
const intensity = merged.intensity;
|
|
@@ -4,7 +4,7 @@ import { useThree } from '@react-three/fiber';
|
|
|
4
4
|
import { SoundPicker } from '../../assetviewer/page';
|
|
5
5
|
import { sound as soundManager } from '../../../helpers/SoundManager';
|
|
6
6
|
import { gameEvents } from '../GameEvents';
|
|
7
|
-
import {
|
|
7
|
+
import { useAssetRuntime, useEntityRuntime } from '../PrefabRoot';
|
|
8
8
|
import { BooleanField, FieldGroup, FieldRenderer, ListEditor, NumberField, SelectField, StringField } from './Input';
|
|
9
9
|
import { colors } from '../styles';
|
|
10
10
|
import { AudioListener } from 'three';
|
|
@@ -123,8 +123,9 @@ function payloadMatchesNode(nodeId, payload) {
|
|
|
123
123
|
const hasEntityIds = ids.some(id => typeof id === 'string');
|
|
124
124
|
return hasEntityIds ? ids.includes(nodeId) : true;
|
|
125
125
|
}
|
|
126
|
-
function SoundComponentView({ properties,
|
|
127
|
-
const { getSound } =
|
|
126
|
+
function SoundComponentView({ properties, children }) {
|
|
127
|
+
const { getSound } = useAssetRuntime();
|
|
128
|
+
const { editMode, nodeId } = useEntityRuntime();
|
|
128
129
|
const { camera } = useThree();
|
|
129
130
|
const { eventName, positional = false, refDistance = 1, maxDistance = 24, rolloffFactor = 1, distanceModel = 'inverse' } = properties;
|
|
130
131
|
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 {
|
|
6
|
+
import { useAssetRuntime, useEntityRuntime } from "../PrefabRoot";
|
|
7
7
|
import { useFrame } from "@react-three/fiber";
|
|
8
8
|
import { TexturePicker } from "../../assetviewer/page";
|
|
9
9
|
import { LightSection, ShadowBiasField, mergeWithDefaults } from "./lightUtils";
|
|
@@ -28,9 +28,10 @@ function SpotLightComponentEditor({ component, onUpdate, basePath = "" }) {
|
|
|
28
28
|
const values = mergeWithDefaults(spotLightDefaults, component.properties);
|
|
29
29
|
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: "angle", label: "Angle", values: values, onChange: onUpdate, min: 0, max: Math.PI / 2, step: 0.05, fallback: Math.PI / 3 }), _jsx(NumberField, { name: "penumbra", label: "Penumbra", values: values, onChange: onUpdate, min: 0, max: 1, step: 0.05, fallback: 0 }), _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 }), _jsx(Vector3Input, { label: "Target Offset", value: values.targetOffset, onChange: targetOffset => onUpdate({ targetOffset }), snap: 0.5 }), _jsxs("div", { children: [_jsx(Label, { children: "Texture Map" }), _jsx(TexturePicker, { value: values.map, onChange: (map) => onUpdate({ map }), basePath: basePath })] })] }), _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] })] }));
|
|
30
30
|
}
|
|
31
|
-
function SpotLightView({ properties, children
|
|
31
|
+
function SpotLightView({ properties, children }) {
|
|
32
32
|
var _a;
|
|
33
|
-
const { getTexture } =
|
|
33
|
+
const { getTexture } = useAssetRuntime();
|
|
34
|
+
const { editMode, isSelected } = useEntityRuntime();
|
|
34
35
|
const merged = mergeWithDefaults(spotLightDefaults, properties);
|
|
35
36
|
const color = merged.color;
|
|
36
37
|
const intensity = merged.intensity;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Object3D } from "three";
|
|
1
2
|
import type { GameObject } from "./types";
|
|
2
3
|
export interface SpawnOptions {
|
|
3
4
|
name?: string;
|
|
@@ -19,6 +20,8 @@ export interface Entity {
|
|
|
19
20
|
readonly enabled: boolean;
|
|
20
21
|
readonly parent: Entity | null;
|
|
21
22
|
readonly children: Entity[];
|
|
23
|
+
readonly object: Object3D | null;
|
|
24
|
+
readonly rigidBody: any;
|
|
22
25
|
set: (data: EntityData) => void;
|
|
23
26
|
update: (update: (node: EntityData) => EntityData) => void;
|
|
24
27
|
getComponent: <TProperties = Record<string, any>>(name: string) => EntityComponent<TProperties> | null;
|
|
@@ -30,7 +33,7 @@ export type EntityUpdate = (node: EntityData) => EntityData;
|
|
|
30
33
|
export type SceneUpdates = Record<string, EntityUpdate>;
|
|
31
34
|
export interface Scene {
|
|
32
35
|
readonly rootId: string;
|
|
33
|
-
find: (
|
|
36
|
+
find: (id: string) => Entity | null;
|
|
34
37
|
get: (id: string) => Entity;
|
|
35
38
|
create: (name: string, components?: Record<string, {
|
|
36
39
|
type: string;
|
|
@@ -48,11 +51,12 @@ interface SceneAdapter {
|
|
|
48
51
|
getNode: (id: string) => EntityData | null;
|
|
49
52
|
getChildIds: (id: string) => string[];
|
|
50
53
|
getParentId: (id: string) => string | null;
|
|
51
|
-
findByName: (name: string) => string | null;
|
|
52
54
|
updateNode: (id: string, update: (node: EntityData) => EntityData) => void;
|
|
53
55
|
updateNodes: (updates: Record<string, (node: EntityData) => EntityData>) => void;
|
|
54
56
|
addNode: (node: GameObject, options?: SpawnOptions) => string;
|
|
55
57
|
removeNode: (id: string) => void;
|
|
58
|
+
getObject?: (id: string) => Object3D | null;
|
|
59
|
+
getRigidBody?: (id: string) => any;
|
|
56
60
|
}
|
|
57
61
|
export declare function createScene(adapter: SceneAdapter): Scene;
|
|
58
62
|
export {};
|
|
@@ -119,6 +119,14 @@ export function createScene(adapter) {
|
|
|
119
119
|
get children() {
|
|
120
120
|
return adapter.getChildIds(id).map(createNode);
|
|
121
121
|
},
|
|
122
|
+
get object() {
|
|
123
|
+
var _a, _b;
|
|
124
|
+
return (_b = (_a = adapter.getObject) === null || _a === void 0 ? void 0 : _a.call(adapter, id)) !== null && _b !== void 0 ? _b : null;
|
|
125
|
+
},
|
|
126
|
+
get rigidBody() {
|
|
127
|
+
var _a, _b;
|
|
128
|
+
return (_b = (_a = adapter.getRigidBody) === null || _a === void 0 ? void 0 : _a.call(adapter, id)) !== null && _b !== void 0 ? _b : null;
|
|
129
|
+
},
|
|
122
130
|
set(data) {
|
|
123
131
|
adapter.updateNode(id, () => data);
|
|
124
132
|
},
|
|
@@ -173,11 +181,8 @@ export function createScene(adapter) {
|
|
|
173
181
|
get rootId() {
|
|
174
182
|
return adapter.getRootId();
|
|
175
183
|
},
|
|
176
|
-
find(
|
|
177
|
-
|
|
178
|
-
return createNode(nameOrId);
|
|
179
|
-
const foundId = adapter.findByName(nameOrId);
|
|
180
|
-
return foundId ? createNode(foundId) : null;
|
|
184
|
+
find(id) {
|
|
185
|
+
return adapter.getNode(id) ? createNode(id) : null;
|
|
181
186
|
},
|
|
182
187
|
get: getNode,
|
|
183
188
|
create(name, components, options) {
|
package/package.json
CHANGED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { Object3D } from "three";
|
|
2
|
-
/**
|
|
3
|
-
* RefBridge — O(1) lookup from entity ID to live Three.js Object3D.
|
|
4
|
-
*
|
|
5
|
-
* In play mode the Scene API writes directly to these refs, bypassing
|
|
6
|
-
* React reconciliation. In edit mode the store path is used instead.
|
|
7
|
-
*
|
|
8
|
-
* Component Views register/unregister refs on mount/unmount via
|
|
9
|
-
* `bridge.register(id, obj)` / `bridge.unregister(id)`.
|
|
10
|
-
*/
|
|
11
|
-
export interface RefBridge {
|
|
12
|
-
register: (id: string, obj: Object3D | null) => void;
|
|
13
|
-
unregister: (id: string) => void;
|
|
14
|
-
get: (id: string) => Object3D | null;
|
|
15
|
-
/** Apply a transform component's properties directly to the Object3D. */
|
|
16
|
-
setTransform: (id: string, position?: number[], rotation?: number[], scale?: number[]) => void;
|
|
17
|
-
/** Read current transform from the Object3D back into a properties bag. */
|
|
18
|
-
readTransform: (id: string) => {
|
|
19
|
-
position: [number, number, number];
|
|
20
|
-
rotation: [number, number, number];
|
|
21
|
-
scale: [number, number, number];
|
|
22
|
-
} | null;
|
|
23
|
-
}
|
|
24
|
-
export declare function createRefBridge(): RefBridge;
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
export function createRefBridge() {
|
|
2
|
-
const refs = new Map();
|
|
3
|
-
return {
|
|
4
|
-
register(id, obj) {
|
|
5
|
-
if (obj) {
|
|
6
|
-
refs.set(id, obj);
|
|
7
|
-
}
|
|
8
|
-
else {
|
|
9
|
-
refs.delete(id);
|
|
10
|
-
}
|
|
11
|
-
},
|
|
12
|
-
unregister(id) {
|
|
13
|
-
refs.delete(id);
|
|
14
|
-
},
|
|
15
|
-
get(id) {
|
|
16
|
-
var _a;
|
|
17
|
-
return (_a = refs.get(id)) !== null && _a !== void 0 ? _a : null;
|
|
18
|
-
},
|
|
19
|
-
setTransform(id, position, rotation, scale) {
|
|
20
|
-
const obj = refs.get(id);
|
|
21
|
-
if (!obj)
|
|
22
|
-
return;
|
|
23
|
-
if (position) {
|
|
24
|
-
obj.position.set(position[0], position[1], position[2]);
|
|
25
|
-
}
|
|
26
|
-
if (rotation) {
|
|
27
|
-
obj.rotation.set(rotation[0], rotation[1], rotation[2]);
|
|
28
|
-
}
|
|
29
|
-
if (scale) {
|
|
30
|
-
obj.scale.set(scale[0], scale[1], scale[2]);
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
readTransform(id) {
|
|
34
|
-
const obj = refs.get(id);
|
|
35
|
-
if (!obj)
|
|
36
|
-
return null;
|
|
37
|
-
return {
|
|
38
|
-
position: [obj.position.x, obj.position.y, obj.position.z],
|
|
39
|
-
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
|
|
40
|
-
scale: [obj.scale.x, obj.scale.y, obj.scale.z],
|
|
41
|
-
};
|
|
42
|
-
},
|
|
43
|
-
};
|
|
44
|
-
}
|