react-three-game 0.0.69 → 0.0.71
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/helpers/SoundManager.d.ts +2 -0
- package/dist/helpers/SoundManager.js +6 -0
- package/dist/index.d.ts +20 -13
- package/dist/index.js +14 -7
- package/dist/shared/GameCanvas.js +0 -2
- package/dist/tools/assetviewer/page.d.ts +5 -0
- package/dist/tools/assetviewer/page.js +3 -0
- package/dist/tools/dragdrop/DragDropLoader.d.ts +3 -2
- package/dist/tools/dragdrop/DragDropLoader.js +18 -3
- package/dist/tools/dragdrop/index.d.ts +2 -2
- package/dist/tools/dragdrop/index.js +1 -1
- package/dist/tools/dragdrop/modelLoader.d.ts +10 -0
- package/dist/tools/dragdrop/modelLoader.js +60 -0
- package/dist/tools/prefabeditor/EditorTree.js +6 -40
- package/dist/tools/prefabeditor/EditorTreeMenus.js +2 -20
- package/dist/tools/prefabeditor/EditorUI.js +8 -5
- package/dist/tools/prefabeditor/InstanceProvider.d.ts +2 -0
- package/dist/tools/prefabeditor/InstanceProvider.js +54 -52
- package/dist/tools/prefabeditor/PrefabEditor.d.ts +23 -1
- package/dist/tools/prefabeditor/PrefabEditor.js +79 -47
- package/dist/tools/prefabeditor/PrefabRoot.d.ts +26 -9
- package/dist/tools/prefabeditor/PrefabRoot.js +195 -159
- package/dist/tools/prefabeditor/RefBridge.d.ts +24 -0
- package/dist/tools/prefabeditor/RefBridge.js +44 -0
- package/dist/tools/prefabeditor/components/AmbientLightComponent.js +10 -7
- package/dist/tools/prefabeditor/components/CameraComponent.js +8 -14
- package/dist/tools/prefabeditor/components/ClickComponent.js +12 -7
- package/dist/tools/prefabeditor/components/ComponentRegistry.d.ts +31 -5
- package/dist/tools/prefabeditor/components/ComponentRegistry.js +6 -6
- package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +124 -52
- package/dist/tools/prefabeditor/components/EnvironmentComponent.js +5 -3
- package/dist/tools/prefabeditor/components/GeometryComponent.js +1 -1
- package/dist/tools/prefabeditor/components/Input.d.ts +16 -0
- package/dist/tools/prefabeditor/components/Input.js +33 -0
- package/dist/tools/prefabeditor/components/MaterialComponent.js +19 -8
- package/dist/tools/prefabeditor/components/ModelComponent.js +39 -45
- package/dist/tools/prefabeditor/components/PhysicsComponent.d.ts +10 -1
- package/dist/tools/prefabeditor/components/PhysicsComponent.js +127 -31
- package/dist/tools/prefabeditor/components/PointLightComponent.d.ts +3 -0
- package/dist/tools/prefabeditor/components/PointLightComponent.js +55 -0
- package/dist/tools/prefabeditor/components/SoundComponent.d.ts +3 -0
- package/dist/tools/prefabeditor/components/SoundComponent.js +244 -0
- package/dist/tools/prefabeditor/components/SpotLightComponent.js +53 -24
- package/dist/tools/prefabeditor/components/TransformComponent.js +2 -2
- package/dist/tools/prefabeditor/components/index.js +4 -0
- package/dist/tools/prefabeditor/components/lightUtils.d.ts +13 -0
- package/dist/tools/prefabeditor/components/lightUtils.js +64 -0
- package/dist/tools/prefabeditor/prefab.d.ts +37 -0
- package/dist/tools/prefabeditor/prefab.js +229 -0
- package/dist/tools/prefabeditor/prefabStore.d.ts +4 -16
- package/dist/tools/prefabeditor/prefabStore.js +32 -173
- package/dist/tools/prefabeditor/{sceneApi.d.ts → scene.d.ts} +15 -1
- package/dist/tools/prefabeditor/{sceneApi.js → scene.js} +66 -32
- package/dist/tools/prefabeditor/styles.d.ts +1 -0
- package/dist/tools/prefabeditor/styles.js +9 -0
- package/dist/tools/prefabeditor/types.d.ts +13 -0
- package/dist/tools/prefabeditor/types.js +28 -1
- package/dist/tools/prefabeditor/useClickValid.d.ts +13 -0
- package/dist/tools/prefabeditor/useClickValid.js +21 -0
- package/dist/tools/prefabeditor/utils.d.ts +2 -4
- package/dist/tools/prefabeditor/utils.js +8 -46
- package/package.json +1 -1
- package/dist/tools/prefabeditor/EditorContext.d.ts +0 -16
- package/dist/tools/prefabeditor/EditorContext.js +0 -9
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
1
|
var __rest = (this && this.__rest) || function (s, e) {
|
|
11
2
|
var t = {};
|
|
12
3
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
@@ -20,47 +11,97 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
20
11
|
};
|
|
21
12
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
22
13
|
import { useHelper } from "@react-three/drei";
|
|
23
|
-
import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
|
|
24
|
-
import { BoxHelper, Euler, Matrix4,
|
|
14
|
+
import { forwardRef, createContext, useCallback, useContext, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
|
|
15
|
+
import { BoxHelper, Euler, Matrix4, } from "three";
|
|
25
16
|
import { useStore } from "zustand";
|
|
26
|
-
import {
|
|
17
|
+
import { useClickValid } from "./useClickValid";
|
|
18
|
+
import { findComponent } from "./types";
|
|
19
|
+
import { getComponentDef, getComponentAssetRefs, registerComponent } from "./components/ComponentRegistry";
|
|
27
20
|
import components from "./components";
|
|
28
|
-
import { loadModel, loadTexture } from "../dragdrop";
|
|
21
|
+
import { loadModel, loadSound, loadTexture } from "../dragdrop";
|
|
29
22
|
import { GameInstance, GameInstanceProvider, getRepeatAxesFromModelProperties, useInstanceCheck } from "./InstanceProvider";
|
|
30
|
-
import { decompose } from "./utils";
|
|
31
|
-
import {
|
|
23
|
+
import { composeTransform, decompose } from "./utils";
|
|
24
|
+
import { isPhysicsProps } from "./components/PhysicsComponent";
|
|
25
|
+
import { denormalizePrefab } from "./prefab";
|
|
26
|
+
import { createPrefabStore, PrefabStoreProvider, useOptionalPrefabStoreApi, usePrefabChildIds, usePrefabNode, usePrefabRootId } from "./prefabStore";
|
|
27
|
+
import { createRefBridge } from "./RefBridge";
|
|
28
|
+
import { sound as soundManager } from "../../helpers/SoundManager";
|
|
32
29
|
components.forEach(registerComponent);
|
|
33
30
|
const IDENTITY = new Matrix4();
|
|
34
31
|
const EMPTY_MODELS = {};
|
|
35
32
|
const EMPTY_TEXTURES = {};
|
|
36
|
-
|
|
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
|
+
/** Resolve a relative or absolute asset file path against a base path. */
|
|
43
|
+
function resolveAssetPath(basePath, file) {
|
|
44
|
+
if (file.startsWith("http://") || file.startsWith("https://"))
|
|
45
|
+
return file;
|
|
46
|
+
return file.startsWith("/") ? `${basePath}${file}` : `${basePath}/${file}`;
|
|
47
|
+
}
|
|
48
|
+
/** Check if all model assets required by a node are loaded. */
|
|
49
|
+
function isNodeReady(node, loadedModels) {
|
|
50
|
+
var _a;
|
|
51
|
+
const model = findComponent(node, "Model");
|
|
52
|
+
if (!((_a = model === null || model === void 0 ? void 0 : model.properties) === null || _a === void 0 ? void 0 : _a.filename))
|
|
53
|
+
return true;
|
|
54
|
+
return Boolean(loadedModels[model.properties.filename]);
|
|
55
|
+
}
|
|
56
|
+
export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSelect, onClick, basePath = "" }, ref) => {
|
|
57
|
+
var _a;
|
|
37
58
|
const [models, setModels] = useState({});
|
|
38
59
|
const [textures, setTextures] = useState({});
|
|
60
|
+
const [sounds, setSounds] = useState({});
|
|
61
|
+
const [injectedModels, setInjectedModels] = useState(EMPTY_MODELS);
|
|
62
|
+
const [injectedTextures, setInjectedTextures] = useState(EMPTY_TEXTURES);
|
|
63
|
+
const [injectedSounds, setInjectedSounds] = useState(EMPTY_SOUNDS);
|
|
39
64
|
const loading = useRef(new Set());
|
|
65
|
+
const failedModels = useRef(new Set());
|
|
40
66
|
const failedTextures = useRef(new Set());
|
|
67
|
+
const failedSounds = useRef(new Set());
|
|
41
68
|
const objectRefs = useRef({});
|
|
42
69
|
const rigidBodyRefs = useRef(new Map());
|
|
43
70
|
const rootRef = useRef(null);
|
|
44
|
-
const [
|
|
45
|
-
const
|
|
46
|
-
const
|
|
71
|
+
const [refBridge] = useState(() => createRefBridge());
|
|
72
|
+
const parentStore = useOptionalPrefabStoreApi();
|
|
73
|
+
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
|
+
const resolvedStore = (_a = store !== null && store !== void 0 ? store : parentStore) !== null && _a !== void 0 ? _a : ownedStore;
|
|
75
|
+
const usesOwnedStore = resolvedStore === ownedStore;
|
|
76
|
+
const shouldProvideStoreContext = !parentStore || parentStore !== resolvedStore;
|
|
77
|
+
const assetManifestKey = useStore(resolvedStore, state => state.assetManifestKey);
|
|
47
78
|
const availableModels = useMemo(() => (Object.assign(Object.assign({}, models), injectedModels)), [models, injectedModels]);
|
|
48
79
|
const availableTextures = useMemo(() => (Object.assign(Object.assign({}, textures), injectedTextures)), [textures, injectedTextures]);
|
|
80
|
+
const availableSounds = useMemo(() => (Object.assign(Object.assign({}, sounds), injectedSounds)), [sounds, injectedSounds]);
|
|
49
81
|
useImperativeHandle(ref, () => ({
|
|
50
82
|
root: rootRef.current,
|
|
83
|
+
refBridge,
|
|
51
84
|
rigidBodyRefs: rigidBodyRefs.current,
|
|
52
85
|
getObject: (nodeId) => { var _a; return (_a = objectRefs.current[nodeId]) !== null && _a !== void 0 ? _a : null; },
|
|
53
|
-
|
|
54
|
-
|
|
86
|
+
getRigidBody: (nodeId) => { var _a; return (_a = rigidBodyRefs.current.get(nodeId)) !== null && _a !== void 0 ? _a : null; },
|
|
87
|
+
addModel: (path, model) => setInjectedModels(prev => (Object.assign(Object.assign({}, prev), { [path]: model }))),
|
|
88
|
+
addTexture: (path, texture) => setInjectedTextures(prev => (Object.assign(Object.assign({}, prev), { [path]: texture }))),
|
|
89
|
+
addSound: (path, sound) => {
|
|
90
|
+
soundManager.setBuffer(path, sound);
|
|
91
|
+
setInjectedSounds(prev => (Object.assign(Object.assign({}, prev), { [path]: sound })));
|
|
92
|
+
},
|
|
93
|
+
}), [refBridge]);
|
|
55
94
|
const registerRef = useCallback((id, obj) => {
|
|
56
95
|
objectRefs.current[id] = obj;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
}, [onSelectedObjectChange, selectedId]);
|
|
96
|
+
refBridge.register(id, obj);
|
|
97
|
+
}, [refBridge]);
|
|
61
98
|
const registerRigidBodyRef = useCallback((id, rb) => {
|
|
62
99
|
rigidBodyRefs.current.set(id, rb);
|
|
63
100
|
}, []);
|
|
101
|
+
const getRigidBody = useCallback((id) => {
|
|
102
|
+
var _a;
|
|
103
|
+
return (_a = rigidBodyRefs.current.get(id)) !== null && _a !== void 0 ? _a : null;
|
|
104
|
+
}, []);
|
|
64
105
|
useEffect(() => {
|
|
65
106
|
const originalError = console.error;
|
|
66
107
|
console.error = (...args) => {
|
|
@@ -71,78 +112,102 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
71
112
|
return () => { console.error = originalError; };
|
|
72
113
|
}, []);
|
|
73
114
|
useEffect(() => {
|
|
74
|
-
if (
|
|
75
|
-
|
|
115
|
+
if (usesOwnedStore && data) {
|
|
116
|
+
resolvedStore.getState().replacePrefab(data);
|
|
76
117
|
}
|
|
77
|
-
}, [data,
|
|
118
|
+
}, [data, resolvedStore, usesOwnedStore]);
|
|
78
119
|
useEffect(() => {
|
|
79
|
-
const syncAssets = (snapshot =
|
|
120
|
+
const syncAssets = (snapshot = resolvedStore.getState()) => {
|
|
80
121
|
const modelsToLoad = new Set();
|
|
81
122
|
const texturesToLoad = new Set();
|
|
123
|
+
const soundsToLoad = new Set();
|
|
82
124
|
Object.values(snapshot.nodesById).forEach(node => {
|
|
83
125
|
var _a;
|
|
84
126
|
Object.values((_a = node.components) !== null && _a !== void 0 ? _a : {}).forEach(component => {
|
|
85
|
-
var _a
|
|
127
|
+
var _a;
|
|
86
128
|
if (!(component === null || component === void 0 ? void 0 : component.type))
|
|
87
129
|
return;
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (component.type === 'SpotLight' && ((_d = component.properties) === null || _d === void 0 ? void 0 : _d.map)) {
|
|
96
|
-
texturesToLoad.add(component.properties.map);
|
|
130
|
+
for (const ref of getComponentAssetRefs(component.type, (_a = component.properties) !== null && _a !== void 0 ? _a : {})) {
|
|
131
|
+
if (ref.type === 'model')
|
|
132
|
+
modelsToLoad.add(ref.path);
|
|
133
|
+
else if (ref.type === 'texture')
|
|
134
|
+
texturesToLoad.add(ref.path);
|
|
135
|
+
else if (ref.type === 'sound')
|
|
136
|
+
soundsToLoad.add(ref.path);
|
|
97
137
|
}
|
|
98
138
|
});
|
|
99
139
|
});
|
|
100
|
-
|
|
101
|
-
if (
|
|
140
|
+
const loadAsset = (file, loaded, injected, failed, loader) => {
|
|
141
|
+
if (loaded[file] || injected[file] || loading.current.has(file) || failed.has(file))
|
|
102
142
|
return;
|
|
103
143
|
loading.current.add(file);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
144
|
+
void loader(resolveAssetPath(basePath, file)).then(result => {
|
|
145
|
+
if (!result.success) {
|
|
146
|
+
console.warn(`Failed to load asset: ${file}`, result.error);
|
|
147
|
+
loading.current.delete(file);
|
|
148
|
+
failed.add(file);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
};
|
|
152
|
+
modelsToLoad.forEach(file => loadAsset(file, models, injectedModels, failedModels.current, (path) => loadModel(path).then(result => {
|
|
153
|
+
const model = result.model;
|
|
154
|
+
if (result.success && model) {
|
|
110
155
|
setModels(m => (Object.assign(Object.assign({}, m), { [file]: model })));
|
|
111
156
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (result.success && result.texture) {
|
|
125
|
-
setTextures(t => (Object.assign(Object.assign({}, t), { [file]: result.texture })));
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
console.warn(`Failed to load texture: ${path}`, result.error);
|
|
157
|
+
return result;
|
|
158
|
+
})));
|
|
159
|
+
texturesToLoad.forEach(file => loadAsset(file, textures, injectedTextures, failedTextures.current, (path) => loadTexture(path).then(result => {
|
|
160
|
+
if (result.success && result.texture) {
|
|
161
|
+
setTextures(t => (Object.assign(Object.assign({}, t), { [file]: result.texture })));
|
|
162
|
+
}
|
|
163
|
+
return result;
|
|
164
|
+
})));
|
|
165
|
+
soundsToLoad.forEach(file => loadAsset(file, sounds, injectedSounds, failedSounds.current, (path) => loadSound(path).then(result => {
|
|
166
|
+
if (result.success && result.sound) {
|
|
167
|
+
soundManager.setBuffer(file, result.sound);
|
|
168
|
+
setSounds(current => (Object.assign(Object.assign({}, current), { [file]: result.sound })));
|
|
129
169
|
loading.current.delete(file);
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
});
|
|
170
|
+
}
|
|
171
|
+
return result;
|
|
172
|
+
})));
|
|
133
173
|
};
|
|
134
174
|
syncAssets();
|
|
135
|
-
}, [
|
|
136
|
-
|
|
175
|
+
}, [resolvedStore, assetManifestKey, basePath, injectedModels, injectedSounds, injectedTextures, models, sounds, textures]);
|
|
176
|
+
// Keep refs current so context getters are always fresh without changing context identity
|
|
177
|
+
const availableModelsRef = useRef(availableModels);
|
|
178
|
+
availableModelsRef.current = availableModels;
|
|
179
|
+
const availableTexturesRef = useRef(availableTextures);
|
|
180
|
+
availableTexturesRef.current = availableTextures;
|
|
181
|
+
const availableSoundsRef = useRef(availableSounds);
|
|
182
|
+
availableSoundsRef.current = availableSounds;
|
|
183
|
+
const sceneRuntime = useMemo(() => ({
|
|
184
|
+
refBridge,
|
|
185
|
+
getRigidBody,
|
|
186
|
+
registerRigidBodyRef,
|
|
187
|
+
editMode,
|
|
188
|
+
getModel: (path) => { var _a; return (_a = availableModelsRef.current[path]) !== null && _a !== void 0 ? _a : null; },
|
|
189
|
+
getTexture: (path) => { var _a; return (_a = availableTexturesRef.current[path]) !== null && _a !== void 0 ? _a : null; },
|
|
190
|
+
getSound: (path) => { var _a; return (_a = availableSoundsRef.current[path]) !== null && _a !== void 0 ? _a : null; },
|
|
191
|
+
getAssetRevision: () => {
|
|
192
|
+
const modelKeys = Object.keys(availableModelsRef.current).sort().join('|');
|
|
193
|
+
const textureKeys = Object.keys(availableTexturesRef.current).sort().join('|');
|
|
194
|
+
return `${textureKeys}::${modelKeys}`;
|
|
195
|
+
},
|
|
196
|
+
}), [refBridge, getRigidBody, registerRigidBodyRef, editMode]);
|
|
197
|
+
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
|
+
if (!shouldProvideStoreContext) {
|
|
199
|
+
return _jsx(SceneRuntimeContext.Provider, { value: sceneRuntime, children: content });
|
|
200
|
+
}
|
|
201
|
+
return _jsx(PrefabStoreProvider, { store: resolvedStore, children: _jsx(SceneRuntimeContext.Provider, { value: sceneRuntime, children: content }) });
|
|
137
202
|
});
|
|
138
203
|
function StoreRootNode(props) {
|
|
139
204
|
const rootId = usePrefabRootId();
|
|
140
205
|
return _jsx(GameObjectRenderer, Object.assign({}, props, { nodeId: rootId }));
|
|
141
206
|
}
|
|
142
207
|
export function GameObjectRenderer(props) {
|
|
143
|
-
var _a, _b
|
|
208
|
+
var _a, _b;
|
|
144
209
|
const node = usePrefabNode(props.nodeId);
|
|
145
|
-
const isInstanced = (
|
|
210
|
+
const isInstanced = (_b = (_a = findComponent(node, "Model")) === null || _a === void 0 ? void 0 : _a.properties) === null || _b === void 0 ? void 0 : _b.instanced;
|
|
146
211
|
const prevInstancedRef = useRef(undefined);
|
|
147
212
|
const [isTransitioning, setIsTransitioning] = useState(false);
|
|
148
213
|
useEffect(() => {
|
|
@@ -160,24 +225,27 @@ export function GameObjectRenderer(props) {
|
|
|
160
225
|
? _jsx(InstancedNode, Object.assign({}, props), key)
|
|
161
226
|
: _jsx(StandardNode, Object.assign({}, props), key);
|
|
162
227
|
}
|
|
163
|
-
function isPhysicsProps(v) {
|
|
164
|
-
return (v === null || v === void 0 ? void 0 : v.type) === "fixed" || (v === null || v === void 0 ? void 0 : v.type) === "dynamic" || (v === null || v === void 0 ? void 0 : v.type) === "kinematicPosition" || (v === null || v === void 0 ? void 0 : v.type) === "kinematicVelocity";
|
|
165
|
-
}
|
|
166
228
|
function InstancedNode({ nodeId, parentMatrix = IDENTITY, editMode, registerRef, onSelect, onClick }) {
|
|
167
|
-
var _a, _b, _c
|
|
229
|
+
var _a, _b, _c;
|
|
168
230
|
const gameObject = usePrefabNode(nodeId);
|
|
169
231
|
if (!gameObject)
|
|
170
232
|
return null;
|
|
171
233
|
const localTransform = getNodeTransformProps(gameObject);
|
|
172
234
|
const isLocked = Boolean(gameObject.locked);
|
|
173
|
-
const
|
|
174
|
-
const
|
|
175
|
-
|
|
235
|
+
const clickComponent = findComponent(gameObject, "Click");
|
|
236
|
+
const clickable = Boolean(clickComponent);
|
|
237
|
+
const clickEventName = (_a = clickComponent === null || clickComponent === void 0 ? void 0 : clickComponent.properties) === null || _a === void 0 ? void 0 : _a.eventName;
|
|
238
|
+
const physicsData = findComponent(gameObject, "Physics");
|
|
239
|
+
const physicsProps = isPhysicsProps(physicsData === null || physicsData === void 0 ? void 0 : physicsData.properties)
|
|
240
|
+
? physicsData === null || physicsData === void 0 ? void 0 : physicsData.properties
|
|
176
241
|
: undefined;
|
|
177
|
-
const modelUrl = (
|
|
242
|
+
const modelUrl = (_c = (_b = findComponent(gameObject, "Model")) === null || _b === void 0 ? void 0 : _b.properties) === null || _c === void 0 ? void 0 : _c.filename;
|
|
178
243
|
const instances = useMemo(() => buildRepeatedInstances(gameObject, parentMatrix, modelUrl, physicsProps), [gameObject, modelUrl, parentMatrix, physicsProps]);
|
|
179
244
|
const groupRef = useRef(null);
|
|
180
|
-
const
|
|
245
|
+
const editClickHandlers = useClickValid(!!editMode && !isLocked, (e) => {
|
|
246
|
+
onSelect === null || onSelect === void 0 ? void 0 : onSelect(nodeId);
|
|
247
|
+
onClick === null || onClick === void 0 ? void 0 : onClick(e, gameObject);
|
|
248
|
+
});
|
|
181
249
|
useEffect(() => {
|
|
182
250
|
if (editMode) {
|
|
183
251
|
registerRef(nodeId, groupRef.current);
|
|
@@ -185,19 +253,12 @@ function InstancedNode({ nodeId, parentMatrix = IDENTITY, editMode, registerRef,
|
|
|
185
253
|
}
|
|
186
254
|
}, [nodeId, registerRef, editMode]);
|
|
187
255
|
if (editMode) {
|
|
188
|
-
return (_jsxs(_Fragment, { children: [_jsx("group", { ref: groupRef, position: localTransform.position, rotation: localTransform.rotation, scale: localTransform.scale,
|
|
189
|
-
if (clickValid.current) {
|
|
190
|
-
e.stopPropagation();
|
|
191
|
-
onSelect === null || onSelect === void 0 ? void 0 : onSelect(nodeId);
|
|
192
|
-
onClick === null || onClick === void 0 ? void 0 : onClick(e, gameObject);
|
|
193
|
-
}
|
|
194
|
-
clickValid.current = false;
|
|
195
|
-
}, children: _jsx("mesh", { visible: false, children: _jsx("boxGeometry", { args: [0.01, 0.01, 0.01] }) }) }), instances.map(instance => (_jsx(GameInstance, { id: instance.id, sourceId: gameObject.id, clickable: clickable, modelUrl: instance.modelUrl, position: instance.position, rotation: instance.rotation, scale: instance.scale, locked: isLocked, physics: instance.physics }, instance.id)))] }));
|
|
256
|
+
return (_jsxs(_Fragment, { children: [_jsx("group", Object.assign({ ref: groupRef, position: localTransform.position, rotation: localTransform.rotation, scale: localTransform.scale }, editClickHandlers, { children: _jsx("mesh", { visible: false, children: _jsx("boxGeometry", { args: [0.01, 0.01, 0.01] }) }) })), instances.map(instance => (_jsx(GameInstance, { id: instance.id, sourceId: gameObject.id, clickable: clickable, clickEventName: clickEventName, modelUrl: instance.modelUrl, position: instance.position, rotation: instance.rotation, scale: instance.scale, locked: isLocked, physics: instance.physics }, instance.id)))] }));
|
|
196
257
|
}
|
|
197
|
-
return (_jsx(_Fragment, { children: instances.map(instance => (_jsx(GameInstance, { id: instance.id, sourceId: gameObject.id, clickable: clickable, modelUrl: instance.modelUrl, position: instance.position, rotation: instance.rotation, scale: instance.scale, locked: isLocked, physics: instance.physics }, instance.id))) }));
|
|
258
|
+
return (_jsx(_Fragment, { children: instances.map(instance => (_jsx(GameInstance, { id: instance.id, sourceId: gameObject.id, clickable: clickable, clickEventName: clickEventName, modelUrl: instance.modelUrl, position: instance.position, rotation: instance.rotation, scale: instance.scale, locked: isLocked, physics: instance.physics }, instance.id))) }));
|
|
198
259
|
}
|
|
199
|
-
function StandardNode({ nodeId, selectedId, onSelect, onClick, registerRef,
|
|
200
|
-
var _a, _b
|
|
260
|
+
function StandardNode({ nodeId, selectedId, onSelect, onClick, registerRef, loadedModels, editMode, parentMatrix = IDENTITY, }) {
|
|
261
|
+
var _a, _b;
|
|
201
262
|
const gameObject = usePrefabNode(nodeId);
|
|
202
263
|
const childIds = usePrefabChildIds(nodeId);
|
|
203
264
|
const isSelected = selectedId === nodeId;
|
|
@@ -205,59 +266,49 @@ function StandardNode({ nodeId, selectedId, onSelect, onClick, registerRef, regi
|
|
|
205
266
|
return null;
|
|
206
267
|
const groupRef = useRef(null);
|
|
207
268
|
const helperRef = useRef(null);
|
|
208
|
-
const clickValid = useRef(false);
|
|
209
269
|
const isLocked = Boolean(gameObject.locked);
|
|
210
270
|
const stillInstanced = useInstanceCheck(nodeId);
|
|
271
|
+
const clickHandlers = useClickValid(!!editMode && !isLocked, (e) => {
|
|
272
|
+
onSelect === null || onSelect === void 0 ? void 0 : onSelect(nodeId);
|
|
273
|
+
onClick === null || onClick === void 0 ? void 0 : onClick(e, gameObject);
|
|
274
|
+
});
|
|
211
275
|
useHelper(editMode && isSelected ? helperRef : null, BoxHelper, "cyan");
|
|
212
276
|
useEffect(() => {
|
|
213
277
|
registerRef(nodeId, groupRef.current);
|
|
214
278
|
return () => registerRef(nodeId, null);
|
|
215
279
|
}, [nodeId, registerRef]);
|
|
216
280
|
const world = parentMatrix.clone().multiply(compose(gameObject));
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
clickValid.current = true;
|
|
220
|
-
};
|
|
221
|
-
const onUp = (e) => {
|
|
222
|
-
if (clickValid.current) {
|
|
223
|
-
e.stopPropagation();
|
|
224
|
-
onSelect === null || onSelect === void 0 ? void 0 : onSelect(nodeId);
|
|
225
|
-
onClick === null || onClick === void 0 ? void 0 : onClick(e, gameObject);
|
|
226
|
-
}
|
|
227
|
-
clickValid.current = false;
|
|
228
|
-
};
|
|
229
|
-
const physics = (_a = gameObject.components) === null || _a === void 0 ? void 0 : _a.physics;
|
|
230
|
-
const ready = !((_b = gameObject.components) === null || _b === void 0 ? void 0 : _b.model) ||
|
|
231
|
-
loadedModels[gameObject.components.model.properties.filename];
|
|
281
|
+
const physics = findComponent(gameObject, "Physics");
|
|
282
|
+
const ready = isNodeReady(gameObject, loadedModels);
|
|
232
283
|
const hasPhysics = physics && ready && !stillInstanced;
|
|
233
284
|
const transform = getNodeTransformProps(gameObject);
|
|
234
|
-
const physicsDef = hasPhysics ?
|
|
235
|
-
const isInstanced = (
|
|
285
|
+
const physicsDef = hasPhysics ? getComponentDef(physics.type) : null;
|
|
286
|
+
const isInstanced = (_b = (_a = findComponent(gameObject, "Model")) === null || _a === void 0 ? void 0 : _a.properties) === null || _b === void 0 ? void 0 : _b.instanced;
|
|
236
287
|
const physicsKey = `physics_${nodeId}_${isInstanced ? 'instanced' : 'standard'}`;
|
|
237
|
-
const renderCtx = { loadedModels,
|
|
288
|
+
const renderCtx = { loadedModels, editMode, registerRef };
|
|
238
289
|
const childNodes = getChildHostComponents(gameObject).length > 0
|
|
239
290
|
? _jsx(CompositionChildren, { childIds: childIds, selectedId: selectedId, ctx: renderCtx, parentMatrix: world })
|
|
240
|
-
: _jsx(ChildNodes, { childIds: childIds, parentMatrix: world, selectedId: selectedId, onSelect: onSelect, onClick: onClick, registerRef: registerRef,
|
|
241
|
-
const inner = (_jsx("group", {
|
|
291
|
+
: _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, isSelected, parentMatrix, childNodes) })));
|
|
242
293
|
if (editMode) {
|
|
243
|
-
return (_jsxs(_Fragment, { children: [_jsx("group", { ref: groupRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: _jsx("mesh", { visible: false, children: _jsx("boxGeometry", { args: [0.01, 0.01, 0.01] }) }) }), _jsx("group", { ref: helperRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: inner }), hasPhysics && (physicsDef === null || physicsDef === void 0 ? void 0 : physicsDef.View) ? (_jsx(physicsDef.View, { properties: physics.properties, position: transform.position, rotation: transform.rotation, scale: transform.scale, editMode: editMode, nodeId: nodeId,
|
|
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] }));
|
|
244
295
|
}
|
|
245
296
|
if (hasPhysics && (physicsDef === null || physicsDef === void 0 ? void 0 : physicsDef.View)) {
|
|
246
|
-
return (_jsx(physicsDef.View, { properties: physics.properties, position: transform.position, rotation: transform.rotation, scale: transform.scale, editMode: editMode, nodeId: nodeId,
|
|
297
|
+
return (_jsx(physicsDef.View, { properties: physics.properties, position: transform.position, rotation: transform.rotation, scale: transform.scale, editMode: editMode, nodeId: nodeId, children: inner }, physicsKey));
|
|
247
298
|
}
|
|
248
299
|
return (_jsx("group", { ref: groupRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: inner }));
|
|
249
300
|
}
|
|
250
|
-
const LEAF_COMPONENT_TYPES = new Set(["Geometry", "Material", "Physics"]);
|
|
251
301
|
function getChildHostComponents(gameObject) {
|
|
252
302
|
var _a;
|
|
253
|
-
return Object.entries((_a = gameObject.components) !== null && _a !== void 0 ? _a : {}).
|
|
303
|
+
return Object.entries((_a = gameObject.components) !== null && _a !== void 0 ? _a : {}).reduce((result, [key, comp]) => {
|
|
254
304
|
if (!(comp === null || comp === void 0 ? void 0 : comp.type))
|
|
255
|
-
return
|
|
256
|
-
const def =
|
|
257
|
-
if (!(def === null || def === void 0 ? void 0 : def.View) ||
|
|
258
|
-
return
|
|
259
|
-
|
|
260
|
-
|
|
305
|
+
return result;
|
|
306
|
+
const def = getComponentDef(comp.type);
|
|
307
|
+
if (!(def === null || def === void 0 ? void 0 : def.View) || def.isWrapper)
|
|
308
|
+
return result;
|
|
309
|
+
result.push({ key, View: def.View, properties: comp.properties });
|
|
310
|
+
return result;
|
|
311
|
+
}, []);
|
|
261
312
|
}
|
|
262
313
|
function ChildNodes(_a) {
|
|
263
314
|
var { childIds, parentMatrix } = _a, props = __rest(_a, ["childIds", "parentMatrix"]);
|
|
@@ -265,11 +316,11 @@ function ChildNodes(_a) {
|
|
|
265
316
|
}
|
|
266
317
|
function compose(node) {
|
|
267
318
|
const { position, rotation, scale } = getNodeTransformProps(node);
|
|
268
|
-
return
|
|
319
|
+
return composeTransform(position, rotation, scale);
|
|
269
320
|
}
|
|
270
321
|
function getModelRepeatSettings(node) {
|
|
271
|
-
var _a, _b
|
|
272
|
-
const properties = (
|
|
322
|
+
var _a, _b;
|
|
323
|
+
const properties = (_b = (_a = findComponent(node, "Model")) === null || _a === void 0 ? void 0 : _a.properties) !== null && _b !== void 0 ? _b : {};
|
|
273
324
|
return {
|
|
274
325
|
repeat: Boolean(properties.repeat),
|
|
275
326
|
repeatAxes: getRepeatAxesFromModelProperties(properties),
|
|
@@ -320,12 +371,12 @@ function buildRepeatedInstances(gameObject, parentMatrix, modelUrl, physics) {
|
|
|
320
371
|
return instances;
|
|
321
372
|
}
|
|
322
373
|
function getNodeTransformProps(node) {
|
|
323
|
-
var _a, _b, _c, _d
|
|
324
|
-
const t = (
|
|
374
|
+
var _a, _b, _c, _d;
|
|
375
|
+
const t = (_a = findComponent(node, "Transform")) === null || _a === void 0 ? void 0 : _a.properties;
|
|
325
376
|
return {
|
|
326
|
-
position: (
|
|
327
|
-
rotation: (
|
|
328
|
-
scale: (
|
|
377
|
+
position: (_b = t === null || t === void 0 ? void 0 : t.position) !== null && _b !== void 0 ? _b : [0, 0, 0],
|
|
378
|
+
rotation: (_c = t === null || t === void 0 ? void 0 : t.rotation) !== null && _c !== void 0 ? _c : [0, 0, 0],
|
|
379
|
+
scale: (_d = t === null || t === void 0 ? void 0 : t.scale) !== null && _d !== void 0 ? _d : [1, 1, 1],
|
|
329
380
|
};
|
|
330
381
|
}
|
|
331
382
|
function renderCompositionSubtree(gameObject, ctx, isSelected, childIds, parentMatrix = IDENTITY) {
|
|
@@ -351,40 +402,25 @@ function renderCompositionNode(gameObject, ctx, isSelected, parentMatrix, childN
|
|
|
351
402
|
const ownContent = renderNodeOwnContent(gameObject, ctx, isSelected, parentMatrix);
|
|
352
403
|
return wrapWithChildHosts(gameObject, ctx, isSelected, parentMatrix, _jsxs(_Fragment, { children: [ownContent, childNodes] }));
|
|
353
404
|
}
|
|
354
|
-
function
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
const material = (_b = gameObject.components) === null || _b === void 0 ? void 0 : _b.material;
|
|
358
|
-
const geometryDef = geometry && getComponent("Geometry");
|
|
359
|
-
const materialDef = material && getComponent("Material");
|
|
360
|
-
const contextProps = {
|
|
361
|
-
loadedModels: ctx.loadedModels,
|
|
362
|
-
loadedTextures: ctx.loadedTextures,
|
|
363
|
-
editMode: ctx.editMode,
|
|
405
|
+
function buildContextProps(gameObject, _ctx, isSelected) {
|
|
406
|
+
return {
|
|
407
|
+
editMode: _ctx.editMode,
|
|
364
408
|
isSelected,
|
|
365
409
|
nodeId: gameObject.id,
|
|
366
|
-
parentMatrix,
|
|
367
|
-
registerRef: ctx.registerRef,
|
|
368
410
|
};
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
411
|
+
}
|
|
412
|
+
function renderNodeOwnContent(gameObject, ctx, isSelected, parentMatrix) {
|
|
413
|
+
const geometry = findComponent(gameObject, "Geometry");
|
|
414
|
+
const material = findComponent(gameObject, "Material");
|
|
415
|
+
const geometryDef = geometry && getComponentDef(geometry.type);
|
|
416
|
+
const materialDef = material && getComponentDef(material.type);
|
|
417
|
+
if (!geometry || !(geometryDef === null || geometryDef === void 0 ? void 0 : geometryDef.View))
|
|
418
|
+
return null;
|
|
419
|
+
const contextProps = buildContextProps(gameObject, ctx, isSelected);
|
|
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"))] }));
|
|
377
421
|
}
|
|
378
422
|
function wrapWithChildHosts(gameObject, ctx, isSelected, parentMatrix, subtree) {
|
|
379
|
-
const contextProps =
|
|
380
|
-
loadedModels: ctx.loadedModels,
|
|
381
|
-
loadedTextures: ctx.loadedTextures,
|
|
382
|
-
editMode: ctx.editMode,
|
|
383
|
-
isSelected,
|
|
384
|
-
nodeId: gameObject.id,
|
|
385
|
-
parentMatrix,
|
|
386
|
-
registerRef: ctx.registerRef,
|
|
387
|
-
};
|
|
423
|
+
const contextProps = buildContextProps(gameObject, ctx, isSelected);
|
|
388
424
|
const childHosts = getChildHostComponents(gameObject);
|
|
389
425
|
return childHosts.reduce((acc, { key, View, properties }) => (_jsx(View, Object.assign({ properties: properties }, contextProps, { children: acc }), key)), subtree);
|
|
390
426
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
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;
|
|
@@ -0,0 +1,44 @@
|
|
|
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
|
+
}
|
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { ColorField,
|
|
2
|
+
import { ColorField, NumberField } from "./Input";
|
|
3
|
+
import { LightSection, mergeWithDefaults } from "./lightUtils";
|
|
4
|
+
const ambientLightDefaults = {
|
|
5
|
+
color: '#ffffff',
|
|
6
|
+
intensity: 1,
|
|
7
|
+
};
|
|
3
8
|
function AmbientLightComponentEditor({ component, onUpdate, }) {
|
|
4
|
-
|
|
9
|
+
const values = mergeWithDefaults(ambientLightDefaults, component.properties);
|
|
10
|
+
return (_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 })] }));
|
|
5
11
|
}
|
|
6
12
|
function AmbientLightComponentView({ properties, children }) {
|
|
7
|
-
const { color
|
|
13
|
+
const { color, intensity } = mergeWithDefaults(ambientLightDefaults, properties);
|
|
8
14
|
return (_jsxs(_Fragment, { children: [_jsx("ambientLight", { color: color, intensity: intensity }), children] }));
|
|
9
15
|
}
|
|
10
16
|
const AmbientLightComponent = {
|
|
11
17
|
name: 'AmbientLight',
|
|
12
18
|
Editor: AmbientLightComponentEditor,
|
|
13
19
|
View: AmbientLightComponentView,
|
|
14
|
-
defaultProperties: {
|
|
15
|
-
color: '#ffffff',
|
|
16
|
-
intensity: 1,
|
|
17
|
-
},
|
|
20
|
+
defaultProperties: {},
|
|
18
21
|
};
|
|
19
22
|
export default AmbientLightComponent;
|