react-three-game 0.0.71 → 0.0.73
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/assetviewer/page.js +1 -1
- package/dist/tools/prefabeditor/PrefabEditor.d.ts +0 -2
- package/dist/tools/prefabeditor/PrefabEditor.js +12 -22
- package/dist/tools/prefabeditor/PrefabRoot.d.ts +0 -21
- package/dist/tools/prefabeditor/PrefabRoot.js +26 -50
- 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/components/index.d.ts +1 -2
- package/dist/tools/prefabeditor/components/index.js +1 -1
- package/dist/tools/prefabeditor/runtimeContext.d.ts +40 -0
- package/dist/tools/prefabeditor/runtimeContext.js +45 -0
- 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 } from './tools/prefabeditor/PrefabRoot';
|
|
23
|
+
export type { AssetRuntime, EntityRuntime, LiveObjectRef, LiveRigidBodyRef } from './tools/prefabeditor/runtimeContext';
|
|
24
|
+
export { useAssetRuntime, useEntityRuntime, useEntityObjectRef, useEntityRigidBodyRef } from './tools/prefabeditor/runtimeContext';
|
|
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/runtimeContext';
|
|
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';
|
|
@@ -4,7 +4,7 @@ import { OrbitControls, View, PerspectiveCamera } from "@react-three/drei";
|
|
|
4
4
|
import { Component as ReactComponent, Suspense, useEffect, useLayoutEffect, useState, useRef } from "react";
|
|
5
5
|
import { createPortal } from 'react-dom';
|
|
6
6
|
import { TextureLoader } from "three";
|
|
7
|
-
import { loadModel } from "../dragdrop";
|
|
7
|
+
import { loadModel } from "../dragdrop/modelLoader";
|
|
8
8
|
class ErrorBoundary extends ReactComponent {
|
|
9
9
|
constructor(props) {
|
|
10
10
|
super(props);
|
|
@@ -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,29 +3,8 @@ 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
|
-
import { type RefBridge } from "./RefBridge";
|
|
7
|
-
/** Runtime context available to all component Views inside a PrefabRoot. */
|
|
8
|
-
export interface SceneRuntime {
|
|
9
|
-
refBridge: RefBridge;
|
|
10
|
-
getRigidBody: (id: string) => any;
|
|
11
|
-
/** @internal Used by PhysicsComponent. */
|
|
12
|
-
registerRigidBodyRef: (id: string, rb: any) => void;
|
|
13
|
-
editMode?: boolean;
|
|
14
|
-
/** Get a loaded model by asset path. */
|
|
15
|
-
getModel: (path: string) => Object3D | null;
|
|
16
|
-
/** Get a loaded texture by asset path. */
|
|
17
|
-
getTexture: (path: string) => Texture | null;
|
|
18
|
-
/** Get a loaded sound buffer by asset path. */
|
|
19
|
-
getSound: (path: string) => AudioBuffer | null;
|
|
20
|
-
/** Get a revision string that changes when loaded assets change (for cache-busting keys). */
|
|
21
|
-
getAssetRevision: () => string;
|
|
22
|
-
}
|
|
23
|
-
/** Access the scene runtime (refBridge, getRigidBody, editMode) from within a component View. */
|
|
24
|
-
export declare function useSceneRuntime(): SceneRuntime;
|
|
25
6
|
export interface PrefabRootRef {
|
|
26
7
|
root: Group | null;
|
|
27
|
-
refBridge: RefBridge;
|
|
28
|
-
rigidBodyRefs: Map<string, any>;
|
|
29
8
|
getObject: (nodeId: string) => Object3D | null;
|
|
30
9
|
getRigidBody: (nodeId: string) => any;
|
|
31
10
|
addModel: (path: string, model: Object3D) => void;
|
|
@@ -11,34 +11,26 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
11
11
|
};
|
|
12
12
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
13
|
import { useHelper } from "@react-three/drei";
|
|
14
|
-
import { forwardRef,
|
|
14
|
+
import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
|
|
15
15
|
import { BoxHelper, Euler, Matrix4, } from "three";
|
|
16
16
|
import { useStore } from "zustand";
|
|
17
17
|
import { useClickValid } from "./useClickValid";
|
|
18
18
|
import { findComponent } from "./types";
|
|
19
19
|
import { getComponentDef, getComponentAssetRefs, registerComponent } from "./components/ComponentRegistry";
|
|
20
|
-
import
|
|
20
|
+
import { builtinComponents } from "./components";
|
|
21
21
|
import { loadModel, loadSound, loadTexture } from "../dragdrop";
|
|
22
22
|
import { GameInstance, GameInstanceProvider, getRepeatAxesFromModelProperties, useInstanceCheck } from "./InstanceProvider";
|
|
23
23
|
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 {
|
|
27
|
+
import { AssetRuntimeContext, EntityRuntimeScope } from "./runtimeContext";
|
|
28
28
|
import { sound as soundManager } from "../../helpers/SoundManager";
|
|
29
|
-
|
|
29
|
+
builtinComponents.forEach(registerComponent);
|
|
30
30
|
const IDENTITY = new Matrix4();
|
|
31
31
|
const EMPTY_MODELS = {};
|
|
32
32
|
const EMPTY_TEXTURES = {};
|
|
33
33
|
const EMPTY_SOUNDS = {};
|
|
34
|
-
const SceneRuntimeContext = createContext(null);
|
|
35
|
-
/** Access the scene runtime (refBridge, getRigidBody, editMode) from within a component View. */
|
|
36
|
-
export function useSceneRuntime() {
|
|
37
|
-
const ctx = useContext(SceneRuntimeContext);
|
|
38
|
-
if (!ctx)
|
|
39
|
-
throw new Error("useSceneRuntime must be used inside <PrefabRoot>");
|
|
40
|
-
return ctx;
|
|
41
|
-
}
|
|
42
34
|
/** Resolve a relative or absolute asset file path against a base path. */
|
|
43
35
|
function resolveAssetPath(basePath, file) {
|
|
44
36
|
if (file.startsWith("http://") || file.startsWith("https://"))
|
|
@@ -68,7 +60,6 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
68
60
|
const objectRefs = useRef({});
|
|
69
61
|
const rigidBodyRefs = useRef(new Map());
|
|
70
62
|
const rootRef = useRef(null);
|
|
71
|
-
const [refBridge] = useState(() => createRefBridge());
|
|
72
63
|
const parentStore = useOptionalPrefabStoreApi();
|
|
73
64
|
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
65
|
const resolvedStore = (_a = store !== null && store !== void 0 ? store : parentStore) !== null && _a !== void 0 ? _a : ownedStore;
|
|
@@ -78,11 +69,13 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
78
69
|
const availableModels = useMemo(() => (Object.assign(Object.assign({}, models), injectedModels)), [models, injectedModels]);
|
|
79
70
|
const availableTextures = useMemo(() => (Object.assign(Object.assign({}, textures), injectedTextures)), [textures, injectedTextures]);
|
|
80
71
|
const availableSounds = useMemo(() => (Object.assign(Object.assign({}, sounds), injectedSounds)), [sounds, injectedSounds]);
|
|
72
|
+
const getObject = useCallback((id) => {
|
|
73
|
+
var _a;
|
|
74
|
+
return (_a = objectRefs.current[id]) !== null && _a !== void 0 ? _a : null;
|
|
75
|
+
}, []);
|
|
81
76
|
useImperativeHandle(ref, () => ({
|
|
82
77
|
root: rootRef.current,
|
|
83
|
-
|
|
84
|
-
rigidBodyRefs: rigidBodyRefs.current,
|
|
85
|
-
getObject: (nodeId) => { var _a; return (_a = objectRefs.current[nodeId]) !== null && _a !== void 0 ? _a : null; },
|
|
78
|
+
getObject,
|
|
86
79
|
getRigidBody: (nodeId) => { var _a; return (_a = rigidBodyRefs.current.get(nodeId)) !== null && _a !== void 0 ? _a : null; },
|
|
87
80
|
addModel: (path, model) => setInjectedModels(prev => (Object.assign(Object.assign({}, prev), { [path]: model }))),
|
|
88
81
|
addTexture: (path, texture) => setInjectedTextures(prev => (Object.assign(Object.assign({}, prev), { [path]: texture }))),
|
|
@@ -90,11 +83,10 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
90
83
|
soundManager.setBuffer(path, sound);
|
|
91
84
|
setInjectedSounds(prev => (Object.assign(Object.assign({}, prev), { [path]: sound })));
|
|
92
85
|
},
|
|
93
|
-
}), [
|
|
86
|
+
}), [getObject]);
|
|
94
87
|
const registerRef = useCallback((id, obj) => {
|
|
95
88
|
objectRefs.current[id] = obj;
|
|
96
|
-
|
|
97
|
-
}, [refBridge]);
|
|
89
|
+
}, []);
|
|
98
90
|
const registerRigidBodyRef = useCallback((id, rb) => {
|
|
99
91
|
rigidBodyRefs.current.set(id, rb);
|
|
100
92
|
}, []);
|
|
@@ -180,11 +172,10 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
180
172
|
availableTexturesRef.current = availableTextures;
|
|
181
173
|
const availableSoundsRef = useRef(availableSounds);
|
|
182
174
|
availableSoundsRef.current = availableSounds;
|
|
183
|
-
const
|
|
184
|
-
|
|
175
|
+
const assetRuntime = useMemo(() => ({
|
|
176
|
+
getObject,
|
|
185
177
|
getRigidBody,
|
|
186
178
|
registerRigidBodyRef,
|
|
187
|
-
editMode,
|
|
188
179
|
getModel: (path) => { var _a; return (_a = availableModelsRef.current[path]) !== null && _a !== void 0 ? _a : null; },
|
|
189
180
|
getTexture: (path) => { var _a; return (_a = availableTexturesRef.current[path]) !== null && _a !== void 0 ? _a : null; },
|
|
190
181
|
getSound: (path) => { var _a; return (_a = availableSoundsRef.current[path]) !== null && _a !== void 0 ? _a : null; },
|
|
@@ -193,12 +184,12 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
193
184
|
const textureKeys = Object.keys(availableTexturesRef.current).sort().join('|');
|
|
194
185
|
return `${textureKeys}::${modelKeys}`;
|
|
195
186
|
},
|
|
196
|
-
}), [
|
|
187
|
+
}), [getObject, getRigidBody, registerRigidBodyRef]);
|
|
197
188
|
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
189
|
if (!shouldProvideStoreContext) {
|
|
199
|
-
return _jsx(
|
|
190
|
+
return _jsx(AssetRuntimeContext.Provider, { value: assetRuntime, children: content });
|
|
200
191
|
}
|
|
201
|
-
return _jsx(PrefabStoreProvider, { store: resolvedStore, children: _jsx(
|
|
192
|
+
return _jsx(PrefabStoreProvider, { store: resolvedStore, children: _jsx(AssetRuntimeContext.Provider, { value: assetRuntime, children: content }) });
|
|
202
193
|
});
|
|
203
194
|
function StoreRootNode(props) {
|
|
204
195
|
const rootId = usePrefabRootId();
|
|
@@ -289,14 +280,8 @@ function StandardNode({ nodeId, selectedId, onSelect, onClick, registerRef, load
|
|
|
289
280
|
const childNodes = getChildHostComponents(gameObject).length > 0
|
|
290
281
|
? _jsx(CompositionChildren, { childIds: childIds, selectedId: selectedId, ctx: renderCtx, parentMatrix: world })
|
|
291
282
|
: _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 }));
|
|
283
|
+
const inner = (_jsx("group", Object.assign({}, clickHandlers, { children: renderCompositionNode(gameObject, renderCtx, childNodes) })));
|
|
284
|
+
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
285
|
}
|
|
301
286
|
function getChildHostComponents(gameObject) {
|
|
302
287
|
var _a;
|
|
@@ -385,7 +370,7 @@ function renderCompositionSubtree(gameObject, ctx, isSelected, childIds, parentM
|
|
|
385
370
|
const transform = getNodeTransformProps(gameObject);
|
|
386
371
|
const world = parentMatrix.clone().multiply(compose(gameObject));
|
|
387
372
|
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,
|
|
373
|
+
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
374
|
}
|
|
390
375
|
function CompositionChildren({ childIds, selectedId, ctx, parentMatrix, }) {
|
|
391
376
|
return childIds.map(childId => (_jsx(CompositionSubtree, { nodeId: childId, selectedId: selectedId, ctx: ctx, parentMatrix: parentMatrix }, childId)));
|
|
@@ -398,31 +383,22 @@ function CompositionSubtree({ nodeId, selectedId, ctx, parentMatrix, }) {
|
|
|
398
383
|
return null;
|
|
399
384
|
return renderCompositionSubtree(gameObject, ctx, isSelected, childIds, parentMatrix);
|
|
400
385
|
}
|
|
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
|
-
};
|
|
386
|
+
function renderCompositionNode(gameObject, ctx, childNodes) {
|
|
387
|
+
const ownContent = renderNodeOwnContent(gameObject);
|
|
388
|
+
return wrapWithChildHosts(gameObject, _jsxs(_Fragment, { children: [ownContent, childNodes] }));
|
|
411
389
|
}
|
|
412
|
-
function renderNodeOwnContent(gameObject
|
|
390
|
+
function renderNodeOwnContent(gameObject) {
|
|
413
391
|
const geometry = findComponent(gameObject, "Geometry");
|
|
414
392
|
const material = findComponent(gameObject, "Material");
|
|
415
393
|
const geometryDef = geometry && getComponentDef(geometry.type);
|
|
416
394
|
const materialDef = material && getComponentDef(material.type);
|
|
417
395
|
if (!geometry || !(geometryDef === null || geometryDef === void 0 ? void 0 : geometryDef.View))
|
|
418
396
|
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"))] }));
|
|
397
|
+
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
398
|
}
|
|
422
|
-
function wrapWithChildHosts(gameObject,
|
|
423
|
-
const contextProps = buildContextProps(gameObject, ctx, isSelected);
|
|
399
|
+
function wrapWithChildHosts(gameObject, subtree) {
|
|
424
400
|
const childHosts = getChildHostComponents(gameObject);
|
|
425
|
-
return childHosts.reduce((acc, { key, View, properties }) => (_jsx(View,
|
|
401
|
+
return childHosts.reduce((acc, { key, View, properties }) => (_jsx(View, { properties: properties, children: acc }, key)), subtree);
|
|
426
402
|
}
|
|
427
403
|
export default PrefabRoot;
|
|
428
404
|
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 '../runtimeContext';
|
|
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 '../runtimeContext';
|
|
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 "../runtimeContext";
|
|
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 '../runtimeContext';
|
|
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 '../runtimeContext';
|
|
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 '../runtimeContext';
|
|
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 "../runtimeContext";
|
|
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 '../runtimeContext';
|
|
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 '../runtimeContext';
|
|
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 "../runtimeContext";
|
|
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,2 +1 @@
|
|
|
1
|
-
declare const
|
|
2
|
-
export default _default;
|
|
1
|
+
export declare const builtinComponents: import("./ComponentRegistry").Component[];
|
|
@@ -12,7 +12,7 @@ import EnvironmentComponent from './EnvironmentComponent';
|
|
|
12
12
|
import CameraComponent from './CameraComponent';
|
|
13
13
|
import ClickComponent from './ClickComponent';
|
|
14
14
|
import SoundComponent from './SoundComponent';
|
|
15
|
-
export
|
|
15
|
+
export const builtinComponents = [
|
|
16
16
|
GeometryComponent,
|
|
17
17
|
TransformComponent,
|
|
18
18
|
MaterialComponent,
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { Object3D, Texture } from "three";
|
|
3
|
+
type RuntimeScopeProps = {
|
|
4
|
+
nodeId: string;
|
|
5
|
+
editMode?: boolean;
|
|
6
|
+
isSelected?: boolean;
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
};
|
|
9
|
+
export interface AssetRuntime {
|
|
10
|
+
registerRigidBodyRef: (id: string, rb: any) => void;
|
|
11
|
+
getModel: (path: string) => Object3D | null;
|
|
12
|
+
getTexture: (path: string) => Texture | null;
|
|
13
|
+
getSound: (path: string) => AudioBuffer | null;
|
|
14
|
+
getAssetRevision: () => string;
|
|
15
|
+
}
|
|
16
|
+
export interface AssetRuntimeContextValue extends AssetRuntime {
|
|
17
|
+
getObject: (id: string) => Object3D | null;
|
|
18
|
+
getRigidBody: (id: string) => any;
|
|
19
|
+
}
|
|
20
|
+
export interface EntityRuntime {
|
|
21
|
+
nodeId: string;
|
|
22
|
+
editMode?: boolean;
|
|
23
|
+
isSelected?: boolean;
|
|
24
|
+
getObject: <T extends Object3D = Object3D>() => T | null;
|
|
25
|
+
getRigidBody: <T = any>() => T | null;
|
|
26
|
+
}
|
|
27
|
+
export interface LiveObjectRef<T extends Object3D = Object3D> {
|
|
28
|
+
readonly current: T | null;
|
|
29
|
+
}
|
|
30
|
+
export interface LiveRigidBodyRef<T = any> {
|
|
31
|
+
readonly current: T | null;
|
|
32
|
+
}
|
|
33
|
+
export declare const AssetRuntimeContext: import("react").Context<AssetRuntimeContextValue | null>;
|
|
34
|
+
export declare const EntityRuntimeContext: import("react").Context<EntityRuntime | null>;
|
|
35
|
+
export declare function useAssetRuntime(): AssetRuntime;
|
|
36
|
+
export declare function useEntityRuntime(): EntityRuntime;
|
|
37
|
+
export declare function useEntityObjectRef<T extends Object3D = Object3D>(): LiveObjectRef<T>;
|
|
38
|
+
export declare function useEntityRigidBodyRef<T = any>(): LiveRigidBodyRef<T>;
|
|
39
|
+
export declare function EntityRuntimeScope({ nodeId, editMode, isSelected, children, }: RuntimeScopeProps): import("react/jsx-runtime").JSX.Element;
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext, useMemo } from "react";
|
|
3
|
+
function createLiveRef(getCurrent) {
|
|
4
|
+
return {
|
|
5
|
+
get current() {
|
|
6
|
+
return getCurrent();
|
|
7
|
+
},
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export const AssetRuntimeContext = createContext(null);
|
|
11
|
+
export const EntityRuntimeContext = createContext(null);
|
|
12
|
+
export function useAssetRuntime() {
|
|
13
|
+
const ctx = useContext(AssetRuntimeContext);
|
|
14
|
+
if (!ctx)
|
|
15
|
+
throw new Error("useAssetRuntime must be used inside <PrefabRoot>");
|
|
16
|
+
return ctx;
|
|
17
|
+
}
|
|
18
|
+
export function useEntityRuntime() {
|
|
19
|
+
const ctx = useContext(EntityRuntimeContext);
|
|
20
|
+
if (!ctx)
|
|
21
|
+
throw new Error("useEntityRuntime must be used inside a component View rendered by <PrefabRoot>");
|
|
22
|
+
return ctx;
|
|
23
|
+
}
|
|
24
|
+
export function useEntityObjectRef() {
|
|
25
|
+
const { getObject } = useEntityRuntime();
|
|
26
|
+
return useMemo(() => createLiveRef(() => getObject()), [getObject]);
|
|
27
|
+
}
|
|
28
|
+
export function useEntityRigidBodyRef() {
|
|
29
|
+
const { getRigidBody } = useEntityRuntime();
|
|
30
|
+
return useMemo(() => createLiveRef(() => getRigidBody()), [getRigidBody]);
|
|
31
|
+
}
|
|
32
|
+
export function EntityRuntimeScope({ nodeId, editMode, isSelected, children, }) {
|
|
33
|
+
const assetRuntime = useContext(AssetRuntimeContext);
|
|
34
|
+
if (!assetRuntime)
|
|
35
|
+
throw new Error("EntityRuntimeScope must be used inside <PrefabRoot>");
|
|
36
|
+
const { getObject, getRigidBody } = assetRuntime;
|
|
37
|
+
const value = useMemo(() => ({
|
|
38
|
+
nodeId,
|
|
39
|
+
editMode,
|
|
40
|
+
isSelected,
|
|
41
|
+
getObject: () => getObject(nodeId),
|
|
42
|
+
getRigidBody: () => getRigidBody(nodeId),
|
|
43
|
+
}), [editMode, getObject, getRigidBody, isSelected, nodeId]);
|
|
44
|
+
return _jsx(EntityRuntimeContext.Provider, { value: value, children: children });
|
|
45
|
+
}
|
|
@@ -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
|
-
}
|