react-three-game 0.0.67 → 0.0.69
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 +109 -304
- package/dist/index.d.ts +15 -8
- package/dist/index.js +11 -8
- package/dist/shared/GameCanvas.d.ts +1 -2
- package/dist/tools/prefabeditor/EditorContext.d.ts +2 -2
- package/dist/tools/prefabeditor/EditorTree.d.ts +6 -6
- package/dist/tools/prefabeditor/EditorTree.js +92 -142
- package/dist/tools/prefabeditor/EditorTreeMenus.d.ts +4 -11
- package/dist/tools/prefabeditor/EditorTreeMenus.js +16 -25
- package/dist/tools/prefabeditor/EditorUI.d.ts +5 -5
- package/dist/tools/prefabeditor/EditorUI.js +14 -11
- package/dist/tools/prefabeditor/GameEvents.d.ts +0 -30
- package/dist/tools/prefabeditor/GameEvents.js +0 -7
- package/dist/tools/prefabeditor/PrefabEditor.d.ts +12 -13
- package/dist/tools/prefabeditor/PrefabEditor.js +168 -138
- package/dist/tools/prefabeditor/PrefabRoot.d.ts +8 -5
- package/dist/tools/prefabeditor/PrefabRoot.js +141 -123
- package/dist/tools/prefabeditor/components/AmbientLightComponent.js +3 -3
- package/dist/tools/prefabeditor/components/CameraComponent.js +2 -2
- package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +2 -2
- package/dist/tools/prefabeditor/components/ModelComponent.js +0 -1
- package/dist/tools/prefabeditor/components/SpotLightComponent.js +2 -2
- package/dist/tools/prefabeditor/components/TextComponent.js +2 -3
- package/dist/tools/prefabeditor/components/TransformComponent.js +9 -14
- package/dist/tools/prefabeditor/prefabStore.d.ts +42 -0
- package/dist/tools/prefabeditor/prefabStore.js +347 -0
- package/dist/tools/prefabeditor/sceneApi.d.ts +44 -0
- package/dist/tools/prefabeditor/sceneApi.js +161 -0
- package/dist/tools/prefabeditor/styles.d.ts +2 -1
- package/dist/tools/prefabeditor/styles.js +2 -12
- package/dist/tools/prefabeditor/utils.d.ts +15 -36
- package/dist/tools/prefabeditor/utils.js +36 -162
- package/package.json +4 -3
- package/dist/tools/prefabeditor/EventSystem.d.ts +0 -7
- package/dist/tools/prefabeditor/EventSystem.js +0 -23
|
@@ -2,57 +2,36 @@ import { GameObject, Prefab } from "./types";
|
|
|
2
2
|
import { Matrix4, Object3D, Vector3 } from 'three';
|
|
3
3
|
export interface ExportGLBOptions {
|
|
4
4
|
filename?: string;
|
|
5
|
-
binary?: boolean;
|
|
6
|
-
onComplete?: (result: ArrayBuffer | object) => void;
|
|
7
|
-
onError?: (error: any) => void;
|
|
8
5
|
}
|
|
9
|
-
/** Save
|
|
6
|
+
/** Save scene JSON, showing a Save As dialog when supported */
|
|
10
7
|
export declare function saveJson(data: Prefab, filename: string): Promise<void>;
|
|
11
|
-
/** Load
|
|
8
|
+
/** Load scene JSON from a file */
|
|
12
9
|
export declare function loadJson(): Promise<Prefab | undefined>;
|
|
13
10
|
/**
|
|
14
|
-
* Export a Three.js scene or object to GLB
|
|
15
|
-
* @param sceneRoot - The Three.js Object3D to export
|
|
16
|
-
* @param options - Export options
|
|
17
|
-
* @returns Promise that resolves when export is complete
|
|
11
|
+
* Export a Three.js scene or object to GLB binary data
|
|
18
12
|
*/
|
|
19
|
-
export declare function
|
|
13
|
+
export declare function exportGLBData(sceneRoot: Object3D): Promise<ArrayBuffer>;
|
|
20
14
|
/**
|
|
21
|
-
* Export a Three.js scene to GLB and
|
|
22
|
-
* @param sceneRoot - The Three.js Object3D to export
|
|
23
|
-
* @returns Promise that resolves with the GLB data as ArrayBuffer
|
|
15
|
+
* Export a Three.js scene or object to GLB and trigger a download
|
|
24
16
|
*/
|
|
25
|
-
export declare function
|
|
17
|
+
export declare function exportGLB(sceneRoot: Object3D, options?: ExportGLBOptions): Promise<ArrayBuffer>;
|
|
26
18
|
export declare function focusCameraOnObject(object: Object3D, camera: Object3D, target: Vector3, update?: () => void): void;
|
|
27
19
|
export declare function decompose(m: Matrix4): {
|
|
28
20
|
position: [number, number, number];
|
|
29
21
|
rotation: [number, number, number];
|
|
30
22
|
scale: [number, number, number];
|
|
31
23
|
};
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
/** Get a flattened list of all nodes */
|
|
42
|
-
export declare function flatten(root: GameObject): GameObject[];
|
|
43
|
-
/** Immutably update a node by ID */
|
|
44
|
-
export declare function updateNode(root: GameObject, id: string, update: (node: GameObject) => GameObject): GameObject;
|
|
45
|
-
/** Immutably insert a node under a parent ID, defaulting to the root when the parent is missing */
|
|
46
|
-
export declare function insertNode(root: GameObject, node: GameObject, parentId?: string): GameObject;
|
|
47
|
-
/** Immutably delete a node by ID */
|
|
48
|
-
export declare function deleteNode(root: GameObject, id: string): GameObject | null;
|
|
49
|
-
/** Deep clone a node with new IDs */
|
|
50
|
-
export declare function cloneNode(node: GameObject): GameObject;
|
|
24
|
+
/** Compute the parent world matrix for a node using the normalized store data */
|
|
25
|
+
export declare function computeParentWorldMatrix(state: {
|
|
26
|
+
nodesById: Record<string, {
|
|
27
|
+
components?: Record<string, {
|
|
28
|
+
properties?: Record<string, any>;
|
|
29
|
+
} | undefined>;
|
|
30
|
+
}>;
|
|
31
|
+
parentIdById: Record<string, string | null>;
|
|
32
|
+
}, targetId: string): Matrix4;
|
|
51
33
|
/** Recursively update all IDs in a node tree */
|
|
52
34
|
export declare function regenerateIds(node: GameObject): GameObject;
|
|
53
|
-
/** Get component data from a node */
|
|
54
|
-
export declare function getComponent<T = any>(node: GameObject, type: string): T | undefined;
|
|
55
|
-
export declare function updateNodeById(root: GameObject, id: string, updater: (node: GameObject) => GameObject): GameObject;
|
|
56
35
|
/** Create a GameObject node for a 3D model file */
|
|
57
36
|
export declare function createModelNode(filename: string, name?: string): GameObject;
|
|
58
37
|
/** Create a GameObject node for an image as a textured plane */
|
|
@@ -9,14 +9,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import { GLTFExporter } from 'three/examples/jsm/exporters/GLTFExporter.js';
|
|
11
11
|
import { Box3, Euler, Matrix4, PerspectiveCamera, Quaternion, Vector3 } from 'three';
|
|
12
|
-
/** Save
|
|
12
|
+
/** Save scene JSON, showing a Save As dialog when supported */
|
|
13
13
|
export function saveJson(data, filename) {
|
|
14
14
|
return __awaiter(this, void 0, void 0, function* () {
|
|
15
15
|
const json = JSON.stringify(data, null, 2);
|
|
16
16
|
if ('showSaveFilePicker' in window) {
|
|
17
17
|
try {
|
|
18
18
|
const handle = yield window.showSaveFilePicker({
|
|
19
|
-
suggestedName: `${filename || '
|
|
19
|
+
suggestedName: `${filename || 'scene'}.json`,
|
|
20
20
|
types: [{ description: 'JSON', accept: { 'application/json': ['.json'] } }],
|
|
21
21
|
});
|
|
22
22
|
const writable = yield handle.createWritable();
|
|
@@ -32,11 +32,11 @@ export function saveJson(data, filename) {
|
|
|
32
32
|
// Fallback for browsers without File System Access API
|
|
33
33
|
const a = document.createElement('a');
|
|
34
34
|
a.href = "data:text/json;charset=utf-8," + encodeURIComponent(json);
|
|
35
|
-
a.download = `${filename || '
|
|
35
|
+
a.download = `${filename || 'scene'}.json`;
|
|
36
36
|
a.click();
|
|
37
37
|
});
|
|
38
38
|
}
|
|
39
|
-
/** Load
|
|
39
|
+
/** Load scene JSON from a file */
|
|
40
40
|
export function loadJson() {
|
|
41
41
|
return new Promise(resolve => {
|
|
42
42
|
const input = document.createElement('input');
|
|
@@ -56,7 +56,7 @@ export function loadJson() {
|
|
|
56
56
|
resolve(JSON.parse(text));
|
|
57
57
|
}
|
|
58
58
|
catch (err) {
|
|
59
|
-
console.error('Error parsing
|
|
59
|
+
console.error('Error parsing scene JSON:', err);
|
|
60
60
|
resolve(undefined);
|
|
61
61
|
}
|
|
62
62
|
};
|
|
@@ -66,44 +66,30 @@ export function loadJson() {
|
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
68
|
/**
|
|
69
|
-
* Export a Three.js scene or object to GLB
|
|
70
|
-
* @param sceneRoot - The Three.js Object3D to export
|
|
71
|
-
* @param options - Export options
|
|
72
|
-
* @returns Promise that resolves when export is complete
|
|
69
|
+
* Export a Three.js scene or object to GLB binary data
|
|
73
70
|
*/
|
|
74
|
-
export function
|
|
75
|
-
const { filename = 'scene.glb', binary = true, onComplete, onError } = options;
|
|
71
|
+
export function exportGLBData(sceneRoot) {
|
|
76
72
|
return new Promise((resolve, reject) => {
|
|
77
|
-
|
|
78
|
-
exporter.parse(sceneRoot, (result) => {
|
|
79
|
-
onComplete === null || onComplete === void 0 ? void 0 : onComplete(result);
|
|
80
|
-
resolve(result);
|
|
81
|
-
// Trigger download if filename is provided
|
|
82
|
-
if (filename) {
|
|
83
|
-
const blob = new Blob([result], { type: binary ? 'application/octet-stream' : 'application/json' });
|
|
84
|
-
const url = URL.createObjectURL(blob);
|
|
85
|
-
const a = document.createElement('a');
|
|
86
|
-
a.href = url;
|
|
87
|
-
a.download = filename;
|
|
88
|
-
a.click();
|
|
89
|
-
URL.revokeObjectURL(url);
|
|
90
|
-
}
|
|
91
|
-
}, (error) => {
|
|
92
|
-
console.error('Error exporting GLB:', error);
|
|
93
|
-
onError === null || onError === void 0 ? void 0 : onError(error);
|
|
94
|
-
reject(error);
|
|
95
|
-
}, { binary });
|
|
73
|
+
new GLTFExporter().parse(sceneRoot, (result) => resolve(result), (error) => reject(error), { binary: true });
|
|
96
74
|
});
|
|
97
75
|
}
|
|
98
76
|
/**
|
|
99
|
-
* Export a Three.js scene to GLB and
|
|
100
|
-
* @param sceneRoot - The Three.js Object3D to export
|
|
101
|
-
* @returns Promise that resolves with the GLB data as ArrayBuffer
|
|
77
|
+
* Export a Three.js scene or object to GLB and trigger a download
|
|
102
78
|
*/
|
|
103
|
-
export function
|
|
104
|
-
return __awaiter(this,
|
|
105
|
-
const
|
|
106
|
-
|
|
79
|
+
export function exportGLB(sceneRoot_1) {
|
|
80
|
+
return __awaiter(this, arguments, void 0, function* (sceneRoot, options = {}) {
|
|
81
|
+
const { filename = 'scene.glb' } = options;
|
|
82
|
+
const data = yield exportGLBData(sceneRoot);
|
|
83
|
+
if (filename) {
|
|
84
|
+
const blob = new Blob([data], { type: 'application/octet-stream' });
|
|
85
|
+
const url = URL.createObjectURL(blob);
|
|
86
|
+
const a = document.createElement('a');
|
|
87
|
+
a.href = url;
|
|
88
|
+
a.download = filename;
|
|
89
|
+
a.click();
|
|
90
|
+
URL.revokeObjectURL(url);
|
|
91
|
+
}
|
|
92
|
+
return data;
|
|
107
93
|
});
|
|
108
94
|
}
|
|
109
95
|
export function focusCameraOnObject(object, camera, target, update) {
|
|
@@ -143,139 +129,27 @@ export function decompose(m) {
|
|
|
143
129
|
scale: [s.x, s.y, s.z],
|
|
144
130
|
};
|
|
145
131
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const identity = new Matrix4();
|
|
156
|
-
let result = null;
|
|
157
|
-
const visit = (node, parent) => {
|
|
158
|
-
var _a;
|
|
159
|
-
if (node.id === targetId) {
|
|
160
|
-
result = parent.clone();
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
const world = parent.clone().multiply(compose(node));
|
|
164
|
-
(_a = node.children) === null || _a === void 0 ? void 0 : _a.forEach(child => !result && visit(child, world));
|
|
165
|
-
};
|
|
166
|
-
visit(root, identity);
|
|
167
|
-
return result !== null && result !== void 0 ? result : identity;
|
|
168
|
-
}
|
|
169
|
-
/** Find a node by ID in the tree */
|
|
170
|
-
export function findNode(root, id) {
|
|
171
|
-
var _a;
|
|
172
|
-
if (root.id === id)
|
|
173
|
-
return root;
|
|
174
|
-
for (const child of (_a = root.children) !== null && _a !== void 0 ? _a : []) {
|
|
175
|
-
const found = findNode(child, id);
|
|
176
|
-
if (found)
|
|
177
|
-
return found;
|
|
178
|
-
}
|
|
179
|
-
return null;
|
|
180
|
-
}
|
|
181
|
-
/** Find the parent of a node by ID */
|
|
182
|
-
export function findParent(root, id) {
|
|
183
|
-
var _a;
|
|
184
|
-
for (const child of (_a = root.children) !== null && _a !== void 0 ? _a : []) {
|
|
185
|
-
if (child.id === id)
|
|
186
|
-
return root;
|
|
187
|
-
const found = findParent(child, id);
|
|
188
|
-
if (found)
|
|
189
|
-
return found;
|
|
190
|
-
}
|
|
191
|
-
return null;
|
|
192
|
-
}
|
|
193
|
-
/** Find all nodes matching a predicate */
|
|
194
|
-
export function findAll(root, predicate) {
|
|
195
|
-
var _a;
|
|
196
|
-
const results = [];
|
|
197
|
-
if (predicate(root))
|
|
198
|
-
results.push(root);
|
|
199
|
-
for (const child of (_a = root.children) !== null && _a !== void 0 ? _a : []) {
|
|
200
|
-
results.push(...findAll(child, predicate));
|
|
201
|
-
}
|
|
202
|
-
return results;
|
|
203
|
-
}
|
|
204
|
-
/** Find all nodes that have a specific component type */
|
|
205
|
-
export function findByComponent(root, componentType) {
|
|
206
|
-
return findAll(root, node => { var _a; return Object.values((_a = node.components) !== null && _a !== void 0 ? _a : {}).some(c => (c === null || c === void 0 ? void 0 : c.type) === componentType); });
|
|
207
|
-
}
|
|
208
|
-
/** Get a flattened list of all nodes */
|
|
209
|
-
export function flatten(root) {
|
|
210
|
-
return findAll(root, () => true);
|
|
211
|
-
}
|
|
212
|
-
/** Immutably update a node by ID */
|
|
213
|
-
export function updateNode(root, id, update) {
|
|
214
|
-
if (root.id === id)
|
|
215
|
-
return update(root);
|
|
216
|
-
if (!root.children)
|
|
217
|
-
return root;
|
|
218
|
-
return Object.assign(Object.assign({}, root), { children: root.children.map(child => updateNode(child, id, update)) });
|
|
219
|
-
}
|
|
220
|
-
/** Immutably insert a node under a parent ID, defaulting to the root when the parent is missing */
|
|
221
|
-
export function insertNode(root, node, parentId) {
|
|
222
|
-
var _a, _b;
|
|
223
|
-
if (!parentId || parentId === root.id) {
|
|
224
|
-
return Object.assign(Object.assign({}, root), { children: [...((_a = root.children) !== null && _a !== void 0 ? _a : []), node] });
|
|
132
|
+
/** Compute the parent world matrix for a node using the normalized store data */
|
|
133
|
+
export function computeParentWorldMatrix(state, targetId) {
|
|
134
|
+
var _a, _b, _c, _d, _e, _f;
|
|
135
|
+
const parentWorld = new Matrix4();
|
|
136
|
+
const chain = [];
|
|
137
|
+
let currentId = state.parentIdById[targetId];
|
|
138
|
+
while (currentId) {
|
|
139
|
+
chain.unshift(currentId);
|
|
140
|
+
currentId = state.parentIdById[currentId];
|
|
225
141
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
});
|
|
230
|
-
if (nextRoot === root) {
|
|
231
|
-
return Object.assign(Object.assign({}, root), { children: [...((_b = root.children) !== null && _b !== void 0 ? _b : []), node] });
|
|
142
|
+
for (const nodeId of chain) {
|
|
143
|
+
const transform = (_c = (_b = (_a = state.nodesById[nodeId]) === null || _a === void 0 ? void 0 : _a.components) === null || _b === void 0 ? void 0 : _b.transform) === null || _c === void 0 ? void 0 : _c.properties;
|
|
144
|
+
parentWorld.multiply(new Matrix4().compose(new Vector3(...((_d = transform === null || transform === void 0 ? void 0 : transform.position) !== null && _d !== void 0 ? _d : [0, 0, 0])), new Quaternion().setFromEuler(new Euler(...((_e = transform === null || transform === void 0 ? void 0 : transform.rotation) !== null && _e !== void 0 ? _e : [0, 0, 0]))), new Vector3(...((_f = transform === null || transform === void 0 ? void 0 : transform.scale) !== null && _f !== void 0 ? _f : [1, 1, 1]))));
|
|
232
145
|
}
|
|
233
|
-
return
|
|
234
|
-
}
|
|
235
|
-
/** Immutably delete a node by ID */
|
|
236
|
-
export function deleteNode(root, id) {
|
|
237
|
-
if (root.id === id)
|
|
238
|
-
return null;
|
|
239
|
-
if (!root.children)
|
|
240
|
-
return root;
|
|
241
|
-
return Object.assign(Object.assign({}, root), { children: root.children
|
|
242
|
-
.map(child => deleteNode(child, id))
|
|
243
|
-
.filter((child) => child !== null) });
|
|
244
|
-
}
|
|
245
|
-
/** Deep clone a node with new IDs */
|
|
246
|
-
export function cloneNode(node) {
|
|
247
|
-
var _a, _b;
|
|
248
|
-
return Object.assign(Object.assign({}, node), { id: crypto.randomUUID(), name: `${(_a = node.name) !== null && _a !== void 0 ? _a : node.id} Copy`, children: (_b = node.children) === null || _b === void 0 ? void 0 : _b.map(cloneNode) });
|
|
146
|
+
return parentWorld;
|
|
249
147
|
}
|
|
250
148
|
/** Recursively update all IDs in a node tree */
|
|
251
149
|
export function regenerateIds(node) {
|
|
252
150
|
var _a;
|
|
253
151
|
return Object.assign(Object.assign({}, node), { id: crypto.randomUUID(), children: (_a = node.children) === null || _a === void 0 ? void 0 : _a.map(regenerateIds) });
|
|
254
152
|
}
|
|
255
|
-
/** Get component data from a node */
|
|
256
|
-
export function getComponent(node, type) {
|
|
257
|
-
var _a;
|
|
258
|
-
const comp = Object.values((_a = node.components) !== null && _a !== void 0 ? _a : {}).find(c => (c === null || c === void 0 ? void 0 : c.type) === type);
|
|
259
|
-
return comp === null || comp === void 0 ? void 0 : comp.properties;
|
|
260
|
-
}
|
|
261
|
-
export function updateNodeById(root, id, updater) {
|
|
262
|
-
if (root.id === id) {
|
|
263
|
-
return updater(root);
|
|
264
|
-
}
|
|
265
|
-
if (!root.children) {
|
|
266
|
-
return root;
|
|
267
|
-
}
|
|
268
|
-
let didChange = false;
|
|
269
|
-
const newChildren = root.children.map(child => {
|
|
270
|
-
const updated = updateNodeById(child, id, updater);
|
|
271
|
-
if (updated !== child)
|
|
272
|
-
didChange = true;
|
|
273
|
-
return updated;
|
|
274
|
-
});
|
|
275
|
-
if (!didChange)
|
|
276
|
-
return root;
|
|
277
|
-
return Object.assign(Object.assign({}, root), { children: newChildren });
|
|
278
|
-
}
|
|
279
153
|
/** Create a GameObject node for a 3D model file */
|
|
280
154
|
export function createModelNode(filename, name) {
|
|
281
155
|
return {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-three-game",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "high performance 3D game engine
|
|
3
|
+
"version": "0.0.69",
|
|
4
|
+
"description": "high performance 3D game engine built in React",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"vite": "^7.3.1"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"react-error-boundary": "^6.1.0"
|
|
51
|
+
"react-error-boundary": "^6.1.0",
|
|
52
|
+
"zustand": "^5.0.12"
|
|
52
53
|
}
|
|
53
54
|
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { useRef, useImperativeHandle, forwardRef, useCallback } from 'react';
|
|
2
|
-
const EventSystemHook = forwardRef(({ entityId }, ref) => {
|
|
3
|
-
const targetRef = useRef(typeof window !== 'undefined' ? window : null);
|
|
4
|
-
// Fire a global JS event with entityId as source
|
|
5
|
-
const fire = useCallback((eventType, data) => {
|
|
6
|
-
if (!targetRef.current)
|
|
7
|
-
return;
|
|
8
|
-
const event = new CustomEvent(eventType, {
|
|
9
|
-
detail: {
|
|
10
|
-
entityId,
|
|
11
|
-
data,
|
|
12
|
-
},
|
|
13
|
-
});
|
|
14
|
-
targetRef.current.dispatchEvent(event);
|
|
15
|
-
}, [entityId]);
|
|
16
|
-
// Expose ref API
|
|
17
|
-
useImperativeHandle(ref, () => ({
|
|
18
|
-
fire,
|
|
19
|
-
}), [fire]);
|
|
20
|
-
return null;
|
|
21
|
-
});
|
|
22
|
-
EventSystemHook.displayName = 'EventSystemHook';
|
|
23
|
-
export default EventSystemHook;
|