react-three-game 0.0.84 → 0.0.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +110 -36
- package/dist/index.d.ts +3 -7
- package/dist/index.js +1 -4
- package/dist/tools/prefabeditor/InstanceProvider.d.ts +0 -4
- package/dist/tools/prefabeditor/InstanceProvider.js +13 -44
- package/dist/tools/prefabeditor/PrefabEditor.d.ts +7 -2
- package/dist/tools/prefabeditor/PrefabEditor.js +13 -24
- package/dist/tools/prefabeditor/PrefabRoot.js +99 -48
- package/dist/tools/prefabeditor/{runtime.d.ts → assetRuntime.d.ts} +0 -25
- package/dist/tools/prefabeditor/assetRuntime.js +37 -0
- package/dist/tools/prefabeditor/components/BufferGeometryComponent.d.ts +3 -0
- package/dist/tools/prefabeditor/components/BufferGeometryComponent.js +98 -0
- package/dist/tools/prefabeditor/components/CameraComponent.js +1 -1
- package/dist/tools/prefabeditor/components/ComponentRegistry.d.ts +0 -3
- package/dist/tools/prefabeditor/components/DataComponent.d.ts +3 -0
- package/dist/tools/prefabeditor/components/DataComponent.js +87 -0
- package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +1 -1
- package/dist/tools/prefabeditor/components/EnvironmentComponent.js +1 -1
- package/dist/tools/prefabeditor/components/GeometryComponent.js +4 -2
- package/dist/tools/prefabeditor/components/Input.d.ts +2 -13
- package/dist/tools/prefabeditor/components/Input.js +0 -55
- package/dist/tools/prefabeditor/components/MaterialComponent.d.ts +3 -0
- package/dist/tools/prefabeditor/components/MaterialComponent.js +49 -18
- package/dist/tools/prefabeditor/components/ModelComponent.js +3 -3
- package/dist/tools/prefabeditor/components/PhysicsComponent.d.ts +4 -0
- package/dist/tools/prefabeditor/components/PhysicsComponent.js +69 -132
- package/dist/tools/prefabeditor/components/PointLightComponent.js +1 -1
- package/dist/tools/prefabeditor/components/SoundComponent.js +17 -17
- package/dist/tools/prefabeditor/components/SpotLightComponent.js +1 -1
- package/dist/tools/prefabeditor/components/index.js +10 -8
- package/dist/tools/prefabeditor/types.d.ts +1 -0
- package/dist/tools/prefabeditor/types.js +18 -0
- package/dist/tools/prefabeditor/usePointerEvents.d.ts +27 -0
- package/dist/tools/prefabeditor/usePointerEvents.js +52 -0
- package/package.json +1 -1
- package/dist/tools/prefabeditor/GameEvents.d.ts +0 -128
- package/dist/tools/prefabeditor/GameEvents.js +0 -118
- package/dist/tools/prefabeditor/components/ClickComponent.d.ts +0 -3
- package/dist/tools/prefabeditor/components/ClickComponent.js +0 -52
- package/dist/tools/prefabeditor/runtime.js +0 -184
|
@@ -13,10 +13,9 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
13
13
|
import { useHelper } from "@react-three/drei";
|
|
14
14
|
import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
|
|
15
15
|
import { BoxHelper, Euler, Matrix4, } from "three";
|
|
16
|
-
import { useFrame } from "@react-three/fiber";
|
|
17
16
|
import { useStore } from "zustand";
|
|
18
17
|
import { useClickValid } from "./useClickValid";
|
|
19
|
-
import { findComponent } from "./types";
|
|
18
|
+
import { findComponent, getNodeUserData } from "./types";
|
|
20
19
|
import { getComponentDef, getComponentAssetRefs, registerComponent } from "./components/ComponentRegistry";
|
|
21
20
|
import { builtinComponents } from "./components";
|
|
22
21
|
import { loadModel, loadSound, loadTexture } from "../dragdrop";
|
|
@@ -24,7 +23,7 @@ import { GameInstance, GameInstanceProvider, getRepeatAxesFromModelProperties, u
|
|
|
24
23
|
import { composeTransform, decompose } from "./utils";
|
|
25
24
|
import { isPhysicsProps } from "./components/PhysicsComponent";
|
|
26
25
|
import { createPrefabStore, PrefabStoreProvider, useOptionalPrefabStoreApi, usePrefabChildIds, usePrefabNode, usePrefabRootId } from "./prefabStore";
|
|
27
|
-
import { AssetRuntimeContext, EntityRuntimeScope
|
|
26
|
+
import { AssetRuntimeContext, EntityRuntimeScope } from "./assetRuntime";
|
|
28
27
|
import { sound as soundManager } from "../../helpers/SoundManager";
|
|
29
28
|
builtinComponents.forEach(registerComponent);
|
|
30
29
|
const IDENTITY = new Matrix4();
|
|
@@ -45,6 +44,36 @@ function isNodeReady(node, loadedModels) {
|
|
|
45
44
|
return true;
|
|
46
45
|
return Boolean(loadedModels[model.properties.filename]);
|
|
47
46
|
}
|
|
47
|
+
function emitNativeEvent(type, detail) {
|
|
48
|
+
const trimmedType = type === null || type === void 0 ? void 0 : type.trim();
|
|
49
|
+
if (!trimmedType || typeof window === 'undefined')
|
|
50
|
+
return;
|
|
51
|
+
window.dispatchEvent(new CustomEvent(trimmedType, { detail }));
|
|
52
|
+
}
|
|
53
|
+
function getNodeClickEventName(node) {
|
|
54
|
+
var _a;
|
|
55
|
+
const clickComponents = [
|
|
56
|
+
findComponent(node, 'BufferGeometry'),
|
|
57
|
+
findComponent(node, 'Geometry'),
|
|
58
|
+
];
|
|
59
|
+
for (const component of clickComponents) {
|
|
60
|
+
if (!((_a = component === null || component === void 0 ? void 0 : component.properties) === null || _a === void 0 ? void 0 : _a.emitClickEvent))
|
|
61
|
+
continue;
|
|
62
|
+
const eventName = component.properties.clickEventName;
|
|
63
|
+
if (typeof eventName === 'string' && eventName.trim()) {
|
|
64
|
+
return eventName.trim();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
function getNodeMetadataProps(node) {
|
|
70
|
+
var _a, _b;
|
|
71
|
+
const nodeName = (_b = (_a = node.name) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : '';
|
|
72
|
+
return {
|
|
73
|
+
name: nodeName,
|
|
74
|
+
userData: Object.assign(Object.assign({ prefabNodeId: node.id }, (nodeName ? { prefabNodeName: nodeName } : {})), getNodeUserData(node)),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
48
77
|
export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSelect, onClick, onObjectRefChange, basePath = "" }, ref) => {
|
|
49
78
|
var _a;
|
|
50
79
|
const [models, setModels] = useState({});
|
|
@@ -94,29 +123,13 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
94
123
|
setInjectedSounds(prev => (Object.assign(Object.assign({}, prev), { [path]: sound })));
|
|
95
124
|
},
|
|
96
125
|
}), [getObject]);
|
|
97
|
-
const runtimeEngine = useMemo(() => createRuntimeEngine({
|
|
98
|
-
store: resolvedStore,
|
|
99
|
-
getObject,
|
|
100
|
-
getRigidBody,
|
|
101
|
-
}), [resolvedStore, getObject, getRigidBody]);
|
|
102
126
|
const registerRef = useCallback((id, obj) => {
|
|
103
127
|
objectRefs.current[id] = obj;
|
|
104
|
-
runtimeEngine.invalidate();
|
|
105
128
|
onObjectRefChange === null || onObjectRefChange === void 0 ? void 0 : onObjectRefChange(id, obj);
|
|
106
|
-
}, [onObjectRefChange
|
|
129
|
+
}, [onObjectRefChange]);
|
|
107
130
|
const registerRigidBodyRef = useCallback((id, rb) => {
|
|
108
131
|
rigidBodyRefs.current.set(id, rb);
|
|
109
|
-
|
|
110
|
-
}, [runtimeEngine]);
|
|
111
|
-
useEffect(() => {
|
|
112
|
-
runtimeEngine.setActive(!editMode);
|
|
113
|
-
}, [editMode, runtimeEngine]);
|
|
114
|
-
useEffect(() => {
|
|
115
|
-
return () => runtimeEngine.dispose();
|
|
116
|
-
}, [runtimeEngine]);
|
|
117
|
-
useFrame((_, dt) => {
|
|
118
|
-
runtimeEngine.tick(dt);
|
|
119
|
-
});
|
|
132
|
+
}, []);
|
|
120
133
|
useEffect(() => {
|
|
121
134
|
if (usesOwnedStore && data) {
|
|
122
135
|
resolvedStore.getState().replacePrefab(data);
|
|
@@ -189,15 +202,32 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
189
202
|
getAssetRevision: () => `${Object.keys(availableTextures).sort().join('|')}::${Object.keys(availableModels).sort().join('|')}`,
|
|
190
203
|
}), [getObject, getRigidBody, registerRigidBodyRef, availableModels, availableTextures, availableSounds]);
|
|
191
204
|
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 }) }) }));
|
|
205
|
+
const runtimeContent = _jsx(AssetRuntimeContext.Provider, { value: assetRuntime, children: content });
|
|
192
206
|
if (!shouldProvideStoreContext) {
|
|
193
|
-
return
|
|
207
|
+
return runtimeContent;
|
|
194
208
|
}
|
|
195
|
-
return _jsx(PrefabStoreProvider, { store: resolvedStore, children:
|
|
209
|
+
return _jsx(PrefabStoreProvider, { store: resolvedStore, children: runtimeContent });
|
|
196
210
|
});
|
|
197
211
|
function StoreRootNode(props) {
|
|
198
212
|
const rootId = usePrefabRootId();
|
|
199
213
|
return _jsx(GameObjectRenderer, Object.assign({}, props, { nodeId: rootId }));
|
|
200
214
|
}
|
|
215
|
+
function emitNodePointerEvent(eventName, event, nodeId, node, fallbackObject) {
|
|
216
|
+
var _a;
|
|
217
|
+
if (!eventName)
|
|
218
|
+
return;
|
|
219
|
+
emitNativeEvent(eventName, {
|
|
220
|
+
nodeId,
|
|
221
|
+
node,
|
|
222
|
+
object: (_a = event.object) !== null && _a !== void 0 ? _a : fallbackObject,
|
|
223
|
+
button: event.button,
|
|
224
|
+
altKey: event.nativeEvent.altKey,
|
|
225
|
+
ctrlKey: event.nativeEvent.ctrlKey,
|
|
226
|
+
metaKey: event.nativeEvent.metaKey,
|
|
227
|
+
shiftKey: event.nativeEvent.shiftKey,
|
|
228
|
+
r3fEvent: event,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
201
231
|
export function GameObjectRenderer(props) {
|
|
202
232
|
var _a, _b;
|
|
203
233
|
const node = usePrefabNode(props.nodeId);
|
|
@@ -221,20 +251,19 @@ export function GameObjectRenderer(props) {
|
|
|
221
251
|
: _jsx(StandardNode, Object.assign({}, props), key);
|
|
222
252
|
}
|
|
223
253
|
function InstancedNode({ nodeId, parentMatrix = IDENTITY, editMode, registerRef, onSelect, onClick }) {
|
|
224
|
-
var _a, _b
|
|
254
|
+
var _a, _b;
|
|
225
255
|
const gameObject = usePrefabNode(nodeId);
|
|
226
256
|
if (!gameObject)
|
|
227
257
|
return null;
|
|
228
258
|
const localTransform = getNodeTransformProps(gameObject);
|
|
229
259
|
const isLocked = Boolean(gameObject.locked);
|
|
230
|
-
const
|
|
231
|
-
const
|
|
232
|
-
const clickEventName = (_a = clickComponent === null || clickComponent === void 0 ? void 0 : clickComponent.properties) === null || _a === void 0 ? void 0 : _a.eventName;
|
|
260
|
+
const metadataProps = getNodeMetadataProps(gameObject);
|
|
261
|
+
const groupProps = Object.assign(Object.assign({}, metadataProps), { position: localTransform.position, rotation: localTransform.rotation, scale: localTransform.scale });
|
|
233
262
|
const physicsData = findComponent(gameObject, "Physics");
|
|
234
263
|
const physicsProps = isPhysicsProps(physicsData === null || physicsData === void 0 ? void 0 : physicsData.properties)
|
|
235
264
|
? physicsData === null || physicsData === void 0 ? void 0 : physicsData.properties
|
|
236
265
|
: undefined;
|
|
237
|
-
const modelUrl = (
|
|
266
|
+
const modelUrl = (_b = (_a = findComponent(gameObject, "Model")) === null || _a === void 0 ? void 0 : _a.properties) === null || _b === void 0 ? void 0 : _b.filename;
|
|
238
267
|
const instances = useMemo(() => buildRepeatedInstances(gameObject, parentMatrix, modelUrl, physicsProps), [gameObject, modelUrl, parentMatrix, physicsProps]);
|
|
239
268
|
const groupRef = useRef(null);
|
|
240
269
|
const handleGroupRef = useCallback((object) => {
|
|
@@ -243,22 +272,27 @@ function InstancedNode({ nodeId, parentMatrix = IDENTITY, editMode, registerRef,
|
|
|
243
272
|
registerRef(nodeId, object);
|
|
244
273
|
}
|
|
245
274
|
}, [editMode, nodeId, registerRef]);
|
|
246
|
-
const editClickHandlers = useClickValid(!!editMode && !isLocked, (
|
|
275
|
+
const editClickHandlers = useClickValid(!!editMode && !isLocked, (event) => {
|
|
247
276
|
onSelect === null || onSelect === void 0 ? void 0 : onSelect(nodeId);
|
|
248
|
-
onClick === null || onClick === void 0 ? void 0 : onClick(
|
|
277
|
+
onClick === null || onClick === void 0 ? void 0 : onClick(event, gameObject);
|
|
249
278
|
});
|
|
279
|
+
const renderedInstances = instances.map(instance => (_jsx(GameInstance, { id: instance.id, sourceId: gameObject.id, modelUrl: instance.modelUrl, position: instance.position, rotation: instance.rotation, scale: instance.scale, locked: isLocked, physics: instance.physics }, instance.id)));
|
|
250
280
|
if (editMode) {
|
|
251
|
-
return (_jsxs(_Fragment, { children: [_jsx("group", Object.assign({ ref: handleGroupRef
|
|
281
|
+
return (_jsxs(_Fragment, { children: [_jsx("group", Object.assign({ ref: handleGroupRef }, groupProps, editClickHandlers, { children: _jsx("mesh", { visible: false, children: _jsx("boxGeometry", { args: [0.01, 0.01, 0.01] }) }) })), renderedInstances] }));
|
|
252
282
|
}
|
|
253
|
-
return
|
|
283
|
+
return _jsx(_Fragment, { children: renderedInstances });
|
|
254
284
|
}
|
|
255
285
|
function StandardNode({ nodeId, selectedId, onSelect, onClick, registerRef, loadedModels, editMode, parentMatrix = IDENTITY, }) {
|
|
256
286
|
var _a, _b;
|
|
257
287
|
const gameObject = usePrefabNode(nodeId);
|
|
258
288
|
const childIds = usePrefabChildIds(nodeId);
|
|
289
|
+
if (!gameObject)
|
|
290
|
+
return null;
|
|
259
291
|
const isSelected = selectedId === nodeId;
|
|
260
|
-
const isLocked = Boolean(gameObject
|
|
292
|
+
const isLocked = Boolean(gameObject.locked);
|
|
261
293
|
const stillInstanced = useInstanceCheck(nodeId);
|
|
294
|
+
const clickEventName = getNodeClickEventName(gameObject);
|
|
295
|
+
const metadataProps = getNodeMetadataProps(gameObject);
|
|
262
296
|
const groupRef = useRef(null);
|
|
263
297
|
const helperRef = useRef(null);
|
|
264
298
|
const handleGroupRef = useCallback((object) => {
|
|
@@ -268,30 +302,47 @@ function StandardNode({ nodeId, selectedId, onSelect, onClick, registerRef, load
|
|
|
268
302
|
const handleHelperRef = useCallback((object) => {
|
|
269
303
|
helperRef.current = object;
|
|
270
304
|
}, []);
|
|
271
|
-
const
|
|
305
|
+
const handleEditGroupRef = useCallback((object) => {
|
|
306
|
+
handleGroupRef(object);
|
|
307
|
+
handleHelperRef(object);
|
|
308
|
+
}, [handleGroupRef, handleHelperRef]);
|
|
309
|
+
const editClickHandlers = useClickValid(!!editMode && !isLocked, (event) => {
|
|
272
310
|
onSelect === null || onSelect === void 0 ? void 0 : onSelect(nodeId);
|
|
273
|
-
|
|
274
|
-
onClick === null || onClick === void 0 ? void 0 : onClick(e, gameObject);
|
|
311
|
+
onClick === null || onClick === void 0 ? void 0 : onClick(event, gameObject);
|
|
275
312
|
});
|
|
313
|
+
const primaryClickHandlers = !editMode && (clickEventName || onClick)
|
|
314
|
+
? {
|
|
315
|
+
onClick: (event) => {
|
|
316
|
+
event.stopPropagation();
|
|
317
|
+
emitNodePointerEvent(clickEventName, event, nodeId, gameObject, groupRef.current);
|
|
318
|
+
onClick === null || onClick === void 0 ? void 0 : onClick(event, gameObject);
|
|
319
|
+
},
|
|
320
|
+
}
|
|
321
|
+
: undefined;
|
|
276
322
|
useHelper(editMode && isSelected ? helperRef : null, BoxHelper, "cyan");
|
|
277
|
-
if (!gameObject)
|
|
278
|
-
return null;
|
|
279
323
|
const world = parentMatrix.clone().multiply(compose(gameObject));
|
|
280
324
|
const physics = findComponent(gameObject, "Physics");
|
|
281
325
|
const ready = isNodeReady(gameObject, loadedModels);
|
|
282
326
|
const hasPhysics = physics && ready && !stillInstanced;
|
|
283
327
|
const transform = getNodeTransformProps(gameObject);
|
|
328
|
+
const transformProps = {
|
|
329
|
+
position: transform.position,
|
|
330
|
+
rotation: transform.rotation,
|
|
331
|
+
scale: transform.scale,
|
|
332
|
+
};
|
|
333
|
+
const groupProps = Object.assign(Object.assign({}, metadataProps), transformProps);
|
|
284
334
|
const physicsDef = hasPhysics ? getComponentDef(physics.type) : null;
|
|
285
335
|
const isInstanced = (_b = (_a = findComponent(gameObject, "Model")) === null || _a === void 0 ? void 0 : _a.properties) === null || _b === void 0 ? void 0 : _b.instanced;
|
|
286
336
|
const physicsKey = `physics_${nodeId}_${isInstanced ? 'instanced' : 'standard'}`;
|
|
287
337
|
const renderCtx = { loadedModels, editMode, registerRef };
|
|
288
338
|
const childNodes = _jsx(ChildNodes, { childIds: childIds, parentMatrix: world, selectedId: selectedId, onSelect: onSelect, onClick: onClick, registerRef: registerRef, loadedModels: loadedModels, editMode: editMode });
|
|
289
|
-
const inner =
|
|
339
|
+
const inner = renderCompositionNode(gameObject, renderCtx, primaryClickHandlers, childNodes);
|
|
290
340
|
const physicsInner = editMode ? _jsx("group", { visible: false, children: inner }) : inner;
|
|
291
|
-
return (_jsx(EntityRuntimeScope, { nodeId: nodeId, editMode: editMode, isSelected: isSelected, children: editMode ? (_jsxs(_Fragment, { children: [
|
|
341
|
+
return (_jsx(EntityRuntimeScope, { nodeId: nodeId, editMode: editMode, isSelected: isSelected, children: editMode ? (_jsxs(_Fragment, { children: [_jsxs("group", Object.assign({ ref: handleEditGroupRef }, groupProps, editClickHandlers, { children: [_jsx("mesh", { visible: false, children: _jsx("boxGeometry", { args: [0.01, 0.01, 0.01] }) }), inner] })), hasPhysics && (physicsDef === null || physicsDef === void 0 ? void 0 : physicsDef.View) ? (_jsx(physicsDef.View, Object.assign({ properties: physics.properties }, transformProps, { children: physicsInner }), physicsKey)) : null] })) : hasPhysics && (physicsDef === null || physicsDef === void 0 ? void 0 : physicsDef.View) ? (_jsx(physicsDef.View, Object.assign({ properties: physics.properties }, transformProps, { children: _jsx("group", Object.assign({ ref: handleGroupRef }, metadataProps, { children: inner })) }), physicsKey)) : (_jsx("group", Object.assign({ ref: handleGroupRef }, groupProps, { children: inner }))) }));
|
|
292
342
|
}
|
|
293
343
|
function isRendererHandledComponent(componentType) {
|
|
294
344
|
return componentType === "Transform"
|
|
345
|
+
|| componentType === "BufferGeometry"
|
|
295
346
|
|| componentType === "Geometry"
|
|
296
347
|
|| componentType === "Material"
|
|
297
348
|
|| componentType === "Physics"
|
|
@@ -382,26 +433,26 @@ function getNodeTransformProps(node) {
|
|
|
382
433
|
scale: (_d = t === null || t === void 0 ? void 0 : t.scale) !== null && _d !== void 0 ? _d : [1, 1, 1],
|
|
383
434
|
};
|
|
384
435
|
}
|
|
385
|
-
function renderCompositionNode(gameObject, ctx, childNodes) {
|
|
386
|
-
const primaryContent = renderNodePrimaryContent(gameObject, ctx);
|
|
436
|
+
function renderCompositionNode(gameObject, ctx, primaryClickHandlers, childNodes) {
|
|
437
|
+
const primaryContent = renderNodePrimaryContent(gameObject, ctx, primaryClickHandlers);
|
|
387
438
|
return applyNodeComposition(gameObject, _jsxs(_Fragment, { children: [primaryContent, childNodes] }));
|
|
388
439
|
}
|
|
389
|
-
function renderNodePrimaryContent(gameObject, ctx) {
|
|
390
|
-
var _a, _b;
|
|
391
|
-
const geometry = findComponent(gameObject, "Geometry");
|
|
440
|
+
function renderNodePrimaryContent(gameObject, ctx, primaryClickHandlers) {
|
|
441
|
+
var _a, _b, _c;
|
|
442
|
+
const geometry = (_a = findComponent(gameObject, "BufferGeometry")) !== null && _a !== void 0 ? _a : findComponent(gameObject, "Geometry");
|
|
392
443
|
const material = findComponent(gameObject, "Material");
|
|
393
444
|
const model = findComponent(gameObject, "Model");
|
|
394
445
|
const geometryDef = geometry && getComponentDef(geometry.type);
|
|
395
446
|
const materialDef = material && getComponentDef(material.type);
|
|
396
447
|
const modelDef = model && getComponentDef(model.type);
|
|
397
|
-
const geometryProperties = (
|
|
448
|
+
const geometryProperties = (_b = geometry === null || geometry === void 0 ? void 0 : geometry.properties) !== null && _b !== void 0 ? _b : {};
|
|
398
449
|
const meshVisible = geometryProperties.visible !== false;
|
|
399
450
|
const meshCastShadow = meshVisible && geometryProperties.castShadow !== false;
|
|
400
451
|
const meshReceiveShadow = meshVisible && geometryProperties.receiveShadow !== false;
|
|
401
452
|
if ((geometry === null || geometry === void 0 ? void 0 : geometry.type) && (geometryDef === null || geometryDef === void 0 ? void 0 : geometryDef.View)) {
|
|
402
|
-
return (_jsxs("mesh", { visible: meshVisible, castShadow: meshCastShadow, receiveShadow: meshReceiveShadow, children: [_jsx(geometryDef.View, { properties: geometry.properties }), material && (materialDef === null || materialDef === void 0 ? void 0 : materialDef.View) && (_jsx(materialDef.View, { properties: material.properties }, "material"))] }));
|
|
453
|
+
return (_jsxs("mesh", Object.assign({ visible: meshVisible, castShadow: meshCastShadow, receiveShadow: meshReceiveShadow }, primaryClickHandlers, { children: [_jsx(geometryDef.View, { properties: geometry.properties }), material && (materialDef === null || materialDef === void 0 ? void 0 : materialDef.View) && (_jsx(materialDef.View, { properties: material.properties }, "material"))] })));
|
|
403
454
|
}
|
|
404
|
-
if ((model === null || model === void 0 ? void 0 : model.type) && (modelDef === null || modelDef === void 0 ? void 0 : modelDef.View) && !((
|
|
455
|
+
if ((model === null || model === void 0 ? void 0 : model.type) && (modelDef === null || modelDef === void 0 ? void 0 : modelDef.View) && !((_c = model.properties) === null || _c === void 0 ? void 0 : _c.instanced) && isNodeReady(gameObject, ctx.loadedModels)) {
|
|
405
456
|
return _jsx(modelDef.View, { properties: model.properties });
|
|
406
457
|
}
|
|
407
458
|
return null;
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
2
|
import type { Object3D, Texture } from "three";
|
|
3
|
-
import type { PrefabStoreApi } from "./prefabStore";
|
|
4
|
-
import { type EntityComponent, type Scene } from "./scene";
|
|
5
3
|
export interface AssetRuntime {
|
|
6
4
|
registerRigidBodyRef: (id: string, rb: any) => void;
|
|
7
5
|
getModel: (path: string) => Object3D | null;
|
|
@@ -36,26 +34,3 @@ export declare function EntityRuntimeScope({ nodeId, editMode, isSelected, child
|
|
|
36
34
|
isSelected?: boolean;
|
|
37
35
|
children: ReactNode;
|
|
38
36
|
}): import("react/jsx-runtime").JSX.Element;
|
|
39
|
-
/** Runtime behaviour produced by `Component.create(ctx)`. All methods optional. */
|
|
40
|
-
export interface ComponentInstance {
|
|
41
|
-
start?(): void;
|
|
42
|
-
update?(dt: number): void;
|
|
43
|
-
destroy?(): void;
|
|
44
|
-
}
|
|
45
|
-
export interface ComponentRuntimeContext<TProperties = Record<string, any>> {
|
|
46
|
-
scene: Scene;
|
|
47
|
-
component: EntityComponent<TProperties>;
|
|
48
|
-
object: Object3D;
|
|
49
|
-
rigidBody: any;
|
|
50
|
-
}
|
|
51
|
-
export interface RuntimeEngine {
|
|
52
|
-
tick: (dt: number) => void;
|
|
53
|
-
setActive: (active: boolean) => void;
|
|
54
|
-
invalidate: () => void;
|
|
55
|
-
dispose: () => void;
|
|
56
|
-
}
|
|
57
|
-
export declare function createRuntimeEngine({ store, getObject, getRigidBody, }: {
|
|
58
|
-
store: PrefabStoreApi;
|
|
59
|
-
getObject: (id: string) => Object3D | null;
|
|
60
|
-
getRigidBody: (id: string) => any;
|
|
61
|
-
}): RuntimeEngine;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext, useMemo } from "react";
|
|
3
|
+
export const AssetRuntimeContext = createContext(null);
|
|
4
|
+
const EntityRuntimeContext = createContext(null);
|
|
5
|
+
export function useAssetRuntime() {
|
|
6
|
+
const ctx = useContext(AssetRuntimeContext);
|
|
7
|
+
if (!ctx)
|
|
8
|
+
throw new Error("useAssetRuntime must be used inside <PrefabRoot>");
|
|
9
|
+
return ctx;
|
|
10
|
+
}
|
|
11
|
+
export function useEntityRuntime() {
|
|
12
|
+
const ctx = useContext(EntityRuntimeContext);
|
|
13
|
+
if (!ctx)
|
|
14
|
+
throw new Error("useEntityRuntime must be used inside a component View rendered by <PrefabRoot>");
|
|
15
|
+
return ctx;
|
|
16
|
+
}
|
|
17
|
+
export function useEntityObjectRef() {
|
|
18
|
+
const { getObject } = useEntityRuntime();
|
|
19
|
+
return useMemo(() => ({ get current() { return getObject(); } }), [getObject]);
|
|
20
|
+
}
|
|
21
|
+
export function useEntityRigidBodyRef() {
|
|
22
|
+
const { getRigidBody } = useEntityRuntime();
|
|
23
|
+
return useMemo(() => ({ get current() { return getRigidBody(); } }), [getRigidBody]);
|
|
24
|
+
}
|
|
25
|
+
export function EntityRuntimeScope({ nodeId, editMode, isSelected, children, }) {
|
|
26
|
+
const asset = useContext(AssetRuntimeContext);
|
|
27
|
+
if (!asset)
|
|
28
|
+
throw new Error("EntityRuntimeScope must be used inside <PrefabRoot>");
|
|
29
|
+
const value = useMemo(() => ({
|
|
30
|
+
nodeId,
|
|
31
|
+
editMode,
|
|
32
|
+
isSelected,
|
|
33
|
+
getObject: () => asset.getObject(nodeId),
|
|
34
|
+
getRigidBody: () => asset.getRigidBody(nodeId),
|
|
35
|
+
}), [asset, editMode, isSelected, nodeId]);
|
|
36
|
+
return _jsx(EntityRuntimeContext.Provider, { value: value, children: children });
|
|
37
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { BooleanField, FieldGroup, StringField } from "./Input";
|
|
3
|
+
const DEFAULT_TRIANGLE_POSITIONS = [
|
|
4
|
+
0, 0, 0,
|
|
5
|
+
1, 0, 0,
|
|
6
|
+
0, 1, 0,
|
|
7
|
+
];
|
|
8
|
+
const DEFAULT_TRIANGLE_INDICES = [0, 1, 2];
|
|
9
|
+
const DEFAULT_TRIANGLE_UVS = [
|
|
10
|
+
0, 0,
|
|
11
|
+
1, 0,
|
|
12
|
+
0, 1,
|
|
13
|
+
];
|
|
14
|
+
function isFiniteNumberArray(value) {
|
|
15
|
+
return Array.isArray(value) && value.every(entry => typeof entry === 'number' && Number.isFinite(entry));
|
|
16
|
+
}
|
|
17
|
+
function normalizeNumberArray(value, fallback) {
|
|
18
|
+
return isFiniteNumberArray(value) ? value : fallback;
|
|
19
|
+
}
|
|
20
|
+
function toAttributeText(value, fallback) {
|
|
21
|
+
return JSON.stringify(normalizeNumberArray(value, fallback));
|
|
22
|
+
}
|
|
23
|
+
function parseArrayInput(raw) {
|
|
24
|
+
const trimmed = raw.trim();
|
|
25
|
+
if (!trimmed)
|
|
26
|
+
return [];
|
|
27
|
+
const parsed = JSON.parse(trimmed);
|
|
28
|
+
if (!isFiniteNumberArray(parsed)) {
|
|
29
|
+
throw new Error('Expected a JSON array of numbers');
|
|
30
|
+
}
|
|
31
|
+
return parsed;
|
|
32
|
+
}
|
|
33
|
+
function getIndexArray(indices) {
|
|
34
|
+
if (indices.length === 0)
|
|
35
|
+
return null;
|
|
36
|
+
const maxIndex = Math.max(...indices);
|
|
37
|
+
return maxIndex > 65535 ? new Uint32Array(indices) : new Uint16Array(indices);
|
|
38
|
+
}
|
|
39
|
+
function BufferArrayField({ label, value, fallback, onChange, rows = 4, }) {
|
|
40
|
+
return (_jsxs("label", { style: { display: 'grid', gap: 4 }, children: [_jsx("span", { style: { fontSize: '10px', color: '#888', textTransform: 'uppercase', letterSpacing: '0.05em', fontWeight: 500 }, children: label }), _jsx("textarea", { rows: rows, spellCheck: false, defaultValue: toAttributeText(value, fallback), onBlur: (event) => {
|
|
41
|
+
try {
|
|
42
|
+
onChange(parseArrayInput(event.target.value));
|
|
43
|
+
event.target.setCustomValidity('');
|
|
44
|
+
}
|
|
45
|
+
catch (_a) {
|
|
46
|
+
event.target.setCustomValidity('Expected a JSON array of numbers');
|
|
47
|
+
event.target.reportValidity();
|
|
48
|
+
}
|
|
49
|
+
}, style: {
|
|
50
|
+
width: '100%',
|
|
51
|
+
backgroundColor: '#171717',
|
|
52
|
+
border: '1px solid #333',
|
|
53
|
+
padding: '6px 8px',
|
|
54
|
+
fontSize: '11px',
|
|
55
|
+
color: '#eee',
|
|
56
|
+
fontFamily: 'monospace',
|
|
57
|
+
outline: 'none',
|
|
58
|
+
borderRadius: 3,
|
|
59
|
+
resize: 'vertical',
|
|
60
|
+
boxSizing: 'border-box',
|
|
61
|
+
} })] }));
|
|
62
|
+
}
|
|
63
|
+
function BufferGeometryComponentEditor({ component, onUpdate, }) {
|
|
64
|
+
var _a;
|
|
65
|
+
const properties = (_a = component.properties) !== null && _a !== void 0 ? _a : {};
|
|
66
|
+
return (_jsxs(FieldGroup, { children: [_jsx(BufferArrayField, { label: "Positions", value: properties.positions, fallback: DEFAULT_TRIANGLE_POSITIONS, rows: 5, onChange: (positions) => onUpdate({ positions }) }), _jsx(BufferArrayField, { label: "Indices", value: properties.indices, fallback: DEFAULT_TRIANGLE_INDICES, onChange: (indices) => onUpdate({ indices }) }), _jsx(BufferArrayField, { label: "Normals", value: properties.normals, fallback: [], onChange: (normals) => onUpdate({ normals }) }), _jsx(BufferArrayField, { label: "UVs", value: properties.uvs, fallback: DEFAULT_TRIANGLE_UVS, onChange: (uvs) => onUpdate({ uvs }) }), _jsx(BooleanField, { name: "computeVertexNormals", label: "Compute Normals", values: properties, onChange: onUpdate, fallback: true }), _jsx(BooleanField, { name: "visible", label: "Visible", values: properties, onChange: onUpdate, fallback: true }), _jsx(BooleanField, { name: "castShadow", label: "Cast Shadow", values: properties, onChange: onUpdate, fallback: true }), _jsx(BooleanField, { name: "receiveShadow", label: "Receive Shadow", values: properties, onChange: onUpdate, fallback: true }), _jsx(BooleanField, { name: "emitClickEvent", label: "Emit Click Event", values: properties, onChange: onUpdate, fallback: false }), properties.emitClickEvent ? (_jsx(StringField, { name: "clickEventName", label: "Click Event Name", values: properties, onChange: onUpdate, placeholder: "entity:click" })) : null] }));
|
|
67
|
+
}
|
|
68
|
+
function BufferGeometryComponentView({ properties }) {
|
|
69
|
+
const positions = normalizeNumberArray(properties.positions, DEFAULT_TRIANGLE_POSITIONS);
|
|
70
|
+
const indices = normalizeNumberArray(properties.indices, DEFAULT_TRIANGLE_INDICES);
|
|
71
|
+
const normals = normalizeNumberArray(properties.normals, []);
|
|
72
|
+
const uvs = normalizeNumberArray(properties.uvs, DEFAULT_TRIANGLE_UVS);
|
|
73
|
+
const indexArray = getIndexArray(indices);
|
|
74
|
+
const hasNormals = normals.length >= 3 && normals.length % 3 === 0;
|
|
75
|
+
const hasUvs = uvs.length >= 2 && uvs.length % 2 === 0;
|
|
76
|
+
return (_jsxs("bufferGeometry", { onUpdate: (geometry) => {
|
|
77
|
+
if (properties.computeVertexNormals !== false && !hasNormals) {
|
|
78
|
+
geometry.computeVertexNormals();
|
|
79
|
+
}
|
|
80
|
+
geometry.computeBoundingBox();
|
|
81
|
+
geometry.computeBoundingSphere();
|
|
82
|
+
}, children: [_jsx("bufferAttribute", { attach: "attributes-position", args: [new Float32Array(positions), 3] }), indexArray ? (_jsx("bufferAttribute", { attach: "index", args: [indexArray, 1] })) : null, hasNormals ? (_jsx("bufferAttribute", { attach: "attributes-normal", args: [new Float32Array(normals), 3] })) : null, hasUvs ? (_jsx("bufferAttribute", { attach: "attributes-uv", args: [new Float32Array(uvs), 2] })) : null] }));
|
|
83
|
+
}
|
|
84
|
+
const BufferGeometryComponent = {
|
|
85
|
+
name: 'BufferGeometry',
|
|
86
|
+
Editor: BufferGeometryComponentEditor,
|
|
87
|
+
View: BufferGeometryComponentView,
|
|
88
|
+
defaultProperties: {
|
|
89
|
+
positions: DEFAULT_TRIANGLE_POSITIONS,
|
|
90
|
+
indices: DEFAULT_TRIANGLE_INDICES,
|
|
91
|
+
normals: [],
|
|
92
|
+
uvs: DEFAULT_TRIANGLE_UVS,
|
|
93
|
+
computeVertexNormals: true,
|
|
94
|
+
emitClickEvent: false,
|
|
95
|
+
clickEventName: '',
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
export default BufferGeometryComponent;
|
|
@@ -3,7 +3,7 @@ import { OrthographicCamera, PerspectiveCamera, useHelper } from '@react-three/d
|
|
|
3
3
|
import { useRef } from 'react';
|
|
4
4
|
import { CameraHelper } from 'three';
|
|
5
5
|
import { useFrame, useThree } from '@react-three/fiber';
|
|
6
|
-
import { useEntityRuntime } from '../
|
|
6
|
+
import { useEntityRuntime } from '../assetRuntime';
|
|
7
7
|
import { FieldGroup, NumberField, SelectField } from './Input';
|
|
8
8
|
const CAMERA_PROJECTION_OPTIONS = [
|
|
9
9
|
{ value: 'perspective', label: 'Perspective' },
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { FC } from "react";
|
|
2
2
|
import { ComponentData, GameObject } from "../types";
|
|
3
|
-
import type { ComponentInstance, ComponentRuntimeContext } from "../runtime";
|
|
4
3
|
export type AssetRef = {
|
|
5
4
|
type: "model" | "texture" | "sound";
|
|
6
5
|
path: string;
|
|
@@ -28,8 +27,6 @@ export interface Component {
|
|
|
28
27
|
}>;
|
|
29
28
|
defaultProperties: any;
|
|
30
29
|
View?: FC<ComponentViewProps>;
|
|
31
|
-
/** Optional runtime factory for the non-React game loop. */
|
|
32
|
-
create?: (ctx: ComponentRuntimeContext) => ComponentInstance | void;
|
|
33
30
|
/** Declare which asset paths this component references (for asset loading). */
|
|
34
31
|
getAssetRefs?: (properties: Record<string, any>) => AssetRef[];
|
|
35
32
|
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { colors } from "../styles";
|
|
4
|
+
const RESERVED_USER_DATA_KEYS = new Set([
|
|
5
|
+
'prefabNodeId',
|
|
6
|
+
'prefabNodeName',
|
|
7
|
+
]);
|
|
8
|
+
const inputStyle = {
|
|
9
|
+
width: '100%',
|
|
10
|
+
backgroundColor: colors.bgInput,
|
|
11
|
+
border: `1px solid ${colors.border}`,
|
|
12
|
+
padding: '6px 8px',
|
|
13
|
+
fontSize: '11px',
|
|
14
|
+
color: colors.text,
|
|
15
|
+
fontFamily: 'monospace',
|
|
16
|
+
outline: 'none',
|
|
17
|
+
borderRadius: 3,
|
|
18
|
+
boxSizing: 'border-box',
|
|
19
|
+
};
|
|
20
|
+
function isRecord(value) {
|
|
21
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
22
|
+
}
|
|
23
|
+
function normalizeData(value) {
|
|
24
|
+
if (!isRecord(value))
|
|
25
|
+
return {};
|
|
26
|
+
return Object.entries(value).reduce((result, [key, entry]) => {
|
|
27
|
+
if (!key.trim() || entry === undefined) {
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
result[key] = entry;
|
|
31
|
+
return result;
|
|
32
|
+
}, {});
|
|
33
|
+
}
|
|
34
|
+
function formatData(value) {
|
|
35
|
+
return JSON.stringify(normalizeData(value), null, 2);
|
|
36
|
+
}
|
|
37
|
+
function parseData(raw) {
|
|
38
|
+
const trimmed = raw.trim();
|
|
39
|
+
if (!trimmed) {
|
|
40
|
+
return { ok: true, value: {} };
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const parsed = JSON.parse(trimmed);
|
|
44
|
+
if (!isRecord(parsed)) {
|
|
45
|
+
return { ok: false, error: 'Data must be a JSON object' };
|
|
46
|
+
}
|
|
47
|
+
const nextData = normalizeData(parsed);
|
|
48
|
+
for (const key of Object.keys(nextData)) {
|
|
49
|
+
if (RESERVED_USER_DATA_KEYS.has(key)) {
|
|
50
|
+
return { ok: false, error: `Reserved key: ${key}` };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return { ok: true, value: nextData };
|
|
54
|
+
}
|
|
55
|
+
catch (_a) {
|
|
56
|
+
return { ok: false, error: 'Data must be valid JSON' };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function DataComponentEditor({ component, onUpdate }) {
|
|
60
|
+
var _a;
|
|
61
|
+
const [draft, setDraft] = useState(() => { var _a; return formatData((_a = component.properties) === null || _a === void 0 ? void 0 : _a.data); });
|
|
62
|
+
const [error, setError] = useState(null);
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
var _a;
|
|
65
|
+
setDraft(formatData((_a = component.properties) === null || _a === void 0 ? void 0 : _a.data));
|
|
66
|
+
setError(null);
|
|
67
|
+
}, [(_a = component.properties) === null || _a === void 0 ? void 0 : _a.data]);
|
|
68
|
+
const commitDraft = () => {
|
|
69
|
+
const parsed = parseData(draft);
|
|
70
|
+
if (!parsed.ok) {
|
|
71
|
+
setError(parsed.error);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
setError(null);
|
|
75
|
+
setDraft(formatData(parsed.value));
|
|
76
|
+
onUpdate({ data: parsed.value });
|
|
77
|
+
};
|
|
78
|
+
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: [_jsx("textarea", { rows: 10, spellCheck: false, value: draft, onChange: (event) => setDraft(event.target.value), onBlur: commitDraft, style: Object.assign(Object.assign({}, inputStyle), { resize: 'vertical', minHeight: 140 }) }), error ? (_jsx("div", { style: { fontSize: 10, color: colors.accent, fontFamily: 'monospace' }, children: error })) : null, _jsx("div", { style: { fontSize: 10, color: colors.textMuted, lineHeight: 1.4 }, children: "Enter a JSON object. Keys map directly onto `object.userData`." })] }));
|
|
79
|
+
}
|
|
80
|
+
const DataComponent = {
|
|
81
|
+
name: 'Data',
|
|
82
|
+
Editor: DataComponentEditor,
|
|
83
|
+
defaultProperties: {
|
|
84
|
+
data: {},
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
export default DataComponent;
|
|
@@ -3,7 +3,7 @@ import { useHelper } from "@react-three/drei";
|
|
|
3
3
|
import { useRef, useEffect, useState } from "react";
|
|
4
4
|
import { useFrame } from "@react-three/fiber";
|
|
5
5
|
import { CameraHelper } from "three";
|
|
6
|
-
import { useEntityRuntime } from "../
|
|
6
|
+
import { useEntityRuntime } from "../assetRuntime";
|
|
7
7
|
import { BooleanField, ColorField, NumberField, NumberInput, Vector3Input } from "./Input";
|
|
8
8
|
import { LightSection, ShadowBiasField, mergeWithDefaults } from "./lightUtils";
|
|
9
9
|
import { colors } from "../styles";
|
|
@@ -1,7 +1,7 @@
|
|
|
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 { useAssetRuntime } from '../
|
|
4
|
+
import { useAssetRuntime } from '../assetRuntime';
|
|
5
5
|
function EnvironmentView({ properties, children, }) {
|
|
6
6
|
const { getAssetRevision } = useAssetRuntime();
|
|
7
7
|
const { intensity = 1, resolution = 256 } = properties;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { BooleanField, FieldGroup, NumberField, SelectField } from "./Input";
|
|
2
|
+
import { BooleanField, FieldGroup, NumberField, SelectField, StringField } from "./Input";
|
|
3
3
|
const GEOMETRY_ARGS = {
|
|
4
4
|
box: {
|
|
5
5
|
fields: [
|
|
@@ -61,7 +61,7 @@ function GeometryComponentEditor({ component, onUpdate, }) {
|
|
|
61
61
|
] }), schema.fields.map((field, index) => {
|
|
62
62
|
var _a;
|
|
63
63
|
return (_jsx(NumberField, { name: field.name, label: field.label, values: { [field.name]: (_a = args[index]) !== null && _a !== void 0 ? _a : field.defaultValue }, onChange: (next) => updateArg(index, next[field.name]), fallback: field.defaultValue, min: field.min, step: field.step }, field.name));
|
|
64
|
-
}), _jsx(BooleanField, { name: "visible", label: "Visible", values: component.properties, onChange: handleChange, fallback: true }), _jsx(BooleanField, { name: "castShadow", label: "Cast Shadow", values: component.properties, onChange: handleChange, fallback: true }), _jsx(BooleanField, { name: "receiveShadow", label: "Receive Shadow", values: component.properties, onChange: handleChange, fallback: true })] }));
|
|
64
|
+
}), _jsx(BooleanField, { name: "visible", label: "Visible", values: component.properties, onChange: handleChange, fallback: true }), _jsx(BooleanField, { name: "castShadow", label: "Cast Shadow", values: component.properties, onChange: handleChange, fallback: true }), _jsx(BooleanField, { name: "receiveShadow", label: "Receive Shadow", values: component.properties, onChange: handleChange, fallback: true }), _jsx(BooleanField, { name: "emitClickEvent", label: "Emit Click Event", values: component.properties, onChange: handleChange, fallback: false }), component.properties.emitClickEvent ? (_jsx(StringField, { name: "clickEventName", label: "Click Event Name", values: component.properties, onChange: handleChange, placeholder: "cannon:fire" })) : null] }));
|
|
65
65
|
}
|
|
66
66
|
// View for Geometry component
|
|
67
67
|
function GeometryComponentView({ properties, children }) {
|
|
@@ -87,6 +87,8 @@ const GeometryComponent = {
|
|
|
87
87
|
defaultProperties: {
|
|
88
88
|
geometryType: 'box',
|
|
89
89
|
args: getDefaultArgs('box'),
|
|
90
|
+
emitClickEvent: false,
|
|
91
|
+
clickEventName: '',
|
|
90
92
|
}
|
|
91
93
|
};
|
|
92
94
|
export default GeometryComponent;
|