react-three-game 0.0.41 → 0.0.42

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 (63) hide show
  1. package/README.md +0 -17
  2. package/dist/helpers/SoundManager.d.ts +35 -0
  3. package/dist/helpers/SoundManager.js +93 -0
  4. package/dist/helpers/index.d.ts +35 -0
  5. package/dist/helpers/index.js +44 -0
  6. package/dist/index.d.ts +14 -0
  7. package/dist/index.js +14 -0
  8. package/dist/index.umd.js +4347 -163
  9. package/dist/shared/GameCanvas.d.ts +9 -0
  10. package/dist/shared/GameCanvas.js +47 -0
  11. package/dist/tools/assetviewer/page.d.ts +35 -0
  12. package/dist/tools/assetviewer/page.js +166 -0
  13. package/dist/tools/dragdrop/DragDropLoader.d.ts +9 -0
  14. package/dist/tools/dragdrop/DragDropLoader.js +78 -0
  15. package/dist/tools/dragdrop/modelLoader.d.ts +7 -0
  16. package/dist/tools/dragdrop/modelLoader.js +52 -0
  17. package/dist/tools/dragdrop/page.d.ts +1 -0
  18. package/dist/tools/dragdrop/page.js +11 -0
  19. package/dist/tools/prefabeditor/EditorContext.d.ts +11 -0
  20. package/dist/tools/prefabeditor/EditorContext.js +9 -0
  21. package/dist/tools/prefabeditor/EditorTree.d.ts +12 -0
  22. package/dist/tools/prefabeditor/EditorTree.js +150 -0
  23. package/dist/tools/prefabeditor/EditorUI.d.ts +14 -0
  24. package/dist/tools/prefabeditor/EditorUI.js +71 -0
  25. package/dist/tools/prefabeditor/EventSystem.d.ts +7 -0
  26. package/dist/tools/prefabeditor/EventSystem.js +23 -0
  27. package/dist/tools/prefabeditor/InstanceProvider.d.ts +30 -0
  28. package/dist/tools/prefabeditor/InstanceProvider.js +254 -0
  29. package/dist/tools/prefabeditor/PrefabEditor.d.ts +16 -0
  30. package/dist/tools/prefabeditor/PrefabEditor.js +140 -0
  31. package/dist/tools/prefabeditor/PrefabRoot.d.ts +28 -0
  32. package/dist/tools/prefabeditor/PrefabRoot.js +293 -0
  33. package/dist/tools/prefabeditor/components/ComponentRegistry.d.ts +18 -0
  34. package/dist/tools/prefabeditor/components/ComponentRegistry.js +13 -0
  35. package/dist/tools/prefabeditor/components/DirectionalLightComponent.d.ts +3 -0
  36. package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +78 -0
  37. package/dist/tools/prefabeditor/components/GeometryComponent.d.ts +3 -0
  38. package/dist/tools/prefabeditor/components/GeometryComponent.js +66 -0
  39. package/dist/tools/prefabeditor/components/Input.d.ts +20 -0
  40. package/dist/tools/prefabeditor/components/Input.js +129 -0
  41. package/dist/tools/prefabeditor/components/MaterialComponent.d.ts +3 -0
  42. package/dist/tools/prefabeditor/components/MaterialComponent.js +100 -0
  43. package/dist/tools/prefabeditor/components/ModelComponent.d.ts +3 -0
  44. package/dist/tools/prefabeditor/components/ModelComponent.js +57 -0
  45. package/dist/tools/prefabeditor/components/PhysicsComponent.d.ts +10 -0
  46. package/dist/tools/prefabeditor/components/PhysicsComponent.js +33 -0
  47. package/dist/tools/prefabeditor/components/SpotLightComponent.d.ts +3 -0
  48. package/dist/tools/prefabeditor/components/SpotLightComponent.js +49 -0
  49. package/dist/tools/prefabeditor/components/TransformComponent.d.ts +3 -0
  50. package/dist/tools/prefabeditor/components/TransformComponent.js +42 -0
  51. package/dist/tools/prefabeditor/components/index.d.ts +2 -0
  52. package/dist/tools/prefabeditor/components/index.js +16 -0
  53. package/dist/tools/prefabeditor/page.d.ts +1 -0
  54. package/dist/tools/prefabeditor/page.js +5 -0
  55. package/dist/tools/prefabeditor/styles.d.ts +1809 -0
  56. package/dist/tools/prefabeditor/styles.js +167 -0
  57. package/dist/tools/prefabeditor/types.d.ts +19 -0
  58. package/dist/tools/prefabeditor/types.js +1 -0
  59. package/dist/tools/prefabeditor/utils.d.ts +26 -0
  60. package/dist/tools/prefabeditor/utils.js +131 -0
  61. package/package.json +2 -4
  62. package/.claude/settings.local.json +0 -9
  63. package/vite.config.ts +0 -34
@@ -0,0 +1,293 @@
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
+ 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, useRef, useState } from "react";
13
+ import { BoxHelper, Euler, Matrix4, Quaternion, SRGBColorSpace, TextureLoader, Vector3, } from "three";
14
+ import { getComponent, registerComponent } from "./components/ComponentRegistry";
15
+ import components from "./components";
16
+ import { loadModel } from "../dragdrop/modelLoader";
17
+ import { GameInstance, GameInstanceProvider, useInstanceCheck } from "./InstanceProvider";
18
+ import { updateNode } from "./utils";
19
+ import { EditorContext } from "./EditorContext";
20
+ components.forEach(registerComponent);
21
+ const IDENTITY = new Matrix4();
22
+ export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selectedId, onSelect, onClick, basePath = "" }, ref) => {
23
+ var _a, _b;
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
+ // prefab root state
29
+ const [models, setModels] = useState({});
30
+ const [textures, setTextures] = useState({});
31
+ const loading = useRef(new Set());
32
+ const objectRefs = useRef({});
33
+ const [selectedObject, setSelectedObject] = useState(null);
34
+ const rootRef = useRef(null);
35
+ useImperativeHandle(ref, () => ({
36
+ root: rootRef.current
37
+ }), []);
38
+ const registerRef = useCallback((id, obj) => {
39
+ objectRefs.current[id] = obj;
40
+ if (id === selectedId)
41
+ setSelectedObject(obj);
42
+ }, [selectedId]);
43
+ useEffect(() => {
44
+ const originalError = console.error;
45
+ console.error = (...args) => {
46
+ if (typeof args[0] === 'string' && args[0].includes('TransformControls') && args[0].includes('scene graph'))
47
+ return;
48
+ originalError.apply(console, args);
49
+ };
50
+ return () => { console.error = originalError; };
51
+ }, []);
52
+ useEffect(() => {
53
+ var _a;
54
+ setSelectedObject(selectedId ? (_a = objectRefs.current[selectedId]) !== null && _a !== void 0 ? _a : null : null);
55
+ }, [selectedId]);
56
+ const onTransformChange = () => {
57
+ if (!selectedId || !onPrefabChange)
58
+ return;
59
+ const obj = objectRefs.current[selectedId];
60
+ if (!obj)
61
+ return;
62
+ const parentWorld = computeParentWorldMatrix(data.root, selectedId);
63
+ const local = parentWorld.clone().invert().multiply(obj.matrixWorld);
64
+ const { position, rotation, scale } = decompose(local);
65
+ const root = updateNode(data.root, selectedId, node => (Object.assign(Object.assign({}, node), { components: Object.assign(Object.assign({}, node.components), { transform: {
66
+ type: "Transform",
67
+ properties: { position, rotation, scale },
68
+ } }) })));
69
+ onPrefabChange(Object.assign(Object.assign({}, data), { root }));
70
+ };
71
+ useEffect(() => {
72
+ const modelsToLoad = new Set();
73
+ const texturesToLoad = new Set();
74
+ walk(data.root, node => {
75
+ var _a, _b, _c, _d, _e, _f;
76
+ ((_c = (_b = (_a = node.components) === null || _a === void 0 ? void 0 : _a.model) === null || _b === void 0 ? void 0 : _b.properties) === null || _c === void 0 ? void 0 : _c.filename) &&
77
+ modelsToLoad.add(node.components.model.properties.filename);
78
+ ((_f = (_e = (_d = node.components) === null || _d === void 0 ? void 0 : _d.material) === null || _e === void 0 ? void 0 : _e.properties) === null || _f === void 0 ? void 0 : _f.texture) &&
79
+ texturesToLoad.add(node.components.material.properties.texture);
80
+ });
81
+ modelsToLoad.forEach((file) => __awaiter(void 0, void 0, void 0, function* () {
82
+ if (models[file] || loading.current.has(file))
83
+ return;
84
+ loading.current.add(file);
85
+ const path = file.startsWith("/")
86
+ ? `${basePath}${file}`
87
+ : `${basePath}/${file}`;
88
+ const res = yield loadModel(path);
89
+ res.success && res.model &&
90
+ setModels(m => (Object.assign(Object.assign({}, m), { [file]: res.model })));
91
+ }));
92
+ const loader = new TextureLoader();
93
+ texturesToLoad.forEach(file => {
94
+ if (textures[file] || loading.current.has(file))
95
+ return;
96
+ loading.current.add(file);
97
+ const path = file.startsWith("/")
98
+ ? `${basePath}${file}`
99
+ : `${basePath}/${file}`;
100
+ loader.load(path, tex => {
101
+ tex.colorSpace = SRGBColorSpace;
102
+ setTextures(t => (Object.assign(Object.assign({}, t), { [file]: tex })));
103
+ });
104
+ });
105
+ }, [data, models, textures]);
106
+ return (_jsxs("group", { ref: rootRef, children: [_jsx(GameInstanceProvider, { models: models, 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, loadedModels: models, loadedTextures: textures, editMode: editMode, parentMatrix: IDENTITY }) }), editMode && (_jsxs(_Fragment, { children: [_jsx(MapControls, { makeDefault: true }), selectedObject && (_jsx(TransformControls, { object: selectedObject, mode: transformMode, space: "local", onObjectChange: onTransformChange, translationSnap: snapResolution > 0 ? snapResolution : undefined, rotationSnap: snapResolution > 0 ? snapResolution : undefined, scaleSnap: snapResolution > 0 ? snapResolution : undefined }, `transform-${snapResolution}`))] }))] }));
107
+ });
108
+ export function GameObjectRenderer(props) {
109
+ var _a, _b, _c;
110
+ const node = props.gameObject;
111
+ if (!node || node.hidden || node.disabled)
112
+ return null;
113
+ const isInstanced = (_c = (_b = (_a = node.components) === null || _a === void 0 ? void 0 : _a.model) === null || _b === void 0 ? void 0 : _b.properties) === null || _c === void 0 ? void 0 : _c.instanced;
114
+ const prevInstancedRef = useRef(undefined);
115
+ const [isTransitioning, setIsTransitioning] = useState(false);
116
+ useEffect(() => {
117
+ if (prevInstancedRef.current !== undefined && prevInstancedRef.current !== isInstanced) {
118
+ setIsTransitioning(true);
119
+ const timer = setTimeout(() => setIsTransitioning(false), 100);
120
+ return () => clearTimeout(timer);
121
+ }
122
+ prevInstancedRef.current = isInstanced;
123
+ }, [isInstanced]);
124
+ if (isTransitioning)
125
+ return null;
126
+ const key = `${node.id}_${isInstanced ? 'instanced' : 'standard'}`;
127
+ return isInstanced
128
+ ? _jsx(InstancedNode, Object.assign({}, props), key)
129
+ : _jsx(StandardNode, Object.assign({}, props), key);
130
+ }
131
+ function isPhysicsProps(v) {
132
+ return (v === null || v === void 0 ? void 0 : v.type) === "fixed" || (v === null || v === void 0 ? void 0 : v.type) === "dynamic";
133
+ }
134
+ function InstancedNode({ gameObject, parentMatrix = IDENTITY, editMode, registerRef, selectedId: _selectedId, onSelect, onClick }) {
135
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
136
+ const world = parentMatrix.clone().multiply(compose(gameObject));
137
+ const { position: worldPosition, rotation: worldRotation, scale: worldScale } = decompose(world);
138
+ const localTransform = getNodeTransformProps(gameObject);
139
+ const physicsProps = isPhysicsProps((_b = (_a = gameObject.components) === null || _a === void 0 ? void 0 : _a.physics) === null || _b === void 0 ? void 0 : _b.properties)
140
+ ? (_d = (_c = gameObject.components) === null || _c === void 0 ? void 0 : _c.physics) === null || _d === void 0 ? void 0 : _d.properties
141
+ : undefined;
142
+ const groupRef = useRef(null);
143
+ const clickValid = useRef(false);
144
+ useEffect(() => {
145
+ if (editMode) {
146
+ registerRef(gameObject.id, groupRef.current);
147
+ return () => registerRef(gameObject.id, null);
148
+ }
149
+ }, [gameObject.id, registerRef, editMode]);
150
+ 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;
151
+ if (editMode) {
152
+ 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) => {
153
+ if (clickValid.current) {
154
+ e.stopPropagation();
155
+ onSelect === null || onSelect === void 0 ? void 0 : onSelect(gameObject.id);
156
+ onClick === null || onClick === void 0 ? void 0 : onClick(e, gameObject);
157
+ }
158
+ clickValid.current = false;
159
+ }, 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 })] }));
160
+ }
161
+ 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 }));
162
+ }
163
+ function StandardNode({ gameObject, selectedId, onSelect, onClick, registerRef, loadedModels, loadedTextures, editMode, parentMatrix = IDENTITY, }) {
164
+ var _a, _b, _c, _d, _e, _f;
165
+ const groupRef = useRef(null);
166
+ const helperRef = useRef(null);
167
+ const clickValid = useRef(false);
168
+ const isSelected = selectedId === gameObject.id;
169
+ const stillInstanced = useInstanceCheck(gameObject.id);
170
+ useHelper(editMode && isSelected ? helperRef : null, BoxHelper, "cyan");
171
+ useEffect(() => {
172
+ registerRef(gameObject.id, groupRef.current);
173
+ return () => registerRef(gameObject.id, null);
174
+ }, [gameObject.id, registerRef]);
175
+ const world = parentMatrix.clone().multiply(compose(gameObject));
176
+ const onDown = (e) => {
177
+ e.stopPropagation();
178
+ clickValid.current = true;
179
+ };
180
+ const onUp = (e) => {
181
+ if (clickValid.current) {
182
+ e.stopPropagation();
183
+ onSelect === null || onSelect === void 0 ? void 0 : onSelect(gameObject.id);
184
+ onClick === null || onClick === void 0 ? void 0 : onClick(e, gameObject);
185
+ }
186
+ clickValid.current = false;
187
+ };
188
+ const physics = (_a = gameObject.components) === null || _a === void 0 ? void 0 : _a.physics;
189
+ const ready = !((_b = gameObject.components) === null || _b === void 0 ? void 0 : _b.model) ||
190
+ loadedModels[gameObject.components.model.properties.filename];
191
+ const hasPhysics = physics && ready && !stillInstanced;
192
+ const transform = getNodeTransformProps(gameObject);
193
+ const physicsDef = hasPhysics ? getComponent("Physics") : null;
194
+ const isInstanced = (_e = (_d = (_c = gameObject.components) === null || _c === void 0 ? void 0 : _c.model) === null || _d === void 0 ? void 0 : _d.properties) === null || _e === void 0 ? void 0 : _e.instanced;
195
+ const physicsKey = `physics_${gameObject.id}_${isInstanced ? 'instanced' : 'standard'}`;
196
+ const inner = (_jsxs("group", { onPointerDown: editMode ? onDown : undefined, onPointerMove: editMode ? () => (clickValid.current = false) : undefined, onPointerUp: editMode ? onUp : undefined, children: [renderCoreNode(gameObject, { loadedModels, loadedTextures, editMode, registerRef }, parentMatrix), (_f = gameObject.children) === null || _f === void 0 ? void 0 : _f.map(child => (_jsx(GameObjectRenderer, { gameObject: child, selectedId: selectedId, onSelect: onSelect, onClick: onClick, registerRef: registerRef, loadedModels: loadedModels, loadedTextures: loadedTextures, editMode: editMode, parentMatrix: world }, child.id)))] }));
197
+ if (editMode) {
198
+ 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, children: inner }, physicsKey)) : null] }));
199
+ }
200
+ if (hasPhysics && (physicsDef === null || physicsDef === void 0 ? void 0 : physicsDef.View)) {
201
+ return (_jsx(physicsDef.View, { properties: physics.properties, position: transform.position, rotation: transform.rotation, scale: transform.scale, editMode: editMode, children: inner }, physicsKey));
202
+ }
203
+ return (_jsx("group", { ref: groupRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, onPointerDown: onDown, onPointerMove: () => (clickValid.current = false), onPointerUp: onUp, children: inner }));
204
+ }
205
+ function walk(node, fn) {
206
+ var _a;
207
+ fn(node);
208
+ (_a = node.children) === null || _a === void 0 ? void 0 : _a.forEach(c => walk(c, fn));
209
+ }
210
+ function compose(node) {
211
+ const { position, rotation, scale } = getNodeTransformProps(node);
212
+ return new Matrix4().compose(new Vector3(...position), new Quaternion().setFromEuler(new Euler(...rotation)), new Vector3(...scale));
213
+ }
214
+ function decompose(m) {
215
+ const p = new Vector3(), q = new Quaternion(), s = new Vector3();
216
+ m.decompose(p, q, s);
217
+ const e = new Euler().setFromQuaternion(q);
218
+ return {
219
+ position: [p.x, p.y, p.z],
220
+ rotation: [e.x, e.y, e.z],
221
+ scale: [s.x, s.y, s.z],
222
+ };
223
+ }
224
+ function getNodeTransformProps(node) {
225
+ var _a, _b, _c, _d, _e;
226
+ 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;
227
+ return {
228
+ position: (_c = t === null || t === void 0 ? void 0 : t.position) !== null && _c !== void 0 ? _c : [0, 0, 0],
229
+ rotation: (_d = t === null || t === void 0 ? void 0 : t.rotation) !== null && _d !== void 0 ? _d : [0, 0, 0],
230
+ scale: (_e = t === null || t === void 0 ? void 0 : t.scale) !== null && _e !== void 0 ? _e : [1, 1, 1],
231
+ };
232
+ }
233
+ function computeParentWorldMatrix(root, targetId) {
234
+ let result = null;
235
+ const visit = (node, parent) => {
236
+ var _a;
237
+ if (node.id === targetId) {
238
+ result = parent.clone();
239
+ return;
240
+ }
241
+ const world = parent.clone().multiply(compose(node));
242
+ (_a = node.children) === null || _a === void 0 ? void 0 : _a.forEach(c => !result && visit(c, world));
243
+ };
244
+ visit(root, IDENTITY);
245
+ return result !== null && result !== void 0 ? result : IDENTITY;
246
+ }
247
+ function renderCoreNode(gameObject, ctx, parentMatrix) {
248
+ var _a, _b, _c;
249
+ const geometry = (_a = gameObject.components) === null || _a === void 0 ? void 0 : _a.geometry;
250
+ const material = (_b = gameObject.components) === null || _b === void 0 ? void 0 : _b.material;
251
+ const model = (_c = gameObject.components) === null || _c === void 0 ? void 0 : _c.model;
252
+ const geometryDef = geometry && getComponent("Geometry");
253
+ const materialDef = material && getComponent("Material");
254
+ const modelDef = model && getComponent("Model");
255
+ const contextProps = {
256
+ loadedModels: ctx.loadedModels,
257
+ loadedTextures: ctx.loadedTextures,
258
+ editMode: ctx.editMode,
259
+ parentMatrix,
260
+ registerRef: ctx.registerRef,
261
+ };
262
+ const wrappers = [];
263
+ const leaves = [];
264
+ if (gameObject.components) {
265
+ Object.entries(gameObject.components)
266
+ .filter(([k]) => !["geometry", "material", "model", "transform", "physics"].includes(k))
267
+ .forEach(([key, comp]) => {
268
+ if (!(comp === null || comp === void 0 ? void 0 : comp.type))
269
+ return;
270
+ const def = getComponent(comp.type);
271
+ if (!(def === null || def === void 0 ? void 0 : def.View))
272
+ return;
273
+ if (def.View.toString().includes("children")) {
274
+ wrappers.push({ key, View: def.View, properties: comp.properties });
275
+ }
276
+ else {
277
+ leaves.push(_jsx(def.View, Object.assign({ properties: comp.properties }, contextProps), key));
278
+ }
279
+ });
280
+ }
281
+ let core;
282
+ if (model && (modelDef === null || modelDef === void 0 ? void 0 : modelDef.View)) {
283
+ core = (_jsxs(modelDef.View, Object.assign({ properties: model.properties }, contextProps, { children: [material && (materialDef === null || materialDef === void 0 ? void 0 : materialDef.View) && (_jsx(materialDef.View, Object.assign({ properties: material.properties }, contextProps), "material")), leaves] })));
284
+ }
285
+ else if (geometry && (geometryDef === null || geometryDef === void 0 ? void 0 : geometryDef.View)) {
286
+ core = (_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")), leaves] }));
287
+ }
288
+ else {
289
+ core = _jsx(_Fragment, { children: leaves });
290
+ }
291
+ return wrappers.reduce((acc, { key, View, properties }) => (_jsx(View, Object.assign({ properties: properties }, contextProps, { children: acc }), key)), core);
292
+ }
293
+ export default PrefabRoot;
@@ -0,0 +1,18 @@
1
+ import { FC } from "react";
2
+ import { ComponentData, GameObject } from "../types";
3
+ export interface Component {
4
+ name: string;
5
+ Editor: FC<{
6
+ node?: GameObject;
7
+ component: ComponentData;
8
+ onUpdate: (newComp: any) => void;
9
+ basePath?: string;
10
+ transformMode?: "translate" | "rotate" | "scale";
11
+ setTransformMode?: (m: "translate" | "rotate" | "scale") => void;
12
+ }>;
13
+ defaultProperties: any;
14
+ View?: FC<any>;
15
+ }
16
+ export declare function registerComponent(component: Component): void;
17
+ export declare function getComponent(name: string): Component | undefined;
18
+ export declare function getAllComponents(): Record<string, Component>;
@@ -0,0 +1,13 @@
1
+ const REGISTRY = {};
2
+ export function registerComponent(component) {
3
+ if (REGISTRY[component.name]) {
4
+ throw new Error(`Component with name ${component.name} already registered.`);
5
+ }
6
+ REGISTRY[component.name] = component;
7
+ }
8
+ export function getComponent(name) {
9
+ return REGISTRY[name];
10
+ }
11
+ export function getAllComponents() {
12
+ return Object.assign({}, REGISTRY);
13
+ }
@@ -0,0 +1,3 @@
1
+ import { Component } from "./ComponentRegistry";
2
+ declare const DirectionalLightComponent: Component;
3
+ export default DirectionalLightComponent;
@@ -0,0 +1,78 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useRef, useEffect } from "react";
3
+ import { useFrame } from "@react-three/fiber";
4
+ import { Vector3 } from "three";
5
+ import { Input, Label } from "./Input";
6
+ function DirectionalLightComponentEditor({ component, onUpdate }) {
7
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
8
+ const props = {
9
+ color: (_a = component.properties.color) !== null && _a !== void 0 ? _a : '#ffffff',
10
+ intensity: (_b = component.properties.intensity) !== null && _b !== void 0 ? _b : 1.0,
11
+ castShadow: (_c = component.properties.castShadow) !== null && _c !== void 0 ? _c : true,
12
+ shadowMapSize: (_d = component.properties.shadowMapSize) !== null && _d !== void 0 ? _d : 1024,
13
+ shadowCameraNear: (_e = component.properties.shadowCameraNear) !== null && _e !== void 0 ? _e : 0.1,
14
+ shadowCameraFar: (_f = component.properties.shadowCameraFar) !== null && _f !== void 0 ? _f : 100,
15
+ shadowCameraTop: (_g = component.properties.shadowCameraTop) !== null && _g !== void 0 ? _g : 30,
16
+ shadowCameraBottom: (_h = component.properties.shadowCameraBottom) !== null && _h !== void 0 ? _h : -30,
17
+ shadowCameraLeft: (_j = component.properties.shadowCameraLeft) !== null && _j !== void 0 ? _j : -30,
18
+ shadowCameraRight: (_k = component.properties.shadowCameraRight) !== null && _k !== void 0 ? _k : 30,
19
+ targetOffset: (_l = component.properties.targetOffset) !== null && _l !== void 0 ? _l : [0, -5, 0]
20
+ };
21
+ const textInputStyle = {
22
+ flex: 1,
23
+ backgroundColor: 'rgba(0, 0, 0, 0.4)',
24
+ border: '1px solid rgba(34, 211, 238, 0.3)',
25
+ padding: '2px 4px',
26
+ fontSize: '10px',
27
+ color: 'rgba(165, 243, 252, 1)',
28
+ fontFamily: 'monospace',
29
+ outline: 'none',
30
+ };
31
+ const smallLabel = { display: 'block', fontSize: '8px', color: 'rgba(34, 211, 238, 0.5)', marginBottom: 2 };
32
+ return _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: [_jsxs("div", { children: [_jsx(Label, { children: "Color" }), _jsxs("div", { style: { display: 'flex', gap: 2 }, children: [_jsx("input", { type: "color", style: { height: 20, width: 20, backgroundColor: 'transparent', border: 'none', cursor: 'pointer' }, value: props.color, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { color: e.target.value })) }), _jsx("input", { type: "text", style: textInputStyle, value: props.color, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { color: e.target.value })) })] })] }), _jsxs("div", { children: [_jsx(Label, { children: "Intensity" }), _jsx(Input, { step: "0.1", value: props.intensity, onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { intensity: value })) })] }), _jsxs("div", { children: [_jsx(Label, { children: "Cast Shadow" }), _jsx("input", { type: "checkbox", style: { height: 16, width: 16, backgroundColor: 'rgba(0, 0, 0, 0.4)', border: '1px solid rgba(34, 211, 238, 0.3)', cursor: 'pointer' }, checked: props.castShadow, onChange: e => onUpdate(Object.assign(Object.assign({}, component.properties), { castShadow: e.target.checked })) })] }), _jsxs("div", { children: [_jsx(Label, { children: "Shadow Map Size" }), _jsx(Input, { step: "256", value: props.shadowMapSize, onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { shadowMapSize: value })) })] }), _jsxs("div", { style: { borderTop: '1px solid rgba(34, 211, 238, 0.2)', paddingTop: 8, marginTop: 8 }, children: [_jsx("label", { style: { display: 'block', fontSize: '9px', color: 'rgba(34, 211, 238, 0.6)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 4 }, children: "Shadow Camera" }), _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: props.shadowCameraNear, onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { shadowCameraNear: value })) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Far" }), _jsx(Input, { step: "1", value: props.shadowCameraFar, onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { shadowCameraFar: value })) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Top" }), _jsx(Input, { step: "1", value: props.shadowCameraTop, onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { shadowCameraTop: value })) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Bottom" }), _jsx(Input, { step: "1", value: props.shadowCameraBottom, onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { shadowCameraBottom: value })) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Left" }), _jsx(Input, { step: "1", value: props.shadowCameraLeft, onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { shadowCameraLeft: value })) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Right" }), _jsx(Input, { step: "1", value: props.shadowCameraRight, onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { shadowCameraRight: value })) })] })] })] }), _jsxs("div", { style: { borderTop: '1px solid rgba(34, 211, 238, 0.2)', paddingTop: 8, marginTop: 8 }, children: [_jsx("label", { style: { display: 'block', fontSize: '9px', color: 'rgba(34, 211, 238, 0.6)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 4 }, children: "Target Offset" }), _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: props.targetOffset[0], onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { targetOffset: [value, props.targetOffset[1], props.targetOffset[2]] })) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Y" }), _jsx(Input, { step: "0.5", value: props.targetOffset[1], onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { targetOffset: [props.targetOffset[0], value, props.targetOffset[2]] })) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Z" }), _jsx(Input, { step: "0.5", value: props.targetOffset[2], onChange: value => onUpdate(Object.assign(Object.assign({}, component.properties), { targetOffset: [props.targetOffset[0], props.targetOffset[1], value] })) })] })] })] })] });
33
+ }
34
+ function DirectionalLightView({ properties, editMode }) {
35
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
36
+ const color = (_a = properties.color) !== null && _a !== void 0 ? _a : '#ffffff';
37
+ const intensity = (_b = properties.intensity) !== null && _b !== void 0 ? _b : 1.0;
38
+ const castShadow = (_c = properties.castShadow) !== null && _c !== void 0 ? _c : true;
39
+ const shadowMapSize = (_d = properties.shadowMapSize) !== null && _d !== void 0 ? _d : 1024;
40
+ const shadowCameraNear = (_e = properties.shadowCameraNear) !== null && _e !== void 0 ? _e : 0.1;
41
+ const shadowCameraFar = (_f = properties.shadowCameraFar) !== null && _f !== void 0 ? _f : 100;
42
+ const shadowCameraTop = (_g = properties.shadowCameraTop) !== null && _g !== void 0 ? _g : 30;
43
+ const shadowCameraBottom = (_h = properties.shadowCameraBottom) !== null && _h !== void 0 ? _h : -30;
44
+ const shadowCameraLeft = (_j = properties.shadowCameraLeft) !== null && _j !== void 0 ? _j : -30;
45
+ const shadowCameraRight = (_k = properties.shadowCameraRight) !== null && _k !== void 0 ? _k : 30;
46
+ const targetOffset = (_l = properties.targetOffset) !== null && _l !== void 0 ? _l : [0, -5, 0];
47
+ const directionalLightRef = useRef(null);
48
+ const targetRef = useRef(null);
49
+ // Set up light target reference when both refs are ready
50
+ useEffect(() => {
51
+ if (directionalLightRef.current && targetRef.current) {
52
+ directionalLightRef.current.target = targetRef.current;
53
+ }
54
+ }, []);
55
+ // Update target world position based on light position + offset
56
+ useFrame(() => {
57
+ if (!directionalLightRef.current || !targetRef.current)
58
+ return;
59
+ const lightWorldPos = new Vector3();
60
+ directionalLightRef.current.getWorldPosition(lightWorldPos);
61
+ // Target is positioned relative to light's world position
62
+ targetRef.current.position.set(lightWorldPos.x + targetOffset[0], lightWorldPos.y + targetOffset[1], lightWorldPos.z + targetOffset[2]);
63
+ });
64
+ return (_jsxs(_Fragment, { children: [_jsx("directionalLight", { ref: directionalLightRef, color: color, intensity: intensity, castShadow: castShadow, "shadow-mapSize-width": shadowMapSize, "shadow-mapSize-height": shadowMapSize, "shadow-camera-near": shadowCameraNear, "shadow-camera-far": shadowCameraFar, "shadow-camera-top": shadowCameraTop, "shadow-camera-bottom": shadowCameraBottom, "shadow-camera-left": shadowCameraLeft, "shadow-camera-right": shadowCameraRight, "shadow-bias": -0.001, "shadow-normalBias": 0.02 }), _jsx("object3D", { ref: targetRef }), editMode && (_jsxs(_Fragment, { children: [_jsxs("mesh", { children: [_jsx("sphereGeometry", { args: [0.3, 8, 6] }), _jsx("meshBasicMaterial", { color: color, wireframe: true })] }), _jsxs("mesh", { position: targetOffset, children: [_jsx("sphereGeometry", { args: [0.2, 8, 6] }), _jsx("meshBasicMaterial", { color: color, wireframe: true, opacity: 0.5, transparent: true })] }), _jsxs("line", { children: [_jsx("bufferGeometry", { onUpdate: (geo) => {
65
+ const points = [
66
+ new Vector3(0, 0, 0),
67
+ new Vector3(targetOffset[0], targetOffset[1], targetOffset[2])
68
+ ];
69
+ geo.setFromPoints(points);
70
+ } }), _jsx("lineBasicMaterial", { color: color, opacity: 0.6, transparent: true })] })] }))] }));
71
+ }
72
+ const DirectionalLightComponent = {
73
+ name: 'DirectionalLight',
74
+ Editor: DirectionalLightComponentEditor,
75
+ View: DirectionalLightView,
76
+ defaultProperties: {}
77
+ };
78
+ export default DirectionalLightComponent;
@@ -0,0 +1,3 @@
1
+ import { Component } from "./ComponentRegistry";
2
+ declare const GeometryComponent: Component;
3
+ export default GeometryComponent;
@@ -0,0 +1,66 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Input, Label } from "./Input";
3
+ const GEOMETRY_ARGS = {
4
+ box: {
5
+ labels: ["Width", "Height", "Depth"],
6
+ defaults: [1, 1, 1],
7
+ },
8
+ sphere: {
9
+ labels: ["Radius", "Width Segments", "Height Segments"],
10
+ defaults: [1, 32, 16],
11
+ },
12
+ plane: {
13
+ labels: ["Width", "Height"],
14
+ defaults: [1, 1],
15
+ },
16
+ };
17
+ function GeometryComponentEditor({ component, onUpdate, }) {
18
+ const { geometryType, args = [] } = component.properties;
19
+ const schema = GEOMETRY_ARGS[geometryType];
20
+ const selectStyle = {
21
+ width: '100%',
22
+ backgroundColor: 'rgba(0, 0, 0, 0.4)',
23
+ border: '1px solid rgba(34, 211, 238, 0.3)',
24
+ padding: '2px 4px',
25
+ fontSize: '10px',
26
+ color: 'rgba(165, 243, 252, 1)',
27
+ fontFamily: 'monospace',
28
+ outline: 'none',
29
+ };
30
+ return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: [_jsxs("div", { children: [_jsx(Label, { children: "Type" }), _jsxs("select", { style: selectStyle, value: geometryType, onChange: e => {
31
+ const type = e.target.value;
32
+ onUpdate({ geometryType: type, args: GEOMETRY_ARGS[type].defaults });
33
+ }, children: [_jsx("option", { value: "box", children: "Box" }), _jsx("option", { value: "sphere", children: "Sphere" }), _jsx("option", { value: "plane", children: "Plane" })] })] }), schema.labels.map((label, i) => {
34
+ var _a;
35
+ return (_jsxs("div", { children: [_jsx(Label, { children: label }), _jsx(Input, { value: (_a = args[i]) !== null && _a !== void 0 ? _a : schema.defaults[i], step: "0.1", onChange: value => {
36
+ const next = [...args];
37
+ next[i] = value;
38
+ onUpdate({ args: next });
39
+ } })] }, label));
40
+ })] }));
41
+ }
42
+ // View for Geometry component
43
+ function GeometryComponentView({ properties, children }) {
44
+ const { geometryType, args = [] } = properties;
45
+ // Only return the geometry node, do not wrap in mesh or group
46
+ switch (geometryType) {
47
+ case "box":
48
+ return _jsx("boxGeometry", { args: args });
49
+ case "sphere":
50
+ return _jsx("sphereGeometry", { args: args });
51
+ case "plane":
52
+ return _jsx("planeGeometry", { args: args });
53
+ default:
54
+ return _jsx("boxGeometry", { args: [1, 1, 1] });
55
+ }
56
+ }
57
+ const GeometryComponent = {
58
+ name: 'Geometry',
59
+ Editor: GeometryComponentEditor,
60
+ View: GeometryComponentView,
61
+ defaultProperties: {
62
+ geometryType: 'box',
63
+ args: GEOMETRY_ARGS.box.defaults,
64
+ }
65
+ };
66
+ export default GeometryComponent;
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ interface InputProps {
3
+ value: number;
4
+ onChange: (value: number) => void;
5
+ step?: string | number;
6
+ min?: number;
7
+ max?: number;
8
+ style?: React.CSSProperties;
9
+ }
10
+ export declare function Input({ value, onChange, step, min, max, style }: InputProps): import("react/jsx-runtime").JSX.Element;
11
+ export declare function Label({ children }: {
12
+ children: React.ReactNode;
13
+ }): import("react/jsx-runtime").JSX.Element;
14
+ export declare function Vector3Input({ label, value, onChange, snap }: {
15
+ label: string;
16
+ value: [number, number, number];
17
+ onChange: (v: [number, number, number]) => void;
18
+ snap?: number;
19
+ }): import("react/jsx-runtime").JSX.Element;
20
+ export {};
@@ -0,0 +1,129 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useState } from 'react';
3
+ // Shared styles
4
+ const styles = {
5
+ input: {
6
+ width: '100%',
7
+ backgroundColor: 'rgba(0, 0, 0, 0.4)',
8
+ border: '1px solid rgba(34, 211, 238, 0.3)',
9
+ padding: '2px 4px',
10
+ fontSize: '10px',
11
+ color: 'rgba(165, 243, 252, 1)',
12
+ fontFamily: 'monospace',
13
+ outline: 'none',
14
+ },
15
+ label: {
16
+ display: 'block',
17
+ fontSize: '9px',
18
+ color: 'rgba(34, 211, 238, 0.6)',
19
+ textTransform: 'uppercase',
20
+ letterSpacing: '0.05em',
21
+ marginBottom: 2,
22
+ },
23
+ };
24
+ export function Input({ value, onChange, step, min, max, style }) {
25
+ return (_jsx("input", { type: "number", value: value, onChange: (e) => onChange(parseFloat(e.target.value)), step: step, min: min, max: max, style: Object.assign(Object.assign({}, styles.input), style) }));
26
+ }
27
+ export function Label({ children }) {
28
+ return _jsx("label", { style: styles.label, children: children });
29
+ }
30
+ export function Vector3Input({ label, value, onChange, snap }) {
31
+ const snapValue = (num) => {
32
+ if (!snap)
33
+ return num;
34
+ return Math.round(num / snap) * snap;
35
+ };
36
+ const [draft, setDraft] = useState(() => value.map(v => v.toString()));
37
+ // Sync external changes (gizmo, undo, etc.)
38
+ useEffect(() => {
39
+ setDraft(value.map(v => v.toString()));
40
+ }, [value[0], value[1], value[2]]);
41
+ const dragState = useRef(null);
42
+ const commit = (index) => {
43
+ const num = parseFloat(draft[index]);
44
+ if (Number.isFinite(num)) {
45
+ const next = [...value];
46
+ next[index] = snapValue(num);
47
+ onChange(next);
48
+ }
49
+ };
50
+ const startScrub = (e, index) => {
51
+ e.preventDefault();
52
+ dragState.current = {
53
+ index,
54
+ startX: e.clientX,
55
+ startValue: value[index]
56
+ };
57
+ e.target.setPointerCapture(e.pointerId);
58
+ document.body.style.cursor = "ew-resize";
59
+ };
60
+ const onScrubMove = (e) => {
61
+ if (!dragState.current)
62
+ return;
63
+ const { index, startX, startValue } = dragState.current;
64
+ const dx = e.clientX - startX;
65
+ let speed = 0.02;
66
+ if (e.shiftKey)
67
+ speed *= 0.1; // fine
68
+ if (e.altKey)
69
+ speed *= 5; // coarse
70
+ const rawValue = startValue + dx * speed;
71
+ const nextValue = snapValue(rawValue);
72
+ const next = [...value];
73
+ next[index] = nextValue;
74
+ setDraft(d => {
75
+ const copy = [...d];
76
+ copy[index] = nextValue.toFixed(3);
77
+ return copy;
78
+ });
79
+ onChange(next);
80
+ };
81
+ const endScrub = (e) => {
82
+ if (!dragState.current)
83
+ return;
84
+ dragState.current = null;
85
+ document.body.style.cursor = "";
86
+ e.target.releasePointerCapture(e.pointerId);
87
+ };
88
+ const axes = [
89
+ { key: "x", color: 'rgba(248, 113, 113, 1)', index: 0 },
90
+ { key: "y", color: 'rgba(134, 239, 172, 1)', index: 1 },
91
+ { key: "z", color: 'rgba(96, 165, 250, 1)', index: 2 }
92
+ ];
93
+ return (_jsxs("div", { style: { marginBottom: 8 }, children: [_jsx("label", { style: Object.assign(Object.assign({}, styles.label), { marginBottom: 4 }), children: label }), _jsx("div", { style: { display: 'flex', gap: 4 }, children: axes.map(({ key, color, index }) => (_jsxs("div", { style: {
94
+ flex: 1,
95
+ display: 'flex',
96
+ alignItems: 'center',
97
+ gap: 4,
98
+ backgroundColor: 'rgba(0, 0, 0, 0.3)',
99
+ border: '1px solid rgba(34, 211, 238, 0.2)',
100
+ borderRadius: 4,
101
+ padding: '4px 6px',
102
+ minHeight: 32,
103
+ }, children: [_jsx("span", { style: {
104
+ fontSize: '12px',
105
+ fontWeight: 'bold',
106
+ color,
107
+ width: 12,
108
+ cursor: 'ew-resize',
109
+ userSelect: 'none',
110
+ }, onPointerDown: e => startScrub(e, index), onPointerMove: onScrubMove, onPointerUp: endScrub, children: key.toUpperCase() }), _jsx("input", { style: {
111
+ flex: 1,
112
+ backgroundColor: 'transparent',
113
+ border: 'none',
114
+ fontSize: '12px',
115
+ color: 'rgba(165, 243, 252, 1)',
116
+ fontFamily: 'monospace',
117
+ outline: 'none',
118
+ width: '100%',
119
+ minWidth: 0,
120
+ }, type: "text", value: draft[index], onChange: e => {
121
+ const next = [...draft];
122
+ next[index] = e.target.value;
123
+ setDraft(next);
124
+ }, onBlur: () => commit(index), onKeyDown: e => {
125
+ if (e.key === "Enter") {
126
+ e.target.blur();
127
+ }
128
+ } })] }, key))) })] }));
129
+ }
@@ -0,0 +1,3 @@
1
+ import { Component } from './ComponentRegistry';
2
+ declare const MaterialComponent: Component;
3
+ export default MaterialComponent;