react-three-game 0.0.99 → 0.0.101
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/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/tools/assetviewer/page.js +70 -58
- package/dist/tools/dragdrop/DragDropLoader.d.ts +3 -0
- package/dist/tools/dragdrop/DragDropLoader.js +183 -44
- package/dist/tools/dragdrop/index.d.ts +1 -1
- package/dist/tools/dragdrop/index.js +1 -1
- package/dist/tools/dragdrop/modelLoader.js +2 -0
- package/dist/tools/prefabeditor/EditorUI.js +7 -8
- package/dist/tools/prefabeditor/PrefabEditor.d.ts +3 -0
- package/dist/tools/prefabeditor/PrefabEditor.js +28 -11
- package/dist/tools/prefabeditor/PrefabRoot.d.ts +2 -0
- package/dist/tools/prefabeditor/PrefabRoot.js +51 -35
- package/dist/tools/prefabeditor/components/BufferGeometryComponent.js +20 -0
- package/dist/tools/prefabeditor/components/CameraComponent.js +1 -14
- package/dist/tools/prefabeditor/components/ComponentRegistry.d.ts +5 -0
- package/dist/tools/prefabeditor/components/ComponentRegistry.js +31 -0
- package/dist/tools/prefabeditor/components/DataComponent.js +1 -0
- package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +31 -119
- package/dist/tools/prefabeditor/components/EnvironmentComponent.js +1 -0
- package/dist/tools/prefabeditor/components/GeometryComponent.js +1 -0
- package/dist/tools/prefabeditor/components/Input.d.ts +1 -0
- package/dist/tools/prefabeditor/components/MaterialComponent.d.ts +1 -0
- package/dist/tools/prefabeditor/components/MaterialComponent.js +89 -52
- package/dist/tools/prefabeditor/components/ModelComponent.js +45 -3
- package/dist/tools/prefabeditor/components/PointLightComponent.js +19 -25
- package/dist/tools/prefabeditor/components/SpotLightComponent.js +27 -42
- package/dist/tools/prefabeditor/components/SpriteComponent.js +1 -0
- package/dist/tools/prefabeditor/components/TransformComponent.js +1 -0
- package/dist/tools/prefabeditor/modelPrefab.d.ts +16 -0
- package/dist/tools/prefabeditor/modelPrefab.js +180 -0
- package/dist/tools/prefabeditor/prefabStore.d.ts +1 -0
- package/dist/tools/prefabeditor/prefabStore.js +75 -42
- package/package.json +1 -1
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { assetRef, assetRefs } from "./ComponentRegistry";
|
|
3
3
|
import { useHelper } from "@react-three/drei";
|
|
4
|
-
import { useRef
|
|
4
|
+
import { useRef } from "react";
|
|
5
5
|
import { BooleanField, ColorField, Label, NumberField, Vector3Input } from "./Input";
|
|
6
6
|
import { SpotLightHelper } from "three";
|
|
7
7
|
import { useAssetRuntime, useNode } from "../assetRuntime";
|
|
8
|
-
import { useFrame } from "@react-three/fiber";
|
|
9
8
|
import { TexturePicker } from "../../assetviewer/page";
|
|
10
9
|
import { LightSection, ShadowBiasField, mergeWithDefaults } from "./lightUtils";
|
|
11
10
|
const spotLightDefaults = {
|
|
@@ -30,51 +29,37 @@ function SpotLightComponentEditor({ component, onUpdate, basePath = "" }) {
|
|
|
30
29
|
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: [_jsxs(LightSection, { title: "Light", children: [_jsx(ColorField, { name: "color", label: "Color", values: values, onChange: onUpdate }), _jsx(NumberField, { name: "intensity", label: "Intensity", values: values, onChange: onUpdate, min: 0, step: 0.1, fallback: 1 }), _jsx(NumberField, { name: "angle", label: "Angle", values: values, onChange: onUpdate, min: 0, max: Math.PI / 2, step: 0.05, fallback: Math.PI / 3 }), _jsx(NumberField, { name: "penumbra", label: "Penumbra", values: values, onChange: onUpdate, min: 0, max: 1, step: 0.05, fallback: 0 }), _jsx(NumberField, { name: "distance", label: "Distance", values: values, onChange: onUpdate, min: 0, step: 1, fallback: 0 }), _jsx(NumberField, { name: "decay", label: "Decay", values: values, onChange: onUpdate, min: 0, step: 0.1, fallback: 2 }), _jsx(Vector3Input, { label: "Target Offset", value: values.targetOffset, onChange: targetOffset => onUpdate({ targetOffset }), snap: 0.5 }), _jsxs("div", { children: [_jsx(Label, { children: "Texture Map" }), _jsx(TexturePicker, { value: values.map, onChange: (map) => onUpdate({ map }), basePath: basePath })] })] }), _jsxs(LightSection, { title: "Shadow", children: [_jsx(BooleanField, { name: "castShadow", label: "Cast Shadow", values: values, onChange: onUpdate, fallback: false }), values.castShadow ? (_jsxs(_Fragment, { children: [_jsx(BooleanField, { name: "shadowAutoUpdate", label: "Auto Update", values: values, onChange: onUpdate, fallback: true }), _jsx(NumberField, { name: "shadowMapSize", label: "Map Size", values: values, onChange: onUpdate, min: 128, step: 128, fallback: 512 }), _jsx(ShadowBiasField, { name: "shadowBias", label: "Bias", values: values, onChange: onUpdate, fallback: 0 }), _jsx(ShadowBiasField, { name: "shadowNormalBias", label: "Normal Bias", values: values, onChange: onUpdate, fallback: 0 }), _jsx(NumberField, { name: "shadowCameraNear", label: "Near", values: values, onChange: onUpdate, min: 0.001, step: 0.1, fallback: 0.5 }), _jsx(NumberField, { name: "shadowCameraFar", label: "Far", values: values, onChange: onUpdate, min: 0.1, step: 1, fallback: 500 })] })) : null] })] }));
|
|
31
30
|
}
|
|
32
31
|
function SpotLightView({ properties, children }) {
|
|
33
|
-
var _a;
|
|
32
|
+
var _a, _b;
|
|
34
33
|
const { getTexture } = useAssetRuntime();
|
|
35
34
|
const { editMode, isSelected } = useNode();
|
|
36
35
|
const merged = mergeWithDefaults(spotLightDefaults, properties);
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
36
|
+
const textureMap = merged.map
|
|
37
|
+
? (_a = getTexture(merged.map)) !== null && _a !== void 0 ? _a : undefined
|
|
38
|
+
: undefined;
|
|
39
|
+
const lightProps = {
|
|
40
|
+
color: merged.color,
|
|
41
|
+
intensity: merged.intensity,
|
|
42
|
+
angle: merged.angle,
|
|
43
|
+
penumbra: merged.penumbra,
|
|
44
|
+
distance: merged.distance,
|
|
45
|
+
decay: merged.decay,
|
|
46
|
+
castShadow: merged.castShadow,
|
|
47
|
+
map: textureMap,
|
|
48
|
+
// mapped props
|
|
49
|
+
"shadow-mapSize-width": merged.shadowMapSize,
|
|
50
|
+
"shadow-mapSize-height": merged.shadowMapSize,
|
|
51
|
+
"shadow-bias": merged.shadowBias,
|
|
52
|
+
"shadow-normalBias": merged.shadowNormalBias,
|
|
53
|
+
"shadow-autoUpdate": merged.shadowAutoUpdate,
|
|
54
|
+
"shadow-camera-near": merged.shadowCameraNear,
|
|
55
|
+
"shadow-camera-far": merged.shadowCameraFar,
|
|
56
|
+
};
|
|
52
57
|
const spotLightRef = useRef(null);
|
|
53
58
|
const targetRef = useRef(null);
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
useEffect(() => {
|
|
59
|
-
if (spotLightRef.current && targetRef.current) {
|
|
60
|
-
spotLightRef.current.target = targetRef.current;
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
useEffect(() => {
|
|
64
|
-
var _a;
|
|
65
|
-
const shadow = (_a = spotLightRef.current) === null || _a === void 0 ? void 0 : _a.shadow;
|
|
66
|
-
if (!shadow)
|
|
67
|
-
return;
|
|
68
|
-
shadow.needsUpdate = true;
|
|
69
|
-
shadow.camera.updateProjectionMatrix();
|
|
70
|
-
});
|
|
71
|
-
useFrame(() => {
|
|
72
|
-
var _a;
|
|
73
|
-
if ((_a = spotLightRef.current) === null || _a === void 0 ? void 0 : _a.target) {
|
|
74
|
-
spotLightRef.current.target.updateMatrixWorld();
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
return (_jsxs(_Fragment, { children: [_jsx("spotLight", { ref: spotLightRef, color: color, intensity: intensity, angle: angle, penumbra: penumbra, distance: distance, decay: decay, map: textureMap, castShadow: castShadow, "shadow-mapSize-width": shadowMapSize, "shadow-mapSize-height": shadowMapSize, "shadow-bias": shadowBias, "shadow-normalBias": shadowNormalBias, "shadow-autoUpdate": shadowAutoUpdate, "shadow-camera-near": shadowCameraNear, "shadow-camera-far": shadowCameraFar }), _jsx("object3D", { ref: targetRef, position: targetOffset }), editMode && isSelected && (_jsxs(_Fragment, { children: [_jsxs("mesh", { children: [_jsx("sphereGeometry", { args: [0.2, 8, 6] }), _jsx("meshBasicMaterial", { color: color, wireframe: true })] }), _jsxs("mesh", { position: targetOffset, children: [_jsx("sphereGeometry", { args: [0.15, 8, 6] }), _jsx("meshBasicMaterial", { color: color, wireframe: true, opacity: 0.5, transparent: true })] })] })), children] }));
|
|
59
|
+
const showHelper = editMode && isSelected;
|
|
60
|
+
const helperTarget = showHelper && spotLightRef.current ? { current: spotLightRef.current } : null;
|
|
61
|
+
useHelper(helperTarget, SpotLightHelper);
|
|
62
|
+
return (_jsxs("group", { children: [_jsxs("spotLight", Object.assign({ ref: spotLightRef }, lightProps, { target: (_b = targetRef.current) !== null && _b !== void 0 ? _b : undefined, children: [showHelper && (_jsxs(_Fragment, { children: [_jsxs("mesh", { children: [_jsx("sphereGeometry", { args: [0.2, 8, 6] }), _jsx("meshBasicMaterial", { color: merged.color, wireframe: true })] }), _jsxs("mesh", { position: merged.targetOffset, children: [_jsx("sphereGeometry", { args: [0.15, 8, 6] }), _jsx("meshBasicMaterial", { color: merged.color, wireframe: true, opacity: 0.5, transparent: true })] })] })), children] })), _jsx("object3D", { ref: targetRef, position: merged.targetOffset })] }));
|
|
78
63
|
}
|
|
79
64
|
const SpotLightComponent = {
|
|
80
65
|
name: 'SpotLight',
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Object3D } from 'three';
|
|
2
|
+
import type { Texture } from 'three';
|
|
3
|
+
import type { GameObject } from './types';
|
|
4
|
+
export interface DecomposeModelOptions {
|
|
5
|
+
/** Prefix used for generated prefab node ids. Defaults to "model". */
|
|
6
|
+
idPrefix?: string;
|
|
7
|
+
/** Include invisible Three objects in the generated prefab tree. */
|
|
8
|
+
includeInvisible?: boolean;
|
|
9
|
+
/** Return a serializable texture ref for embedded or externally loaded textures. */
|
|
10
|
+
getTexturePath?: (texture: Texture, usage: 'map' | 'normalMap') => string | null | undefined;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Converts a live Three object hierarchy into prefab JSON nodes made from
|
|
14
|
+
* Transform, BufferGeometry, and Material components.
|
|
15
|
+
*/
|
|
16
|
+
export declare function decomposeModelToPrefabNodes(object: Object3D, options?: DecomposeModelOptions): GameObject;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { BackSide, DoubleSide, Mesh, } from 'three';
|
|
2
|
+
function createId(prefix) {
|
|
3
|
+
return `${prefix}-${crypto.randomUUID()}`;
|
|
4
|
+
}
|
|
5
|
+
function toArrayAttribute(attribute, itemSize, vertexIndices) {
|
|
6
|
+
if (!attribute || attribute.itemSize < itemSize)
|
|
7
|
+
return undefined;
|
|
8
|
+
const values = [];
|
|
9
|
+
for (const vertexIndex of vertexIndices) {
|
|
10
|
+
for (let axis = 0; axis < itemSize; axis += 1) {
|
|
11
|
+
values.push(attribute.getComponent(vertexIndex, axis));
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return values;
|
|
15
|
+
}
|
|
16
|
+
function getSequentialVertexIndices(count) {
|
|
17
|
+
return Array.from({ length: count }, (_, index) => index);
|
|
18
|
+
}
|
|
19
|
+
function serializeGeometry(geometry) {
|
|
20
|
+
var _a, _b, _c, _d;
|
|
21
|
+
const position = geometry.getAttribute('position');
|
|
22
|
+
const normal = geometry.getAttribute('normal');
|
|
23
|
+
const uv = geometry.getAttribute('uv');
|
|
24
|
+
const vertexIndices = getSequentialVertexIndices((_a = position === null || position === void 0 ? void 0 : position.count) !== null && _a !== void 0 ? _a : 0);
|
|
25
|
+
const index = geometry.index;
|
|
26
|
+
const indices = [];
|
|
27
|
+
if (index) {
|
|
28
|
+
for (let offset = 0; offset < index.count; offset += 1) {
|
|
29
|
+
indices.push(index.getX(offset));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
indices.push(...vertexIndices);
|
|
34
|
+
}
|
|
35
|
+
const positions = (_b = toArrayAttribute(position, 3, vertexIndices)) !== null && _b !== void 0 ? _b : [];
|
|
36
|
+
const normals = (_c = toArrayAttribute(normal, 3, vertexIndices)) !== null && _c !== void 0 ? _c : [];
|
|
37
|
+
const uvs = (_d = toArrayAttribute(uv, 2, vertexIndices)) !== null && _d !== void 0 ? _d : [];
|
|
38
|
+
const groups = geometry.groups.map(group => {
|
|
39
|
+
var _a;
|
|
40
|
+
return ({
|
|
41
|
+
start: group.start,
|
|
42
|
+
count: group.count,
|
|
43
|
+
materialIndex: (_a = group.materialIndex) !== null && _a !== void 0 ? _a : 0,
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
return {
|
|
47
|
+
positions,
|
|
48
|
+
indices,
|
|
49
|
+
normals,
|
|
50
|
+
uvs,
|
|
51
|
+
groups,
|
|
52
|
+
computeVertexNormals: normals.length === 0,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function getSideName(side) {
|
|
56
|
+
if (side === BackSide)
|
|
57
|
+
return 'BackSide';
|
|
58
|
+
if (side === DoubleSide)
|
|
59
|
+
return 'DoubleSide';
|
|
60
|
+
return 'FrontSide';
|
|
61
|
+
}
|
|
62
|
+
function getMaterialColor(material) {
|
|
63
|
+
var _a, _b;
|
|
64
|
+
const maybeColor = material;
|
|
65
|
+
return (_b = (_a = maybeColor.color) === null || _a === void 0 ? void 0 : _a.getStyle) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
66
|
+
}
|
|
67
|
+
function getTextureImagePath(texture) {
|
|
68
|
+
var _a;
|
|
69
|
+
const image = texture === null || texture === void 0 ? void 0 : texture.image;
|
|
70
|
+
const path = (_a = image === null || image === void 0 ? void 0 : image.currentSrc) !== null && _a !== void 0 ? _a : image === null || image === void 0 ? void 0 : image.src;
|
|
71
|
+
if (typeof path === 'string' && path.trim() && !path.startsWith('blob:')) {
|
|
72
|
+
return path;
|
|
73
|
+
}
|
|
74
|
+
return getTextureImageDataUrl(image);
|
|
75
|
+
}
|
|
76
|
+
function getTextureImageDataUrl(image) {
|
|
77
|
+
if (!image
|
|
78
|
+
|| typeof document === 'undefined'
|
|
79
|
+
|| typeof image.width !== 'number'
|
|
80
|
+
|| typeof image.height !== 'number'
|
|
81
|
+
|| image.width <= 0
|
|
82
|
+
|| image.height <= 0) {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
const canvas = document.createElement('canvas');
|
|
87
|
+
canvas.width = image.width;
|
|
88
|
+
canvas.height = image.height;
|
|
89
|
+
const context = canvas.getContext('2d');
|
|
90
|
+
if (!context)
|
|
91
|
+
return undefined;
|
|
92
|
+
context.drawImage(image, 0, 0);
|
|
93
|
+
return canvas.toDataURL('image/png');
|
|
94
|
+
}
|
|
95
|
+
catch (_a) {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function getTexturePath(texture, usage, options) {
|
|
100
|
+
var _a, _b;
|
|
101
|
+
if (!texture)
|
|
102
|
+
return undefined;
|
|
103
|
+
return (_b = (_a = getTextureImagePath(texture)) !== null && _a !== void 0 ? _a : options.getTexturePath(texture, usage)) !== null && _b !== void 0 ? _b : undefined;
|
|
104
|
+
}
|
|
105
|
+
function serializeMaterial(material, attach, options) {
|
|
106
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
107
|
+
const source = material;
|
|
108
|
+
const materialType = 'metalness' in source || 'roughness' in source ? 'standard' : 'basic';
|
|
109
|
+
const texture = getTexturePath(source.map, 'map', options);
|
|
110
|
+
const normalMapTexture = getTexturePath(source.normalMap, 'normalMap', options);
|
|
111
|
+
const normalScale = (_b = (_a = source.normalScale) === null || _a === void 0 ? void 0 : _a.toArray) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
112
|
+
return {
|
|
113
|
+
type: 'Material',
|
|
114
|
+
properties: Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ attach,
|
|
115
|
+
materialType, color: (_c = getMaterialColor(material)) !== null && _c !== void 0 ? _c : '#ffffff' }, (texture ? { texture } : null)), (normalMapTexture ? { normalMapTexture } : null)), (normalScale ? { normalScale } : null)), { opacity: material.opacity, transparent: material.transparent, side: getSideName(material.side), wireframe: (_d = source.wireframe) !== null && _d !== void 0 ? _d : false, toneMapped: (_e = source.toneMapped) !== null && _e !== void 0 ? _e : true }), (materialType === 'standard' ? {
|
|
116
|
+
metalness: (_f = source.metalness) !== null && _f !== void 0 ? _f : 0,
|
|
117
|
+
roughness: (_g = source.roughness) !== null && _g !== void 0 ? _g : 1,
|
|
118
|
+
} : null)),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function getMeshParts(mesh) {
|
|
122
|
+
const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
|
|
123
|
+
return materials.map((material, index) => {
|
|
124
|
+
return {
|
|
125
|
+
key: materials.length > 1 ? `material_${index}` : 'material',
|
|
126
|
+
material,
|
|
127
|
+
attach: materials.length > 1 ? `material-${index}` : 'material',
|
|
128
|
+
};
|
|
129
|
+
}).filter(part => part.material);
|
|
130
|
+
}
|
|
131
|
+
function createTransformComponent(object) {
|
|
132
|
+
return {
|
|
133
|
+
type: 'Transform',
|
|
134
|
+
properties: {
|
|
135
|
+
position: object.position.toArray(),
|
|
136
|
+
rotation: [object.rotation.x, object.rotation.y, object.rotation.z],
|
|
137
|
+
scale: object.scale.toArray(),
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function createNode(object, idPrefix, children = [], components = {}) {
|
|
142
|
+
return {
|
|
143
|
+
id: createId(idPrefix),
|
|
144
|
+
name: object.name || object.type,
|
|
145
|
+
hidden: object.visible === false ? true : undefined,
|
|
146
|
+
components: Object.assign({ transform: createTransformComponent(object) }, components),
|
|
147
|
+
children,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function decomposeObject(object, options) {
|
|
151
|
+
if (!options.includeInvisible && !object.visible)
|
|
152
|
+
return null;
|
|
153
|
+
const childNodes = object.children
|
|
154
|
+
.map(child => decomposeObject(child, options))
|
|
155
|
+
.filter((child) => child != null);
|
|
156
|
+
if (!(object instanceof Mesh)) {
|
|
157
|
+
return createNode(object, options.idPrefix, childNodes);
|
|
158
|
+
}
|
|
159
|
+
const parts = getMeshParts(object);
|
|
160
|
+
const materialComponents = parts.reduce((result, part) => {
|
|
161
|
+
result[part.key] = serializeMaterial(part.material, part.attach, options);
|
|
162
|
+
return result;
|
|
163
|
+
}, {});
|
|
164
|
+
return createNode(object, options.idPrefix, childNodes, Object.assign({ geometry: {
|
|
165
|
+
type: 'BufferGeometry',
|
|
166
|
+
properties: Object.assign(Object.assign({}, serializeGeometry(object.geometry)), { visible: object.visible, castShadow: object.castShadow, receiveShadow: object.receiveShadow }),
|
|
167
|
+
} }, materialComponents));
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Converts a live Three object hierarchy into prefab JSON nodes made from
|
|
171
|
+
* Transform, BufferGeometry, and Material components.
|
|
172
|
+
*/
|
|
173
|
+
export function decomposeModelToPrefabNodes(object, options = {}) {
|
|
174
|
+
var _a, _b, _c, _d, _e;
|
|
175
|
+
return (_d = decomposeObject(object, {
|
|
176
|
+
idPrefix: (_a = options.idPrefix) !== null && _a !== void 0 ? _a : 'model',
|
|
177
|
+
includeInvisible: (_b = options.includeInvisible) !== null && _b !== void 0 ? _b : false,
|
|
178
|
+
getTexturePath: (_c = options.getTexturePath) !== null && _c !== void 0 ? _c : (() => undefined),
|
|
179
|
+
})) !== null && _d !== void 0 ? _d : createNode(object, (_e = options.idPrefix) !== null && _e !== void 0 ? _e : 'model');
|
|
180
|
+
}
|
|
@@ -5,6 +5,7 @@ import { denormalizePrefab, PrefabState, PrefabNodeRecord } from "./prefab";
|
|
|
5
5
|
export interface PrefabStoreState extends PrefabState {
|
|
6
6
|
replacePrefab: (prefab: Prefab) => void;
|
|
7
7
|
updateNode: (id: string, update: (node: PrefabNodeRecord) => PrefabNodeRecord) => void;
|
|
8
|
+
replaceNode: (id: string, node: GameObject) => void;
|
|
8
9
|
addChild: (parentId: string, node: GameObject) => void;
|
|
9
10
|
deleteNode: (id: string) => void;
|
|
10
11
|
duplicateNode: (id: string) => string | null;
|
|
@@ -5,6 +5,45 @@ import { createStore } from "zustand/vanilla";
|
|
|
5
5
|
import { collectAssetRefsForIds, collectSubtreeAssetRefs, collectSubtreeIds, cloneSubtree, createPrefabPatch, denormalizePrefab, insertSubtree, isDescendant, normalizePrefab, updateAssetRefsForNodeChange, } from "./prefab";
|
|
6
6
|
const PrefabStoreContext = createContext(null);
|
|
7
7
|
const EMPTY_CHILD_IDS = [];
|
|
8
|
+
function addAssetRefs(assetRefCounts, refs) {
|
|
9
|
+
refs.forEach(ref => {
|
|
10
|
+
var _a;
|
|
11
|
+
assetRefCounts[ref] = ((_a = assetRefCounts[ref]) !== null && _a !== void 0 ? _a : 0) + 1;
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
function removeAssetRefs(assetRefCounts, refs) {
|
|
15
|
+
refs.forEach(ref => {
|
|
16
|
+
var _a;
|
|
17
|
+
const nextCount = ((_a = assetRefCounts[ref]) !== null && _a !== void 0 ? _a : 0) - 1;
|
|
18
|
+
if (nextCount > 0) {
|
|
19
|
+
assetRefCounts[ref] = nextCount;
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
delete assetRefCounts[ref];
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function cloneGraphState(state) {
|
|
26
|
+
return {
|
|
27
|
+
nodesById: Object.assign({}, state.nodesById),
|
|
28
|
+
childIdsById: Object.assign({}, state.childIdsById),
|
|
29
|
+
parentIdById: Object.assign({}, state.parentIdById),
|
|
30
|
+
assetRefCounts: Object.assign({}, state.assetRefCounts),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function removeSubtreeFromGraph(id, state, next) {
|
|
34
|
+
const ids = collectSubtreeIds(id, state.childIdsById);
|
|
35
|
+
removeAssetRefs(next.assetRefCounts, collectAssetRefsForIds(ids, state.nodesById));
|
|
36
|
+
ids.forEach(nodeId => {
|
|
37
|
+
delete next.nodesById[nodeId];
|
|
38
|
+
delete next.childIdsById[nodeId];
|
|
39
|
+
delete next.parentIdById[nodeId];
|
|
40
|
+
});
|
|
41
|
+
return ids;
|
|
42
|
+
}
|
|
43
|
+
function insertSubtreeIntoGraph(node, parentId, next) {
|
|
44
|
+
insertSubtree(node, parentId, next.nodesById, next.childIdsById, next.parentIdById);
|
|
45
|
+
addAssetRefs(next.assetRefCounts, collectSubtreeAssetRefs(node));
|
|
46
|
+
}
|
|
8
47
|
export function PrefabStoreProvider({ store, children, }) {
|
|
9
48
|
return createElement(PrefabStoreContext.Provider, { value: store }, children);
|
|
10
49
|
}
|
|
@@ -42,26 +81,40 @@ export function createPrefabStore(prefab) {
|
|
|
42
81
|
set(createPrefabPatch(state, {
|
|
43
82
|
nodesById: Object.assign(Object.assign({}, state.nodesById), { [id]: nextNode }),
|
|
44
83
|
}, nextAssetRefCounts));
|
|
84
|
+
}, replaceNode: (id, node) => {
|
|
85
|
+
var _a;
|
|
86
|
+
const state = get();
|
|
87
|
+
if (!state.nodesById[id])
|
|
88
|
+
return;
|
|
89
|
+
const parentId = state.parentIdById[id];
|
|
90
|
+
const next = cloneGraphState(state);
|
|
91
|
+
removeSubtreeFromGraph(id, state, next);
|
|
92
|
+
insertSubtreeIntoGraph(node, parentId, next);
|
|
93
|
+
const patch = {
|
|
94
|
+
nodesById: next.nodesById,
|
|
95
|
+
childIdsById: next.childIdsById,
|
|
96
|
+
parentIdById: next.parentIdById,
|
|
97
|
+
};
|
|
98
|
+
if (id === state.rootId) {
|
|
99
|
+
patch.rootId = node.id;
|
|
100
|
+
}
|
|
101
|
+
else if (parentId) {
|
|
102
|
+
next.childIdsById[parentId] = ((_a = next.childIdsById[parentId]) !== null && _a !== void 0 ? _a : []).map(childId => childId === id ? node.id : childId);
|
|
103
|
+
}
|
|
104
|
+
set(createPrefabPatch(state, patch, next.assetRefCounts));
|
|
45
105
|
}, addChild: (parentId, node) => {
|
|
46
106
|
var _a;
|
|
47
107
|
const state = get();
|
|
48
108
|
if (!state.nodesById[parentId])
|
|
49
109
|
return;
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const nextAssetRefCounts = Object.assign({}, state.assetRefCounts);
|
|
54
|
-
insertSubtree(node, parentId, nextNodesById, nextChildIdsById, nextParentIdById);
|
|
55
|
-
nextChildIdsById[parentId] = [...((_a = nextChildIdsById[parentId]) !== null && _a !== void 0 ? _a : []), node.id];
|
|
56
|
-
collectSubtreeAssetRefs(node).forEach(ref => {
|
|
57
|
-
var _a;
|
|
58
|
-
nextAssetRefCounts[ref] = ((_a = nextAssetRefCounts[ref]) !== null && _a !== void 0 ? _a : 0) + 1;
|
|
59
|
-
});
|
|
110
|
+
const next = cloneGraphState(state);
|
|
111
|
+
insertSubtreeIntoGraph(node, parentId, next);
|
|
112
|
+
next.childIdsById[parentId] = [...((_a = next.childIdsById[parentId]) !== null && _a !== void 0 ? _a : []), node.id];
|
|
60
113
|
set(createPrefabPatch(state, {
|
|
61
|
-
nodesById:
|
|
62
|
-
childIdsById:
|
|
63
|
-
parentIdById:
|
|
64
|
-
},
|
|
114
|
+
nodesById: next.nodesById,
|
|
115
|
+
childIdsById: next.childIdsById,
|
|
116
|
+
parentIdById: next.parentIdById,
|
|
117
|
+
}, next.assetRefCounts));
|
|
65
118
|
}, deleteNode: (id) => {
|
|
66
119
|
var _a;
|
|
67
120
|
const state = get();
|
|
@@ -70,31 +123,14 @@ export function createPrefabStore(prefab) {
|
|
|
70
123
|
const parentId = state.parentIdById[id];
|
|
71
124
|
if (!parentId)
|
|
72
125
|
return;
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const nextParentIdById = Object.assign({}, state.parentIdById);
|
|
77
|
-
const nextAssetRefCounts = Object.assign({}, state.assetRefCounts);
|
|
78
|
-
collectAssetRefsForIds(idsToDelete, state.nodesById).forEach(ref => {
|
|
79
|
-
var _a;
|
|
80
|
-
const nextCount = ((_a = nextAssetRefCounts[ref]) !== null && _a !== void 0 ? _a : 0) - 1;
|
|
81
|
-
if (nextCount > 0) {
|
|
82
|
-
nextAssetRefCounts[ref] = nextCount;
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
delete nextAssetRefCounts[ref];
|
|
86
|
-
});
|
|
87
|
-
idsToDelete.forEach(nodeId => {
|
|
88
|
-
delete nextNodesById[nodeId];
|
|
89
|
-
delete nextChildIdsById[nodeId];
|
|
90
|
-
delete nextParentIdById[nodeId];
|
|
91
|
-
});
|
|
92
|
-
nextChildIdsById[parentId] = ((_a = nextChildIdsById[parentId]) !== null && _a !== void 0 ? _a : []).filter(childId => childId !== id);
|
|
126
|
+
const next = cloneGraphState(state);
|
|
127
|
+
removeSubtreeFromGraph(id, state, next);
|
|
128
|
+
next.childIdsById[parentId] = ((_a = next.childIdsById[parentId]) !== null && _a !== void 0 ? _a : []).filter(childId => childId !== id);
|
|
93
129
|
set(createPrefabPatch(state, {
|
|
94
|
-
nodesById:
|
|
95
|
-
childIdsById:
|
|
96
|
-
parentIdById:
|
|
97
|
-
},
|
|
130
|
+
nodesById: next.nodesById,
|
|
131
|
+
childIdsById: next.childIdsById,
|
|
132
|
+
parentIdById: next.parentIdById,
|
|
133
|
+
}, next.assetRefCounts));
|
|
98
134
|
}, duplicateNode: (id) => {
|
|
99
135
|
var _a;
|
|
100
136
|
const state = get();
|
|
@@ -119,10 +155,7 @@ export function createPrefabStore(prefab) {
|
|
|
119
155
|
}
|
|
120
156
|
nextChildIdsById[parentId] = siblings;
|
|
121
157
|
const nextAssetRefCounts = Object.assign({}, state.assetRefCounts);
|
|
122
|
-
collectAssetRefsForIds(collectSubtreeIds(id, state.childIdsById), state.nodesById)
|
|
123
|
-
var _a;
|
|
124
|
-
nextAssetRefCounts[ref] = ((_a = nextAssetRefCounts[ref]) !== null && _a !== void 0 ? _a : 0) + 1;
|
|
125
|
-
});
|
|
158
|
+
addAssetRefs(nextAssetRefCounts, collectAssetRefsForIds(collectSubtreeIds(id, state.childIdsById), state.nodesById));
|
|
126
159
|
set(createPrefabPatch(state, {
|
|
127
160
|
nodesById: nextNodesById,
|
|
128
161
|
childIdsById: nextChildIdsById,
|