react-three-game 0.0.64 → 0.0.66

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.
Files changed (33) hide show
  1. package/LICENSE +2 -660
  2. package/README.md +164 -91
  3. package/dist/index.d.ts +4 -2
  4. package/dist/index.js +2 -1
  5. package/dist/tools/assetviewer/page.js +6 -6
  6. package/dist/tools/prefabeditor/EditorContext.d.ts +2 -2
  7. package/dist/tools/prefabeditor/EditorTree.js +17 -3
  8. package/dist/tools/prefabeditor/EditorTreeMenus.d.ts +3 -1
  9. package/dist/tools/prefabeditor/EditorTreeMenus.js +7 -8
  10. package/dist/tools/prefabeditor/EditorUI.js +3 -7
  11. package/dist/tools/prefabeditor/GameEvents.d.ts +14 -1
  12. package/dist/tools/prefabeditor/GameEvents.js +2 -1
  13. package/dist/tools/prefabeditor/InstanceProvider.d.ts +14 -0
  14. package/dist/tools/prefabeditor/InstanceProvider.js +198 -34
  15. package/dist/tools/prefabeditor/PrefabEditor.js +77 -16
  16. package/dist/tools/prefabeditor/PrefabRoot.d.ts +3 -1
  17. package/dist/tools/prefabeditor/PrefabRoot.js +88 -120
  18. package/dist/tools/prefabeditor/components/CameraComponent.js +1 -1
  19. package/dist/tools/prefabeditor/components/ClickComponent.d.ts +3 -0
  20. package/dist/tools/prefabeditor/components/ClickComponent.js +45 -0
  21. package/dist/tools/prefabeditor/components/ComponentRegistry.js +0 -3
  22. package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +3 -3
  23. package/dist/tools/prefabeditor/components/Input.d.ts +5 -2
  24. package/dist/tools/prefabeditor/components/Input.js +71 -38
  25. package/dist/tools/prefabeditor/components/MaterialComponent.js +3 -3
  26. package/dist/tools/prefabeditor/components/ModelComponent.js +95 -16
  27. package/dist/tools/prefabeditor/components/PhysicsComponent.d.ts +2 -0
  28. package/dist/tools/prefabeditor/components/PhysicsComponent.js +77 -10
  29. package/dist/tools/prefabeditor/components/index.js +2 -0
  30. package/dist/tools/prefabeditor/types.d.ts +1 -0
  31. package/dist/tools/prefabeditor/utils.d.ts +7 -1
  32. package/dist/tools/prefabeditor/utils.js +40 -2
  33. package/package.json +1 -1
@@ -8,25 +8,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
11
- import { MapControls, TransformControls, useHelper } from "@react-three/drei";
12
- import { forwardRef, useCallback, useContext, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
13
- import { BoxHelper, Euler, Matrix4, Quaternion, SRGBColorSpace, TextureLoader, Vector3, } from "three";
14
- import { getComponent, registerComponent, getNonComposableKeys } from "./components/ComponentRegistry";
11
+ import { useHelper } from "@react-three/drei";
12
+ import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
13
+ import { BoxHelper, Euler, Matrix4, Quaternion, Vector3, } from "three";
14
+ import { getComponent, registerComponent } from "./components/ComponentRegistry";
15
15
  import components from "./components";
16
- import { loadModel } from "../dragdrop";
17
- import { GameInstance, GameInstanceProvider, useInstanceCheck } from "./InstanceProvider";
18
- import { focusCameraOnObject, updateNode } from "./utils";
19
- import { EditorContext } from "./EditorContext";
16
+ import { loadModel, loadTexture } from "../dragdrop";
17
+ import { GameInstance, GameInstanceProvider, getRepeatAxesFromModelProperties, useInstanceCheck } from "./InstanceProvider";
18
+ import { decompose } from "./utils";
20
19
  components.forEach(registerComponent);
21
20
  const IDENTITY = new Matrix4();
22
- export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selectedId, onSelect, onClick, basePath = "", injectedModels = {}, injectedTextures = {} }, ref) => {
23
- var _a, _b, _c, _d;
24
- // optional editor context
25
- const editorContext = useContext(EditorContext);
26
- const transformMode = (_a = editorContext === null || editorContext === void 0 ? void 0 : editorContext.transformMode) !== null && _a !== void 0 ? _a : "translate";
27
- const snapResolution = (_b = editorContext === null || editorContext === void 0 ? void 0 : editorContext.snapResolution) !== null && _b !== void 0 ? _b : 0;
28
- const positionSnap = (_c = editorContext === null || editorContext === void 0 ? void 0 : editorContext.positionSnap) !== null && _c !== void 0 ? _c : 0.5;
29
- const rotationSnap = (_d = editorContext === null || editorContext === void 0 ? void 0 : editorContext.rotationSnap) !== null && _d !== void 0 ? _d : Math.PI / 4;
21
+ export const PrefabRoot = forwardRef(({ editMode, data, selectedId, onSelect, onClick, onSelectedObjectChange, onFocusNode, basePath = "", injectedModels = {}, injectedTextures = {} }, ref) => {
30
22
  // prefab root state
31
23
  const [models, setModels] = useState({});
32
24
  const [textures, setTextures] = useState({});
@@ -34,28 +26,21 @@ export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selected
34
26
  const failedTextures = useRef(new Set());
35
27
  const objectRefs = useRef({});
36
28
  const rigidBodyRefs = useRef(new Map());
37
- const [selectedObject, setSelectedObject] = useState(null);
38
29
  const rootRef = useRef(null);
39
- const controlsRef = useRef(null);
40
30
  const availableModels = useMemo(() => (Object.assign(Object.assign({}, models), injectedModels)), [models, injectedModels]);
41
31
  const availableTextures = useMemo(() => (Object.assign(Object.assign({}, textures), injectedTextures)), [textures, injectedTextures]);
42
32
  useImperativeHandle(ref, () => ({
43
33
  root: rootRef.current,
44
34
  rigidBodyRefs: rigidBodyRefs.current,
45
- focusNode: (nodeId) => {
46
- const object = objectRefs.current[nodeId];
47
- const controls = controlsRef.current;
48
- const camera = controls === null || controls === void 0 ? void 0 : controls.object;
49
- if (!object || !controls || !camera)
50
- return;
51
- focusCameraOnObject(object, camera, controls.target, () => { var _a; return (_a = controls.update) === null || _a === void 0 ? void 0 : _a.call(controls); });
52
- }
53
- }), []);
35
+ getObject: (nodeId) => { var _a; return (_a = objectRefs.current[nodeId]) !== null && _a !== void 0 ? _a : null; },
36
+ focusNode: (nodeId) => onFocusNode === null || onFocusNode === void 0 ? void 0 : onFocusNode(nodeId),
37
+ }), [onFocusNode]);
54
38
  const registerRef = useCallback((id, obj) => {
55
39
  objectRefs.current[id] = obj;
56
- if (id === selectedId)
57
- setSelectedObject(obj);
58
- }, [selectedId]);
40
+ if (id === selectedId) {
41
+ onSelectedObjectChange === null || onSelectedObjectChange === void 0 ? void 0 : onSelectedObjectChange(obj);
42
+ }
43
+ }, [onSelectedObjectChange, selectedId]);
59
44
  const registerRigidBodyRef = useCallback((id, rb) => {
60
45
  rigidBodyRefs.current.set(id, rb);
61
46
  }, []);
@@ -68,25 +53,6 @@ export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selected
68
53
  };
69
54
  return () => { console.error = originalError; };
70
55
  }, []);
71
- useEffect(() => {
72
- var _a;
73
- setSelectedObject(selectedId ? (_a = objectRefs.current[selectedId]) !== null && _a !== void 0 ? _a : null : null);
74
- }, [selectedId]);
75
- const onTransformChange = () => {
76
- if (!selectedId || !onPrefabChange)
77
- return;
78
- const obj = objectRefs.current[selectedId];
79
- if (!obj)
80
- return;
81
- const parentWorld = computeParentWorldMatrix(data.root, selectedId);
82
- const local = parentWorld.clone().invert().multiply(obj.matrixWorld);
83
- const { position, rotation, scale } = decompose(local);
84
- const root = updateNode(data.root, selectedId, node => (Object.assign(Object.assign({}, node), { components: Object.assign(Object.assign({}, node.components), { transform: {
85
- type: "Transform",
86
- properties: { position, rotation, scale },
87
- } }) })));
88
- onPrefabChange(Object.assign(Object.assign({}, data), { root }));
89
- };
90
56
  useEffect(() => {
91
57
  const modelsToLoad = new Set();
92
58
  const texturesToLoad = new Set();
@@ -112,7 +78,6 @@ export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selected
112
78
  setModels(m => (Object.assign(Object.assign({}, m), { [file]: model })));
113
79
  }
114
80
  }));
115
- const loader = new TextureLoader();
116
81
  texturesToLoad.forEach(file => {
117
82
  if (availableTextures[file] || loading.current.has(file) || failedTextures.current.has(file))
118
83
  return;
@@ -123,17 +88,18 @@ export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selected
123
88
  : file.startsWith("/")
124
89
  ? `${basePath}${file}`
125
90
  : `${basePath}/${file}`;
126
- loader.load(path, tex => {
127
- tex.colorSpace = SRGBColorSpace;
128
- setTextures(t => (Object.assign(Object.assign({}, t), { [file]: tex })));
129
- }, undefined, (err) => {
130
- console.warn(`Failed to load texture: ${path}`, err);
91
+ void loadTexture(path).then(result => {
92
+ if (result.success && result.texture) {
93
+ setTextures(t => (Object.assign(Object.assign({}, t), { [file]: result.texture })));
94
+ return;
95
+ }
96
+ console.warn(`Failed to load texture: ${path}`, result.error);
131
97
  loading.current.delete(file);
132
98
  failedTextures.current.add(file);
133
99
  });
134
100
  });
135
101
  }, [data, availableModels, availableTextures, basePath]);
136
- return (_jsxs("group", { ref: rootRef, children: [_jsx(GameInstanceProvider, { models: availableModels, selectedId: selectedId, editMode: editMode, onSelect: editMode ? onSelect : undefined, registerRef: registerRef, children: _jsx(GameObjectRenderer, { gameObject: data.root, selectedId: selectedId, onSelect: editMode ? onSelect : undefined, onClick: onClick, registerRef: registerRef, registerRigidBodyRef: registerRigidBodyRef, loadedModels: availableModels, loadedTextures: availableTextures, editMode: editMode, parentMatrix: IDENTITY }) }), editMode && (_jsxs(_Fragment, { children: [_jsx(MapControls, { ref: controlsRef, makeDefault: true }), selectedObject && (_jsx(TransformControls, { object: selectedObject, mode: transformMode, space: "local", onObjectChange: onTransformChange, translationSnap: positionSnap > 0 ? positionSnap : undefined, rotationSnap: rotationSnap > 0 ? rotationSnap : undefined, scaleSnap: snapResolution > 0 ? snapResolution : undefined }, `transform-${transformMode}-${positionSnap}-${rotationSnap}-${snapResolution}`))] }))] }));
102
+ return (_jsx("group", { ref: rootRef, children: _jsx(GameInstanceProvider, { models: availableModels, selectedId: selectedId, editMode: editMode, onSelect: editMode ? onSelect : undefined, registerRef: registerRef, children: _jsx(GameObjectRenderer, { gameObject: data.root, selectedId: selectedId, onSelect: editMode ? onSelect : undefined, onClick: onClick, registerRef: registerRef, registerRigidBodyRef: registerRigidBodyRef, loadedModels: availableModels, loadedTextures: availableTextures, editMode: editMode, parentMatrix: IDENTITY }) }) }));
137
103
  });
138
104
  export function GameObjectRenderer(props) {
139
105
  var _a, _b, _c;
@@ -159,16 +125,18 @@ export function GameObjectRenderer(props) {
159
125
  : _jsx(StandardNode, Object.assign({}, props), key);
160
126
  }
161
127
  function isPhysicsProps(v) {
162
- return (v === null || v === void 0 ? void 0 : v.type) === "fixed" || (v === null || v === void 0 ? void 0 : v.type) === "dynamic";
128
+ 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";
163
129
  }
164
130
  function InstancedNode({ gameObject, parentMatrix = IDENTITY, editMode, registerRef, selectedId: _selectedId, onSelect, onClick }) {
165
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
166
- const world = parentMatrix.clone().multiply(compose(gameObject));
167
- const { position: worldPosition, rotation: worldRotation, scale: worldScale } = decompose(world);
131
+ var _a, _b, _c, _d, _e, _f, _g, _h;
168
132
  const localTransform = getNodeTransformProps(gameObject);
169
- const physicsProps = isPhysicsProps((_b = (_a = gameObject.components) === null || _a === void 0 ? void 0 : _a.physics) === null || _b === void 0 ? void 0 : _b.properties)
170
- ? (_d = (_c = gameObject.components) === null || _c === void 0 ? void 0 : _c.physics) === null || _d === void 0 ? void 0 : _d.properties
133
+ const isLocked = Boolean(gameObject.locked);
134
+ const clickable = Object.values((_a = gameObject.components) !== null && _a !== void 0 ? _a : {}).some(component => (component === null || component === void 0 ? void 0 : component.type) === 'Click');
135
+ const physicsProps = isPhysicsProps((_c = (_b = gameObject.components) === null || _b === void 0 ? void 0 : _b.physics) === null || _c === void 0 ? void 0 : _c.properties)
136
+ ? (_e = (_d = gameObject.components) === null || _d === void 0 ? void 0 : _d.physics) === null || _e === void 0 ? void 0 : _e.properties
171
137
  : undefined;
138
+ const modelUrl = (_h = (_g = (_f = gameObject.components) === null || _f === void 0 ? void 0 : _f.model) === null || _g === void 0 ? void 0 : _g.properties) === null || _h === void 0 ? void 0 : _h.filename;
139
+ const instances = useMemo(() => buildRepeatedInstances(gameObject, parentMatrix, modelUrl, physicsProps), [gameObject, modelUrl, parentMatrix, physicsProps]);
172
140
  const groupRef = useRef(null);
173
141
  const clickValid = useRef(false);
174
142
  useEffect(() => {
@@ -177,24 +145,24 @@ function InstancedNode({ gameObject, parentMatrix = IDENTITY, editMode, register
177
145
  return () => registerRef(gameObject.id, null);
178
146
  }
179
147
  }, [gameObject.id, registerRef, editMode]);
180
- const modelUrl = (_g = (_f = (_e = gameObject.components) === null || _e === void 0 ? void 0 : _e.model) === null || _f === void 0 ? void 0 : _f.properties) === null || _g === void 0 ? void 0 : _g.filename;
181
148
  if (editMode) {
182
- return (_jsxs(_Fragment, { children: [_jsx("group", { ref: groupRef, position: localTransform.position, rotation: localTransform.rotation, scale: localTransform.scale, onPointerDown: (e) => { e.stopPropagation(); clickValid.current = true; }, onPointerMove: () => { clickValid.current = false; }, onPointerUp: (e) => {
149
+ return (_jsxs(_Fragment, { children: [_jsx("group", { ref: groupRef, position: localTransform.position, rotation: localTransform.rotation, scale: localTransform.scale, onPointerDown: isLocked ? undefined : (e) => { e.stopPropagation(); clickValid.current = true; }, onPointerMove: isLocked ? undefined : () => { clickValid.current = false; }, onPointerUp: isLocked ? undefined : (e) => {
183
150
  if (clickValid.current) {
184
151
  e.stopPropagation();
185
152
  onSelect === null || onSelect === void 0 ? void 0 : onSelect(gameObject.id);
186
153
  onClick === null || onClick === void 0 ? void 0 : onClick(e, gameObject);
187
154
  }
188
155
  clickValid.current = false;
189
- }, children: _jsx("mesh", { visible: false, children: _jsx("boxGeometry", { args: [0.01, 0.01, 0.01] }) }) }), _jsx(GameInstance, { id: gameObject.id, modelUrl: modelUrl, position: worldPosition, rotation: worldRotation, scale: worldScale, physics: physicsProps })] }));
156
+ }, 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)))] }));
190
157
  }
191
- return (_jsx(GameInstance, { id: gameObject.id, modelUrl: (_k = (_j = (_h = gameObject.components) === null || _h === void 0 ? void 0 : _h.model) === null || _j === void 0 ? void 0 : _j.properties) === null || _k === void 0 ? void 0 : _k.filename, position: worldPosition, rotation: worldRotation, scale: worldScale, physics: physicsProps }));
158
+ 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))) }));
192
159
  }
193
160
  function StandardNode({ gameObject, selectedId, onSelect, onClick, registerRef, registerRigidBodyRef, loadedModels, loadedTextures, editMode, parentMatrix = IDENTITY, }) {
194
161
  var _a, _b, _c, _d, _e;
195
162
  const groupRef = useRef(null);
196
163
  const helperRef = useRef(null);
197
164
  const clickValid = useRef(false);
165
+ const isLocked = Boolean(gameObject.locked);
198
166
  const isSelected = selectedId === gameObject.id;
199
167
  const stillInstanced = useInstanceCheck(gameObject.id);
200
168
  useHelper(editMode && isSelected ? helperRef : null, BoxHelper, "cyan");
@@ -236,26 +204,22 @@ function StandardNode({ gameObject, selectedId, onSelect, onClick, registerRef,
236
204
  loadedTextures,
237
205
  editMode,
238
206
  });
239
- const inner = (_jsx("group", { onPointerDown: editMode ? onDown : undefined, onPointerMove: editMode ? () => (clickValid.current = false) : undefined, onPointerUp: editMode ? onUp : undefined, children: renderCompositionNode(gameObject, renderCtx, parentMatrix, childNodes) }));
207
+ const inner = (_jsx("group", { onPointerDown: editMode && !isLocked ? onDown : undefined, onPointerMove: editMode && !isLocked ? () => (clickValid.current = false) : undefined, onPointerUp: editMode && !isLocked ? onUp : undefined, children: renderCompositionNode(gameObject, renderCtx, parentMatrix, childNodes) }));
240
208
  if (editMode) {
241
209
  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: gameObject.id, registerRigidBodyRef: registerRigidBodyRef, children: inner }, physicsKey)) : null] }));
242
210
  }
243
211
  if (hasPhysics && (physicsDef === null || physicsDef === void 0 ? void 0 : physicsDef.View)) {
244
212
  return (_jsx(physicsDef.View, { properties: physics.properties, position: transform.position, rotation: transform.rotation, scale: transform.scale, editMode: editMode, nodeId: gameObject.id, registerRigidBodyRef: registerRigidBodyRef, children: inner }, physicsKey));
245
213
  }
246
- return (_jsx("group", { ref: groupRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, onPointerDown: onDown, onPointerMove: () => (clickValid.current = false), onPointerUp: onUp, children: inner }));
247
- }
248
- const CHILD_HOST_COMPONENT_TYPES = new Set(["Environment"]);
249
- function isChildHostType(type) {
250
- return CHILD_HOST_COMPONENT_TYPES.has(type);
214
+ return (_jsx("group", { ref: groupRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: inner }));
251
215
  }
252
216
  function getChildHostComponents(gameObject) {
253
217
  var _a;
254
218
  return Object.entries((_a = gameObject.components) !== null && _a !== void 0 ? _a : {}).flatMap(([key, comp]) => {
255
- if (!(comp === null || comp === void 0 ? void 0 : comp.type) || !isChildHostType(comp.type))
219
+ if (!(comp === null || comp === void 0 ? void 0 : comp.type))
256
220
  return [];
257
221
  const def = getComponent(comp.type);
258
- if (!(def === null || def === void 0 ? void 0 : def.View))
222
+ if (!(def === null || def === void 0 ? void 0 : def.View) || def.nonComposable)
259
223
  return [];
260
224
  return { key, View: def.View, properties: comp.properties };
261
225
  });
@@ -276,16 +240,58 @@ function compose(node) {
276
240
  const { position, rotation, scale } = getNodeTransformProps(node);
277
241
  return new Matrix4().compose(new Vector3(...position), new Quaternion().setFromEuler(new Euler(...rotation)), new Vector3(...scale));
278
242
  }
279
- function decompose(m) {
280
- const p = new Vector3(), q = new Quaternion(), s = new Vector3();
281
- m.decompose(p, q, s);
282
- const e = new Euler().setFromQuaternion(q);
243
+ function getModelRepeatSettings(node) {
244
+ var _a, _b, _c;
245
+ const properties = (_c = (_b = (_a = node === null || node === void 0 ? void 0 : node.components) === null || _a === void 0 ? void 0 : _a.model) === null || _b === void 0 ? void 0 : _b.properties) !== null && _c !== void 0 ? _c : {};
283
246
  return {
284
- position: [p.x, p.y, p.z],
285
- rotation: [e.x, e.y, e.z],
286
- scale: [s.x, s.y, s.z],
247
+ repeat: Boolean(properties.repeat),
248
+ repeatAxes: getRepeatAxesFromModelProperties(properties),
287
249
  };
288
250
  }
251
+ function buildRepeatedInstances(gameObject, parentMatrix, modelUrl, physics) {
252
+ if (!modelUrl)
253
+ return [];
254
+ const transform = getNodeTransformProps(gameObject);
255
+ const repeat = getModelRepeatSettings(gameObject);
256
+ const counts = [1, 1, 1];
257
+ const offsets = [0, 0, 0];
258
+ if (repeat.repeat) {
259
+ for (const entry of repeat.repeatAxes) {
260
+ const axisIndex = entry.axis === 'x' ? 0 : entry.axis === 'y' ? 1 : 2;
261
+ counts[axisIndex] = entry.count;
262
+ offsets[axisIndex] = entry.offset;
263
+ }
264
+ }
265
+ const baseTranslation = new Matrix4().makeTranslation(transform.position[0], transform.position[1], transform.position[2]);
266
+ const baseRotation = new Matrix4().makeRotationFromEuler(new Euler(...transform.rotation));
267
+ const baseScale = new Matrix4().makeScale(transform.scale[0], transform.scale[1], transform.scale[2]);
268
+ const offsetMatrix = new Matrix4();
269
+ const worldMatrix = new Matrix4();
270
+ const instances = [];
271
+ for (let x = 0; x < counts[0]; x++) {
272
+ for (let y = 0; y < counts[1]; y++) {
273
+ for (let z = 0; z < counts[2]; z++) {
274
+ offsetMatrix.makeTranslation(x * offsets[0], y * offsets[1], z * offsets[2]);
275
+ worldMatrix.copy(parentMatrix)
276
+ .multiply(baseTranslation)
277
+ .multiply(baseRotation)
278
+ .multiply(offsetMatrix)
279
+ .multiply(baseScale);
280
+ const { position, rotation, scale } = decompose(worldMatrix);
281
+ const isBaseInstance = x === 0 && y === 0 && z === 0;
282
+ instances.push({
283
+ id: isBaseInstance ? gameObject.id : `${gameObject.id}__repeat_${x}_${y}_${z}`,
284
+ modelUrl,
285
+ position,
286
+ rotation,
287
+ scale,
288
+ physics,
289
+ });
290
+ }
291
+ }
292
+ }
293
+ return instances;
294
+ }
289
295
  function getNodeTransformProps(node) {
290
296
  var _a, _b, _c, _d, _e;
291
297
  const t = (_b = (_a = node === null || node === void 0 ? void 0 : node.components) === null || _a === void 0 ? void 0 : _a.transform) === null || _b === void 0 ? void 0 : _b.properties;
@@ -295,20 +301,6 @@ function getNodeTransformProps(node) {
295
301
  scale: (_e = t === null || t === void 0 ? void 0 : t.scale) !== null && _e !== void 0 ? _e : [1, 1, 1],
296
302
  };
297
303
  }
298
- function computeParentWorldMatrix(root, targetId) {
299
- let result = null;
300
- const visit = (node, parent) => {
301
- var _a;
302
- if (node.id === targetId) {
303
- result = parent.clone();
304
- return;
305
- }
306
- const world = parent.clone().multiply(compose(node));
307
- (_a = node.children) === null || _a === void 0 ? void 0 : _a.forEach(c => !result && visit(c, world));
308
- };
309
- visit(root, IDENTITY);
310
- return result !== null && result !== void 0 ? result : IDENTITY;
311
- }
312
304
  function renderCompositionSubtree(gameObject, ctx, parentMatrix = IDENTITY) {
313
305
  if (!gameObject || gameObject.disabled)
314
306
  return null;
@@ -323,9 +315,7 @@ function renderHostedChildren(gameObject, ctx, parentMatrix) {
323
315
  }
324
316
  function renderCompositionNode(gameObject, ctx, parentMatrix, childNodes) {
325
317
  const ownContent = renderNodeOwnContent(gameObject, ctx, parentMatrix);
326
- const siblingContent = renderNodeSiblingComponents(gameObject, ctx, parentMatrix);
327
- const subtree = _jsxs(_Fragment, { children: [ownContent, siblingContent, childNodes] });
328
- return wrapWithChildHosts(gameObject, ctx, parentMatrix, subtree);
318
+ return wrapWithChildHosts(gameObject, ctx, parentMatrix, _jsxs(_Fragment, { children: [ownContent, childNodes] }));
329
319
  }
330
320
  function renderNodeOwnContent(gameObject, ctx, parentMatrix) {
331
321
  var _a, _b, _c, _d;
@@ -361,28 +351,6 @@ function renderNodeOwnContent(gameObject, ctx, parentMatrix) {
361
351
  }
362
352
  return core;
363
353
  }
364
- function renderNodeSiblingComponents(gameObject, ctx, parentMatrix) {
365
- var _a;
366
- const contextProps = {
367
- loadedModels: ctx.loadedModels,
368
- loadedTextures: ctx.loadedTextures,
369
- editMode: ctx.editMode,
370
- isSelected: ctx.selectedId === gameObject.id,
371
- nodeId: gameObject.id,
372
- parentMatrix,
373
- registerRef: ctx.registerRef,
374
- };
375
- return Object.entries((_a = gameObject.components) !== null && _a !== void 0 ? _a : {})
376
- .filter(([key]) => !getNonComposableKeys().includes(key))
377
- .flatMap(([key, comp]) => {
378
- if (!(comp === null || comp === void 0 ? void 0 : comp.type) || isChildHostType(comp.type))
379
- return [];
380
- const def = getComponent(comp.type);
381
- if (!(def === null || def === void 0 ? void 0 : def.View))
382
- return [];
383
- return _jsx(def.View, Object.assign({ properties: comp.properties }, contextProps), key);
384
- });
385
- }
386
354
  function wrapWithChildHosts(gameObject, ctx, parentMatrix, subtree) {
387
355
  const contextProps = {
388
356
  loadedModels: ctx.loadedModels,
@@ -34,7 +34,7 @@ function CameraComponentView({ properties, editMode, isSelected }) {
34
34
  cameraHelper.update();
35
35
  }
36
36
  });
37
- return (_jsxs(_Fragment, { children: [_jsx(PerspectiveCamera, { ref: (instance) => setCamera(instance), makeDefault: !editMode, fov: fov, near: near, zoom: zoom, far: far }), editMode && isSelected && cameraHelper && (_jsx("primitive", { object: cameraHelper })), editMode && !isSelected ? (_jsxs("mesh", { children: [_jsx("boxGeometry", { args: [0.34, 0.22, 0.18] }), _jsx("meshBasicMaterial", { color: "#22d3ee", wireframe: true })] })) : null] }));
37
+ return (_jsxs(_Fragment, { children: [_jsx(PerspectiveCamera, { ref: (instance) => setCamera(instance), makeDefault: !editMode, fov: fov, near: near, zoom: zoom, far: far }), editMode && isSelected && cameraHelper && (_jsx("primitive", { object: cameraHelper })), editMode ? (_jsxs("group", { children: [_jsxs("mesh", { children: [_jsx("boxGeometry", { args: [0.3, 0.3, 0.5] }), _jsx("meshBasicMaterial", { color: '#22d3ee', wireframe: true })] }), _jsxs("mesh", { position: [0, 0, -0.25], rotation: [Math.PI / 2, 0, 0], children: [_jsx("coneGeometry", { args: [0.08, 0.16, 16] }), _jsx("meshBasicMaterial", { color: '#22d3ee', wireframe: true })] })] })) : null] }));
38
38
  }
39
39
  const CameraComponent = {
40
40
  name: 'Camera',
@@ -0,0 +1,3 @@
1
+ import { Component } from './ComponentRegistry';
2
+ declare const ClickComponent: Component;
3
+ export default ClickComponent;
@@ -0,0 +1,45 @@
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useRef } from 'react';
3
+ import { gameEvents } from '../GameEvents';
4
+ import { FieldGroup } from './Input';
5
+ function ClickComponentEditor() {
6
+ return (_jsx(FieldGroup, { children: _jsx("div", { style: { fontSize: 12, opacity: 0.8 }, children: "Emits a click game event in play mode when this entity is clicked." }) }));
7
+ }
8
+ function ClickComponentView({ children, editMode, nodeId }) {
9
+ const clickValid = useRef(false);
10
+ const emitClick = (event) => {
11
+ if (!nodeId)
12
+ return;
13
+ gameEvents.emit('click', {
14
+ sourceEntityId: nodeId,
15
+ point: [event.point.x, event.point.y, event.point.z],
16
+ button: event.button,
17
+ altKey: event.nativeEvent.altKey,
18
+ ctrlKey: event.nativeEvent.ctrlKey,
19
+ metaKey: event.nativeEvent.metaKey,
20
+ shiftKey: event.nativeEvent.shiftKey,
21
+ });
22
+ };
23
+ if (editMode) {
24
+ return _jsx(_Fragment, { children: children });
25
+ }
26
+ return (_jsx("group", { onPointerDown: (event) => {
27
+ event.stopPropagation();
28
+ clickValid.current = true;
29
+ }, onPointerMove: () => {
30
+ clickValid.current = false;
31
+ }, onPointerUp: (event) => {
32
+ if (!clickValid.current)
33
+ return;
34
+ event.stopPropagation();
35
+ emitClick(event);
36
+ clickValid.current = false;
37
+ }, children: children }));
38
+ }
39
+ const ClickComponent = {
40
+ name: 'Click',
41
+ Editor: ClickComponentEditor,
42
+ View: ClickComponentView,
43
+ defaultProperties: {},
44
+ };
45
+ export default ClickComponent;
@@ -1,8 +1,5 @@
1
1
  const REGISTRY = {};
2
2
  export function registerComponent(component) {
3
- if (REGISTRY[component.name]) {
4
- throw new Error(`Component with name ${component.name} already registered.`);
5
- }
6
3
  REGISTRY[component.name] = component;
7
4
  }
8
5
  export function getComponent(name) {
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import { useRef, useEffect, useMemo, useState } from "react";
3
3
  import { useFrame } from "@react-three/fiber";
4
4
  import { CameraHelper, Vector3 } from "three";
5
- import { FieldRenderer, Input } from "./Input";
5
+ import { FieldRenderer, NumberInput } from "./Input";
6
6
  const smallLabel = { display: 'block', fontSize: '8px', color: 'rgba(34, 211, 238, 0.5)', marginBottom: 2 };
7
7
  const directionalLightDefaults = {
8
8
  color: '#ffffff',
@@ -28,7 +28,7 @@ const directionalLightFields = [
28
28
  label: 'Shadow Camera',
29
29
  render: ({ values, onChangeMultiple }) => {
30
30
  var _a, _b, _c, _d, _e, _f;
31
- return (_jsxs("div", { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 4 }, children: [_jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Near" }), _jsx(Input, { step: 0.1, value: (_a = values.shadowCameraNear) !== null && _a !== void 0 ? _a : 0.1, onChange: v => onChangeMultiple({ shadowCameraNear: v }) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Far" }), _jsx(Input, { step: 1, value: (_b = values.shadowCameraFar) !== null && _b !== void 0 ? _b : 100, onChange: v => onChangeMultiple({ shadowCameraFar: v }) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Top" }), _jsx(Input, { step: 1, value: (_c = values.shadowCameraTop) !== null && _c !== void 0 ? _c : 30, onChange: v => onChangeMultiple({ shadowCameraTop: v }) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Bottom" }), _jsx(Input, { step: 1, value: (_d = values.shadowCameraBottom) !== null && _d !== void 0 ? _d : -30, onChange: v => onChangeMultiple({ shadowCameraBottom: v }) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Left" }), _jsx(Input, { step: 1, value: (_e = values.shadowCameraLeft) !== null && _e !== void 0 ? _e : -30, onChange: v => onChangeMultiple({ shadowCameraLeft: v }) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Right" }), _jsx(Input, { step: 1, value: (_f = values.shadowCameraRight) !== null && _f !== void 0 ? _f : 30, onChange: v => onChangeMultiple({ shadowCameraRight: v }) })] })] }));
31
+ return (_jsxs("div", { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 4 }, children: [_jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Near" }), _jsx(NumberInput, { step: 0.1, value: (_a = values.shadowCameraNear) !== null && _a !== void 0 ? _a : 0.1, onChange: v => onChangeMultiple({ shadowCameraNear: v }) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Far" }), _jsx(NumberInput, { step: 1, value: (_b = values.shadowCameraFar) !== null && _b !== void 0 ? _b : 100, onChange: v => onChangeMultiple({ shadowCameraFar: v }) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Top" }), _jsx(NumberInput, { step: 1, value: (_c = values.shadowCameraTop) !== null && _c !== void 0 ? _c : 30, onChange: v => onChangeMultiple({ shadowCameraTop: v }) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Bottom" }), _jsx(NumberInput, { step: 1, value: (_d = values.shadowCameraBottom) !== null && _d !== void 0 ? _d : -30, onChange: v => onChangeMultiple({ shadowCameraBottom: v }) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Left" }), _jsx(NumberInput, { step: 1, value: (_e = values.shadowCameraLeft) !== null && _e !== void 0 ? _e : -30, onChange: v => onChangeMultiple({ shadowCameraLeft: v }) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Right" }), _jsx(NumberInput, { step: 1, value: (_f = values.shadowCameraRight) !== null && _f !== void 0 ? _f : 30, onChange: v => onChangeMultiple({ shadowCameraRight: v }) })] })] }));
32
32
  },
33
33
  },
34
34
  {
@@ -37,7 +37,7 @@ const directionalLightFields = [
37
37
  label: 'Target Offset',
38
38
  render: ({ value, onChange }) => {
39
39
  const offset = value !== null && value !== void 0 ? value : [0, -5, 0];
40
- return (_jsxs("div", { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 4 }, children: [_jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "X" }), _jsx(Input, { step: 0.5, value: offset[0], onChange: v => onChange([v, offset[1], offset[2]]) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Y" }), _jsx(Input, { step: 0.5, value: offset[1], onChange: v => onChange([offset[0], v, offset[2]]) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Z" }), _jsx(Input, { step: 0.5, value: offset[2], onChange: v => onChange([offset[0], offset[1], v]) })] })] }));
40
+ return (_jsxs("div", { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 4 }, children: [_jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "X" }), _jsx(NumberInput, { step: 0.5, value: offset[0], onChange: v => onChange([v, offset[1], offset[2]]) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Y" }), _jsx(NumberInput, { step: 0.5, value: offset[1], onChange: v => onChange([offset[0], v, offset[2]]) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Z" }), _jsx(NumberInput, { step: 0.5, value: offset[2], onChange: v => onChange([offset[0], offset[1], v]) })] })] }));
41
41
  },
42
42
  },
43
43
  ];
@@ -52,12 +52,15 @@ interface InputProps {
52
52
  min?: number;
53
53
  max?: number;
54
54
  style?: React.CSSProperties;
55
- label?: string;
56
55
  }
57
- export declare function Input({ value, onChange, step, min, max, style, label }: InputProps): import("react/jsx-runtime").JSX.Element;
56
+ export declare function NumberInput({ value, onChange, step, min, max, style }: InputProps): import("react/jsx-runtime").JSX.Element;
58
57
  export declare function Label({ children }: {
59
58
  children: React.ReactNode;
60
59
  }): import("react/jsx-runtime").JSX.Element;
60
+ export declare function FieldRow({ label, children, }: {
61
+ label: string;
62
+ children: React.ReactNode;
63
+ }): import("react/jsx-runtime").JSX.Element;
61
64
  export declare function Vector3Input({ label, value, onChange, snap, labelExtra }: {
62
65
  label: string;
63
66
  value: [number, number, number];