react-three-game 0.0.106 → 0.0.108
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 +7 -0
- package/dist/plugins/crashcat/CrashcatPhysicsComponent.js +74 -46
- package/dist/plugins/crashcat/CrashcatRagdoll.d.ts +58 -0
- package/dist/plugins/crashcat/CrashcatRagdoll.js +410 -0
- package/dist/plugins/crashcat/CrashcatRuntime.js +23 -32
- package/dist/plugins/crashcat/index.d.ts +1 -0
- package/dist/plugins/crashcat/index.js +1 -0
- package/dist/tools/assetviewer/page.js +4 -4
- package/dist/tools/prefabeditor/EditorTree.js +5 -2
- package/dist/tools/prefabeditor/EditorTreeMenus.d.ts +2 -1
- package/dist/tools/prefabeditor/EditorTreeMenus.js +17 -5
- package/dist/tools/prefabeditor/GameEvents.d.ts +1 -0
- package/dist/tools/prefabeditor/PrefabEditor.d.ts +2 -1
- package/dist/tools/prefabeditor/PrefabEditor.js +26 -13
- package/dist/tools/prefabeditor/PrefabRoot.d.ts +3 -1
- package/dist/tools/prefabeditor/PrefabRoot.js +61 -25
- package/dist/tools/prefabeditor/components/ComponentRegistry.d.ts +15 -0
- package/dist/tools/prefabeditor/components/PrefabRefComponent.d.ts +3 -0
- package/dist/tools/prefabeditor/components/PrefabRefComponent.js +72 -0
- package/dist/tools/prefabeditor/components/TextComponent.js +8 -5
- package/dist/tools/prefabeditor/components/index.js +2 -0
- package/dist/tools/prefabeditor/modelPrefab.d.ts +3 -0
- package/dist/tools/prefabeditor/modelPrefab.js +44 -11
- package/dist/tools/prefabeditor/prefab.d.ts +5 -4
- package/dist/tools/prefabeditor/prefab.js +47 -29
- package/dist/tools/prefabeditor/utils.d.ts +8 -1
- package/dist/tools/prefabeditor/utils.js +74 -22
- package/package.json +13 -13
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { FC } from "react";
|
|
2
|
+
import type { ThreeEvent } from "@react-three/fiber";
|
|
2
3
|
import type { ComponentData, GameObject } from "../types";
|
|
3
4
|
export type AssetRef = {
|
|
4
5
|
type: "model" | "texture" | "sound";
|
|
@@ -12,13 +13,27 @@ export interface ComponentViewProps<P = Record<string, unknown>> {
|
|
|
12
13
|
properties: P;
|
|
13
14
|
/** Children to render for components that wrap the current subtree. */
|
|
14
15
|
children?: React.ReactNode;
|
|
16
|
+
/** Whether this node is currently rendered in editor mode. */
|
|
17
|
+
editMode?: boolean;
|
|
18
|
+
/** Node-level pointer/click handlers for custom components that render their own pickable objects. */
|
|
19
|
+
nodeInteractionHandlers?: NodeInteractionHandlers;
|
|
15
20
|
/** Current node local position for wrapper components. */
|
|
16
21
|
position?: [number, number, number];
|
|
17
22
|
/** Current node local rotation in radians for wrapper components. */
|
|
18
23
|
rotation?: [number, number, number];
|
|
19
24
|
/** Current node local scale for wrapper components. */
|
|
20
25
|
scale?: [number, number, number];
|
|
26
|
+
/** Current node world position. Components that create world-space resources should prefer this. */
|
|
27
|
+
worldPosition?: [number, number, number];
|
|
28
|
+
/** Public asset URL prefix, such as a Next.js basePath. */
|
|
29
|
+
basePath?: string;
|
|
21
30
|
}
|
|
31
|
+
export type NodeInteractionHandlers = {
|
|
32
|
+
onClick?: (event: ThreeEvent<PointerEvent>) => void;
|
|
33
|
+
onPointerDown?: (event: ThreeEvent<PointerEvent>) => void;
|
|
34
|
+
onPointerMove?: (event: ThreeEvent<PointerEvent>) => void;
|
|
35
|
+
onPointerUp?: (event: ThreeEvent<PointerEvent>) => void;
|
|
36
|
+
};
|
|
22
37
|
export interface Component {
|
|
23
38
|
name: string;
|
|
24
39
|
/** Set when this component occupies a single slot on a node. Use a string to share a slot across component types. */
|
|
@@ -0,0 +1,72 @@
|
|
|
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 { useEffect, useState } from 'react';
|
|
12
|
+
import PrefabRoot from '../PrefabRoot';
|
|
13
|
+
import { useEditorRef } from '../PrefabEditor';
|
|
14
|
+
import { withBasePath } from '../utils';
|
|
15
|
+
import { base, colors } from '../styles';
|
|
16
|
+
import { FieldGroup, Label } from './Input';
|
|
17
|
+
function PrefabRefView({ properties, children, basePath = '' }) {
|
|
18
|
+
var _a;
|
|
19
|
+
const [loadedPrefab, setLoadedPrefab] = useState(null);
|
|
20
|
+
const url = (_a = properties.url) !== null && _a !== void 0 ? _a : '';
|
|
21
|
+
const resolvedUrl = url ? withBasePath(basePath, url) : '';
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (!resolvedUrl)
|
|
24
|
+
return;
|
|
25
|
+
let cancelled = false;
|
|
26
|
+
fetch(resolvedUrl)
|
|
27
|
+
.then(r => r.json())
|
|
28
|
+
.then(data => { if (!cancelled)
|
|
29
|
+
setLoadedPrefab(data); })
|
|
30
|
+
.catch(err => console.warn('[PrefabRef] Failed to load:', resolvedUrl, err));
|
|
31
|
+
return () => { cancelled = true; };
|
|
32
|
+
}, [resolvedUrl]);
|
|
33
|
+
return (_jsxs(_Fragment, { children: [loadedPrefab && _jsx(PrefabRoot, { data: loadedPrefab, editMode: false, basePath: basePath }), children] }));
|
|
34
|
+
}
|
|
35
|
+
function PrefabRefEditor({ node, component, onUpdate, basePath = '', }) {
|
|
36
|
+
var _a, _b;
|
|
37
|
+
const url = (_b = (_a = component.properties) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '';
|
|
38
|
+
const [manifest, setManifest] = useState([]);
|
|
39
|
+
const [unpacking, setUnpacking] = useState(false);
|
|
40
|
+
const editor = useEditorRef();
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
fetch(withBasePath(basePath, '/prefabs/manifest.json'))
|
|
43
|
+
.then(r => r.json())
|
|
44
|
+
.then(data => setManifest(data))
|
|
45
|
+
.catch(() => setManifest([]));
|
|
46
|
+
}, []);
|
|
47
|
+
const handleUnpack = () => __awaiter(this, void 0, void 0, function* () {
|
|
48
|
+
if (!node || !url)
|
|
49
|
+
return;
|
|
50
|
+
setUnpacking(true);
|
|
51
|
+
try {
|
|
52
|
+
const prefab = yield fetch(withBasePath(basePath, url)).then(r => r.json());
|
|
53
|
+
editor.replaceNode(node.id, prefab.root);
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
console.error('[PrefabRef] Unpack failed:', err);
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
setUnpacking(false);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
return (_jsxs(FieldGroup, { children: [_jsxs("div", { children: [_jsx(Label, { children: "Prefab URL" }), _jsx("input", { type: "text", style: Object.assign(Object.assign({}, base.input), { width: '100%', boxSizing: 'border-box', fontFamily: 'monospace' }), value: url, onChange: e => onUpdate({ url: e.target.value }), placeholder: "/prefabs/my-prefab.json" }), manifest.length > 0 && (_jsxs("select", { style: Object.assign(Object.assign({}, base.input), { width: '100%', marginTop: 4, background: colors.bgInput, boxSizing: 'border-box' }), value: url, onChange: e => onUpdate({ url: e.target.value }), children: [_jsx("option", { value: "", children: "\u2014 pick from manifest \u2014" }), manifest.map(entry => (_jsx("option", { value: entry, children: entry.replace(/^.*\//, '') }, entry)))] }))] }), _jsx("button", { type: "button", style: Object.assign(Object.assign({}, base.btn), { width: '100%', opacity: unpacking || !url ? 0.5 : 1 }), disabled: unpacking || !url, onClick: handleUnpack, children: unpacking ? 'Unpacking…' : 'Unpack' })] }));
|
|
63
|
+
}
|
|
64
|
+
const PrefabRefComponent = {
|
|
65
|
+
name: 'PrefabRef',
|
|
66
|
+
Editor: PrefabRefEditor,
|
|
67
|
+
View: PrefabRefView,
|
|
68
|
+
defaultProperties: {
|
|
69
|
+
url: '',
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
export default PrefabRefComponent;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { ColorField, FieldGroup, NumberField, SelectField, StringField } from "./Input";
|
|
3
3
|
import { Text } from 'three-text/three/react';
|
|
4
|
-
import { useRef, useState, useCallback } from 'react';
|
|
5
|
-
|
|
6
|
-
Text.setHarfBuzzPath('/fonts/hb.wasm');
|
|
4
|
+
import { useRef, useState, useCallback, useEffect } from 'react';
|
|
5
|
+
import { withBasePath } from "../utils";
|
|
7
6
|
function TextComponentEditor({ component, onUpdate, }) {
|
|
8
7
|
return (_jsxs(FieldGroup, { children: [_jsx(StringField, { name: "text", label: "Text", values: component.properties, onChange: onUpdate, placeholder: "Enter text..." }), _jsx(ColorField, { name: "color", label: "Color", values: component.properties, onChange: onUpdate }), _jsx(StringField, { name: "font", label: "Font", values: component.properties, onChange: onUpdate, placeholder: "/fonts/NotoSans-Regular.ttf" }), _jsx(NumberField, { name: "size", label: "Size", values: component.properties, onChange: onUpdate, min: 0.01, step: 0.1 }), _jsx(NumberField, { name: "depth", label: "Depth", values: component.properties, onChange: onUpdate, min: 0, step: 0.1 }), _jsx(NumberField, { name: "width", label: "Width", values: component.properties, onChange: onUpdate, min: 0, step: 0.5 }), _jsx(SelectField, { name: "align", label: "Align", values: component.properties, onChange: onUpdate, options: [
|
|
9
8
|
{ value: 'left', label: 'Left' },
|
|
@@ -11,11 +10,15 @@ function TextComponentEditor({ component, onUpdate, }) {
|
|
|
11
10
|
{ value: 'right', label: 'Right' },
|
|
12
11
|
] })] }));
|
|
13
12
|
}
|
|
14
|
-
function TextComponentView({ properties, children }) {
|
|
13
|
+
function TextComponentView({ properties, children, basePath = "" }) {
|
|
15
14
|
const { text = '', font, size, depth, width, align, color } = properties;
|
|
16
15
|
const textContent = String(text || '');
|
|
16
|
+
const resolvedFont = font ? withBasePath(basePath, font) : font;
|
|
17
17
|
const meshRef = useRef(null);
|
|
18
18
|
const [offset, setOffset] = useState([0, 0, 0]);
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
Text.setHarfBuzzPath(withBasePath(basePath, '/fonts/hb.wasm'));
|
|
21
|
+
}, [basePath]);
|
|
19
22
|
const handleLoad = useCallback((_geometry, info) => {
|
|
20
23
|
if (info === null || info === void 0 ? void 0 : info.planeBounds) {
|
|
21
24
|
const bounds = info.planeBounds;
|
|
@@ -37,7 +40,7 @@ function TextComponentView({ properties, children }) {
|
|
|
37
40
|
}, [align]);
|
|
38
41
|
if (!textContent)
|
|
39
42
|
return null;
|
|
40
|
-
return (_jsxs("group", { position: offset, children: [_jsx(Text, { ref: meshRef, font:
|
|
43
|
+
return (_jsxs("group", { position: offset, children: [_jsx(Text, { ref: meshRef, font: resolvedFont, size: size, depth: depth, layout: { align, width }, color: color, onLoad: handleLoad, children: textContent }), children] }));
|
|
41
44
|
}
|
|
42
45
|
const TextComponent = {
|
|
43
46
|
name: 'Text',
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// biome-ignore assist/source/organizeImports: <in order of display in the editor>
|
|
2
2
|
import TransformComponent from "./TransformComponent";
|
|
3
|
+
import PrefabRefComponent from "./PrefabRefComponent";
|
|
3
4
|
import GeometryComponent from "./GeometryComponent";
|
|
4
5
|
import BufferGeometryComponent from "./BufferGeometryComponent";
|
|
5
6
|
import ModelComponent from "./ModelComponent";
|
|
@@ -35,4 +36,5 @@ export const builtinComponents = [
|
|
|
35
36
|
CameraComponent,
|
|
36
37
|
SoundComponent,
|
|
37
38
|
DataComponent,
|
|
39
|
+
PrefabRefComponent,
|
|
38
40
|
];
|
|
@@ -6,9 +6,12 @@ export interface DecomposeModelOptions {
|
|
|
6
6
|
idPrefix?: string;
|
|
7
7
|
/** Include invisible Three objects in the generated prefab tree. */
|
|
8
8
|
includeInvisible?: boolean;
|
|
9
|
+
/** Create CrashcatPhysics components from Blender-style mesh name suffixes. */
|
|
10
|
+
inferCollisionMeshes?: boolean;
|
|
9
11
|
/** Return a serializable texture ref for embedded or externally loaded textures. */
|
|
10
12
|
getTexturePath?: (texture: Texture, usage: 'map' | 'normalMap') => string | null | undefined;
|
|
11
13
|
}
|
|
14
|
+
export declare function hasCollisionMeshConventions(object: Object3D, enabled?: boolean): boolean;
|
|
12
15
|
/**
|
|
13
16
|
* Converts a live Three object hierarchy into prefab JSON nodes made from
|
|
14
17
|
* Transform, BufferGeometry, and Material components.
|
|
@@ -52,6 +52,27 @@ function serializeGeometry(geometry) {
|
|
|
52
52
|
computeVertexNormals: normals.length === 0,
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
|
+
function getCollisionMeshConvention(name, enabled) {
|
|
56
|
+
if (!enabled)
|
|
57
|
+
return null;
|
|
58
|
+
const match = name.match(/^(.*)_(colonly|col)(?:\.\d+)?$/i);
|
|
59
|
+
if (!match)
|
|
60
|
+
return null;
|
|
61
|
+
const [, baseName, suffix] = match;
|
|
62
|
+
return {
|
|
63
|
+
displayName: baseName || name,
|
|
64
|
+
renderMesh: suffix.toLowerCase() === 'col',
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export function hasCollisionMeshConventions(object, enabled = true) {
|
|
68
|
+
let hasConvention = false;
|
|
69
|
+
object.traverse(child => {
|
|
70
|
+
if (hasConvention)
|
|
71
|
+
return;
|
|
72
|
+
hasConvention = child instanceof Mesh && getCollisionMeshConvention(child.name, enabled) != null;
|
|
73
|
+
});
|
|
74
|
+
return hasConvention;
|
|
75
|
+
}
|
|
55
76
|
function getSideName(side) {
|
|
56
77
|
if (side === BackSide)
|
|
57
78
|
return 'BackSide';
|
|
@@ -138,17 +159,19 @@ function createTransformComponent(object) {
|
|
|
138
159
|
},
|
|
139
160
|
};
|
|
140
161
|
}
|
|
141
|
-
function createNode(object, idPrefix, children = [], components = {}) {
|
|
162
|
+
function createNode(object, idPrefix, children = [], components = {}, options = {}) {
|
|
163
|
+
var _a, _b;
|
|
142
164
|
return {
|
|
143
165
|
id: createId(idPrefix),
|
|
144
|
-
name: object.name || object.type,
|
|
145
|
-
hidden: object.visible === false ? true : undefined,
|
|
166
|
+
name: ((_a = options.name) !== null && _a !== void 0 ? _a : object.name) || object.type,
|
|
167
|
+
hidden: (_b = options.hidden) !== null && _b !== void 0 ? _b : (object.visible === false ? true : undefined),
|
|
146
168
|
components: Object.assign({ transform: createTransformComponent(object) }, components),
|
|
147
169
|
children,
|
|
148
170
|
};
|
|
149
171
|
}
|
|
150
172
|
function decomposeObject(object, options) {
|
|
151
|
-
|
|
173
|
+
const collisionMesh = getCollisionMeshConvention(object.name, options.inferCollisionMeshes);
|
|
174
|
+
if (!options.includeInvisible && !object.visible && !collisionMesh)
|
|
152
175
|
return null;
|
|
153
176
|
const childNodes = object.children
|
|
154
177
|
.map(child => decomposeObject(child, options))
|
|
@@ -161,20 +184,30 @@ function decomposeObject(object, options) {
|
|
|
161
184
|
result[part.key] = serializeMaterial(part.material, part.attach, options);
|
|
162
185
|
return result;
|
|
163
186
|
}, {});
|
|
164
|
-
return createNode(object, options.idPrefix, childNodes, Object.assign({ geometry: {
|
|
187
|
+
return createNode(object, options.idPrefix, childNodes, Object.assign(Object.assign({ geometry: {
|
|
165
188
|
type: 'BufferGeometry',
|
|
166
|
-
properties: Object.assign(Object.assign({}, serializeGeometry(object.geometry)), { visible: object.visible, castShadow: object.castShadow, receiveShadow: object.receiveShadow }),
|
|
167
|
-
} }, materialComponents)
|
|
189
|
+
properties: Object.assign(Object.assign({}, serializeGeometry(object.geometry)), { visible: collisionMesh ? collisionMesh.renderMesh : object.visible, castShadow: object.castShadow, receiveShadow: object.receiveShadow }),
|
|
190
|
+
} }, materialComponents), (collisionMesh ? {
|
|
191
|
+
crashcatPhysics: {
|
|
192
|
+
type: 'CrashcatPhysics',
|
|
193
|
+
properties: {
|
|
194
|
+
type: 'fixed',
|
|
195
|
+
colliders: 'trimesh',
|
|
196
|
+
sensor: false,
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
} : null)), collisionMesh ? { name: collisionMesh.displayName, hidden: false } : undefined);
|
|
168
200
|
}
|
|
169
201
|
/**
|
|
170
202
|
* Converts a live Three object hierarchy into prefab JSON nodes made from
|
|
171
203
|
* Transform, BufferGeometry, and Material components.
|
|
172
204
|
*/
|
|
173
205
|
export function decomposeModelToPrefabNodes(object, options = {}) {
|
|
174
|
-
var _a, _b, _c, _d, _e;
|
|
175
|
-
return (
|
|
206
|
+
var _a, _b, _c, _d, _e, _f;
|
|
207
|
+
return (_e = decomposeObject(object, {
|
|
176
208
|
idPrefix: (_a = options.idPrefix) !== null && _a !== void 0 ? _a : 'model',
|
|
177
209
|
includeInvisible: (_b = options.includeInvisible) !== null && _b !== void 0 ? _b : false,
|
|
178
|
-
|
|
179
|
-
|
|
210
|
+
inferCollisionMeshes: (_c = options.inferCollisionMeshes) !== null && _c !== void 0 ? _c : true,
|
|
211
|
+
getTexturePath: (_d = options.getTexturePath) !== null && _d !== void 0 ? _d : (() => undefined),
|
|
212
|
+
})) !== null && _e !== void 0 ? _e : createNode(object, (_f = options.idPrefix) !== null && _f !== void 0 ? _f : 'model');
|
|
180
213
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { ComponentData, GameObject, Prefab } from
|
|
2
|
-
export type PrefabNodeRecord = Omit<GameObject,
|
|
1
|
+
import type { ComponentData, GameObject, Prefab } from "./types";
|
|
2
|
+
export type PrefabNodeRecord = Omit<GameObject, "children">;
|
|
3
3
|
export type PrefabAssetRefCounts = Record<string, number>;
|
|
4
4
|
export interface PrefabState {
|
|
5
5
|
prefabId?: string;
|
|
@@ -24,12 +24,13 @@ export declare function createEmptyNode(name?: string): GameObject;
|
|
|
24
24
|
export declare function createEmptyPrefab(): Prefab;
|
|
25
25
|
export declare function createModelNode(filename: string, name?: string): GameObject;
|
|
26
26
|
export declare function createImageNode(texturePath: string, name?: string): GameObject;
|
|
27
|
+
export declare function createPackedPrefabNode(url: string): GameObject;
|
|
27
28
|
export declare function normalizePrefab(prefab: Prefab): PrefabState;
|
|
28
29
|
export declare function createPrefabPatch(state: PrefabState, patch: Partial<PrefabState>, nextAssetRefCounts?: PrefabAssetRefCounts): Partial<PrefabState>;
|
|
29
|
-
export declare function denormalizePrefab(state: Pick<PrefabState,
|
|
30
|
+
export declare function denormalizePrefab(state: Pick<PrefabState, "prefabId" | "prefabName" | "rootId" | "nodesById" | "childIdsById">): Prefab;
|
|
30
31
|
export declare function collectSubtreeIds(id: string, childIdsById: Record<string, string[]>): string[];
|
|
31
32
|
export declare function insertSubtree(node: GameObject, parentId: string | null, nodesById: Record<string, PrefabNodeRecord>, childIdsById: Record<string, string[]>, parentIdById: Record<string, string | null>): void;
|
|
32
|
-
export declare function cloneSubtree(id: string, parentId: string | null, source: Pick<PrefabState,
|
|
33
|
+
export declare function cloneSubtree(id: string, parentId: string | null, source: Pick<PrefabState, "nodesById" | "childIdsById">, nodesById: Record<string, PrefabNodeRecord>, childIdsById: Record<string, string[]>, parentIdById: Record<string, string | null>): string | null;
|
|
33
34
|
export declare function isDescendant(id: string, potentialAncestorId: string, parentIdById: Record<string, string | null>): boolean;
|
|
34
35
|
export declare function updateAssetRefsForNodeChange(assetRefCounts: PrefabAssetRefCounts, currentNode: PrefabNodeRecord, nextNode: PrefabNodeRecord): PrefabAssetRefCounts;
|
|
35
36
|
export declare function collectSubtreeAssetRefs(node: GameObject): string[];
|
|
@@ -9,12 +9,12 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
9
9
|
}
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
|
-
import { getComponentAssetRefs, getComponentDef } from
|
|
12
|
+
import { getComponentAssetRefs, getComponentDef, } from "./components/ComponentRegistry";
|
|
13
13
|
function clonePrefabValue(value) {
|
|
14
14
|
if (Array.isArray(value)) {
|
|
15
|
-
return value.map(item => clonePrefabValue(item));
|
|
15
|
+
return value.map((item) => clonePrefabValue(item));
|
|
16
16
|
}
|
|
17
|
-
if (value && typeof value ===
|
|
17
|
+
if (value && typeof value === "object") {
|
|
18
18
|
const clone = {};
|
|
19
19
|
Object.entries(value).forEach(([key, entry]) => {
|
|
20
20
|
clone[key] = clonePrefabValue(entry);
|
|
@@ -25,7 +25,7 @@ function clonePrefabValue(value) {
|
|
|
25
25
|
}
|
|
26
26
|
function createComponentMap(components) {
|
|
27
27
|
const componentMap = {
|
|
28
|
-
transform: createComponentData(
|
|
28
|
+
transform: createComponentData("Transform"),
|
|
29
29
|
};
|
|
30
30
|
Object.entries(components).forEach(([key, component]) => {
|
|
31
31
|
componentMap[key] = createComponentData(component.type, component.properties);
|
|
@@ -33,10 +33,10 @@ function createComponentMap(components) {
|
|
|
33
33
|
return componentMap;
|
|
34
34
|
}
|
|
35
35
|
function getNodeNameFromPath(path, name) {
|
|
36
|
-
return name !== null && name !== void 0 ? name : path.replace(/^.*[\/]/,
|
|
36
|
+
return name !== null && name !== void 0 ? name : path.replace(/^.*[\/]/, "").replace(/\.[^.]+$/, "");
|
|
37
37
|
}
|
|
38
38
|
function getAssetManifestKey(assetRefCounts) {
|
|
39
|
-
return Object.keys(assetRefCounts).sort().join(
|
|
39
|
+
return Object.keys(assetRefCounts).sort().join("|");
|
|
40
40
|
}
|
|
41
41
|
function sameStringArrays(left, right) {
|
|
42
42
|
if (left.length !== right.length)
|
|
@@ -46,7 +46,7 @@ function sameStringArrays(left, right) {
|
|
|
46
46
|
function getAssetRefs(node) {
|
|
47
47
|
var _a;
|
|
48
48
|
const refs = [];
|
|
49
|
-
Object.values((_a = node === null || node === void 0 ? void 0 : node.components) !== null && _a !== void 0 ? _a : {}).forEach(component => {
|
|
49
|
+
Object.values((_a = node === null || node === void 0 ? void 0 : node.components) !== null && _a !== void 0 ? _a : {}).forEach((component) => {
|
|
50
50
|
var _a;
|
|
51
51
|
if (!(component === null || component === void 0 ? void 0 : component.type))
|
|
52
52
|
return;
|
|
@@ -57,13 +57,13 @@ function getAssetRefs(node) {
|
|
|
57
57
|
return refs.sort();
|
|
58
58
|
}
|
|
59
59
|
function addAssetRefs(assetRefCounts, refs) {
|
|
60
|
-
refs.forEach(ref => {
|
|
60
|
+
refs.forEach((ref) => {
|
|
61
61
|
var _a;
|
|
62
62
|
assetRefCounts[ref] = ((_a = assetRefCounts[ref]) !== null && _a !== void 0 ? _a : 0) + 1;
|
|
63
63
|
});
|
|
64
64
|
}
|
|
65
65
|
function removeAssetRefs(assetRefCounts, refs) {
|
|
66
|
-
refs.forEach(ref => {
|
|
66
|
+
refs.forEach((ref) => {
|
|
67
67
|
var _a;
|
|
68
68
|
const nextCount = ((_a = assetRefCounts[ref]) !== null && _a !== void 0 ? _a : 0) - 1;
|
|
69
69
|
if (nextCount > 0) {
|
|
@@ -75,13 +75,15 @@ function removeAssetRefs(assetRefCounts, refs) {
|
|
|
75
75
|
}
|
|
76
76
|
function createAssetRefCounts(nodesById) {
|
|
77
77
|
const assetRefCounts = {};
|
|
78
|
-
Object.values(nodesById).forEach(node =>
|
|
78
|
+
Object.values(nodesById).forEach((node) => {
|
|
79
|
+
addAssetRefs(assetRefCounts, getAssetRefs(node));
|
|
80
|
+
});
|
|
79
81
|
return assetRefCounts;
|
|
80
82
|
}
|
|
81
83
|
function denormalizeNode(id, nodesById, childIdsById) {
|
|
82
84
|
var _a;
|
|
83
85
|
const node = nodesById[id];
|
|
84
|
-
return Object.assign(Object.assign({}, node), { children: ((_a = childIdsById[id]) !== null && _a !== void 0 ? _a : []).map(childId => denormalizeNode(childId, nodesById, childIdsById)) });
|
|
86
|
+
return Object.assign(Object.assign({}, node), { children: ((_a = childIdsById[id]) !== null && _a !== void 0 ? _a : []).map((childId) => denormalizeNode(childId, nodesById, childIdsById)) });
|
|
85
87
|
}
|
|
86
88
|
export function createDefaultComponentProperties(type) {
|
|
87
89
|
var _a, _b;
|
|
@@ -90,32 +92,34 @@ export function createDefaultComponentProperties(type) {
|
|
|
90
92
|
export function createComponentData(type, properties) {
|
|
91
93
|
return {
|
|
92
94
|
type,
|
|
93
|
-
properties: properties
|
|
95
|
+
properties: properties
|
|
96
|
+
? clonePrefabValue(properties)
|
|
97
|
+
: createDefaultComponentProperties(type),
|
|
94
98
|
};
|
|
95
99
|
}
|
|
96
100
|
export function createNode(name, components = {}, options) {
|
|
97
101
|
var _a;
|
|
98
102
|
return Object.assign({ id: (_a = options === null || options === void 0 ? void 0 : options.id) !== null && _a !== void 0 ? _a : crypto.randomUUID(), name, components: createComponentMap(components) }, ((options === null || options === void 0 ? void 0 : options.children) ? { children: options.children } : null));
|
|
99
103
|
}
|
|
100
|
-
export function createEmptyNode(name =
|
|
104
|
+
export function createEmptyNode(name = "New Node") {
|
|
101
105
|
return createNode(name);
|
|
102
106
|
}
|
|
103
107
|
export function createEmptyPrefab() {
|
|
104
108
|
return {
|
|
105
109
|
id: crypto.randomUUID(),
|
|
106
|
-
name:
|
|
107
|
-
root: createNode(
|
|
110
|
+
name: "New Prefab",
|
|
111
|
+
root: createNode("Root", {}, { id: crypto.randomUUID(), children: [] }),
|
|
108
112
|
};
|
|
109
113
|
}
|
|
110
114
|
export function createModelNode(filename, name) {
|
|
111
115
|
return createNode(getNodeNameFromPath(filename, name), {
|
|
112
116
|
model: {
|
|
113
|
-
type:
|
|
117
|
+
type: "Model",
|
|
114
118
|
properties: {
|
|
115
119
|
filename,
|
|
116
120
|
instanced: false,
|
|
117
121
|
repeat: false,
|
|
118
|
-
repeatAxes: [{ axis:
|
|
122
|
+
repeatAxes: [{ axis: "x", count: 1, offset: 1 }],
|
|
119
123
|
},
|
|
120
124
|
},
|
|
121
125
|
});
|
|
@@ -123,12 +127,20 @@ export function createModelNode(filename, name) {
|
|
|
123
127
|
export function createImageNode(texturePath, name) {
|
|
124
128
|
return createNode(getNodeNameFromPath(texturePath, name), {
|
|
125
129
|
geometry: {
|
|
126
|
-
type:
|
|
127
|
-
properties: { geometryType:
|
|
130
|
+
type: "Geometry",
|
|
131
|
+
properties: { geometryType: "plane", args: [1, 1] },
|
|
128
132
|
},
|
|
129
133
|
material: {
|
|
130
|
-
type:
|
|
131
|
-
properties: { color:
|
|
134
|
+
type: "Material",
|
|
135
|
+
properties: { color: "#ffffff", texture: texturePath },
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
export function createPackedPrefabNode(url) {
|
|
140
|
+
return createNode("Packed Prefab", {
|
|
141
|
+
prefabref: {
|
|
142
|
+
type: "PrefabRef",
|
|
143
|
+
properties: { url },
|
|
132
144
|
},
|
|
133
145
|
});
|
|
134
146
|
}
|
|
@@ -151,10 +163,12 @@ export function normalizePrefab(prefab) {
|
|
|
151
163
|
}
|
|
152
164
|
export function createPrefabPatch(state, patch, nextAssetRefCounts = state.assetRefCounts) {
|
|
153
165
|
const assetRefsChanged = nextAssetRefCounts !== state.assetRefCounts;
|
|
154
|
-
return Object.assign(Object.assign({}, patch), (assetRefsChanged
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
166
|
+
return Object.assign(Object.assign({}, patch), (assetRefsChanged
|
|
167
|
+
? {
|
|
168
|
+
assetRefCounts: nextAssetRefCounts,
|
|
169
|
+
assetManifestKey: getAssetManifestKey(nextAssetRefCounts),
|
|
170
|
+
}
|
|
171
|
+
: null));
|
|
158
172
|
}
|
|
159
173
|
export function denormalizePrefab(state) {
|
|
160
174
|
return {
|
|
@@ -175,9 +189,11 @@ export function insertSubtree(node, parentId, nodesById, childIdsById, parentIdB
|
|
|
175
189
|
var _a;
|
|
176
190
|
const { children } = node, nodeRecord = __rest(node, ["children"]);
|
|
177
191
|
nodesById[node.id] = nodeRecord;
|
|
178
|
-
childIdsById[node.id] = (_a = children === null || children === void 0 ? void 0 : children.map(child => child.id)) !== null && _a !== void 0 ? _a : [];
|
|
192
|
+
childIdsById[node.id] = (_a = children === null || children === void 0 ? void 0 : children.map((child) => child.id)) !== null && _a !== void 0 ? _a : [];
|
|
179
193
|
parentIdById[node.id] = parentId;
|
|
180
|
-
children === null || children === void 0 ? void 0 : children.forEach(child =>
|
|
194
|
+
children === null || children === void 0 ? void 0 : children.forEach((child) => {
|
|
195
|
+
insertSubtree(child, node.id, nodesById, childIdsById, parentIdById);
|
|
196
|
+
});
|
|
181
197
|
}
|
|
182
198
|
export function cloneSubtree(id, parentId, source, nodesById, childIdsById, parentIdById) {
|
|
183
199
|
var _a, _b;
|
|
@@ -189,7 +205,7 @@ export function cloneSubtree(id, parentId, source, nodesById, childIdsById, pare
|
|
|
189
205
|
nodesById[clonedId] = clonedNode;
|
|
190
206
|
parentIdById[clonedId] = parentId;
|
|
191
207
|
const clonedChildIds = ((_b = source.childIdsById[id]) !== null && _b !== void 0 ? _b : [])
|
|
192
|
-
.map(childId => cloneSubtree(childId, clonedId, source, nodesById, childIdsById, parentIdById))
|
|
208
|
+
.map((childId) => cloneSubtree(childId, clonedId, source, nodesById, childIdsById, parentIdById))
|
|
193
209
|
.filter((childId) => Boolean(childId));
|
|
194
210
|
childIdsById[clonedId] = clonedChildIds;
|
|
195
211
|
return clonedId;
|
|
@@ -217,7 +233,9 @@ export function updateAssetRefsForNodeChange(assetRefCounts, currentNode, nextNo
|
|
|
217
233
|
export function collectSubtreeAssetRefs(node) {
|
|
218
234
|
var _a;
|
|
219
235
|
const refs = getAssetRefs(node);
|
|
220
|
-
(_a = node.children) === null || _a === void 0 ? void 0 : _a.forEach(child =>
|
|
236
|
+
(_a = node.children) === null || _a === void 0 ? void 0 : _a.forEach((child) => {
|
|
237
|
+
refs.push(...collectSubtreeAssetRefs(child));
|
|
238
|
+
});
|
|
221
239
|
return refs;
|
|
222
240
|
}
|
|
223
241
|
export function collectAssetRefsForIds(ids, nodesById) {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { GameObject, Prefab } from "./types";
|
|
2
|
-
import { Matrix4, Object3D, Vector3 } from
|
|
2
|
+
import { Matrix4, Object3D, Vector3 } from "three";
|
|
3
|
+
export declare function isExternalPath(path: string): boolean;
|
|
4
|
+
export declare function withBasePath(basePath: string | undefined, path: string): string;
|
|
3
5
|
export interface ExportGLBOptions {
|
|
4
6
|
filename?: string;
|
|
5
7
|
}
|
|
@@ -7,6 +9,11 @@ export interface ExportGLBOptions {
|
|
|
7
9
|
export declare function saveJson(data: Prefab, filename: string): Promise<void>;
|
|
8
10
|
/** Load scene JSON from a file */
|
|
9
11
|
export declare function loadJson(): Promise<Prefab | undefined>;
|
|
12
|
+
/** Load scene JSON from a file, also returning the original filename */
|
|
13
|
+
export declare function loadJsonFile(): Promise<{
|
|
14
|
+
prefab: Prefab;
|
|
15
|
+
filename: string;
|
|
16
|
+
} | undefined>;
|
|
10
17
|
/**
|
|
11
18
|
* Export a Three.js scene or object to GLB binary data
|
|
12
19
|
*/
|