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.
Files changed (34) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.js +1 -0
  3. package/dist/tools/assetviewer/page.js +70 -58
  4. package/dist/tools/dragdrop/DragDropLoader.d.ts +3 -0
  5. package/dist/tools/dragdrop/DragDropLoader.js +183 -44
  6. package/dist/tools/dragdrop/index.d.ts +1 -1
  7. package/dist/tools/dragdrop/index.js +1 -1
  8. package/dist/tools/dragdrop/modelLoader.js +2 -0
  9. package/dist/tools/prefabeditor/EditorUI.js +7 -8
  10. package/dist/tools/prefabeditor/PrefabEditor.d.ts +3 -0
  11. package/dist/tools/prefabeditor/PrefabEditor.js +28 -11
  12. package/dist/tools/prefabeditor/PrefabRoot.d.ts +2 -0
  13. package/dist/tools/prefabeditor/PrefabRoot.js +51 -35
  14. package/dist/tools/prefabeditor/components/BufferGeometryComponent.js +20 -0
  15. package/dist/tools/prefabeditor/components/CameraComponent.js +1 -14
  16. package/dist/tools/prefabeditor/components/ComponentRegistry.d.ts +5 -0
  17. package/dist/tools/prefabeditor/components/ComponentRegistry.js +31 -0
  18. package/dist/tools/prefabeditor/components/DataComponent.js +1 -0
  19. package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +31 -119
  20. package/dist/tools/prefabeditor/components/EnvironmentComponent.js +1 -0
  21. package/dist/tools/prefabeditor/components/GeometryComponent.js +1 -0
  22. package/dist/tools/prefabeditor/components/Input.d.ts +1 -0
  23. package/dist/tools/prefabeditor/components/MaterialComponent.d.ts +1 -0
  24. package/dist/tools/prefabeditor/components/MaterialComponent.js +89 -52
  25. package/dist/tools/prefabeditor/components/ModelComponent.js +45 -3
  26. package/dist/tools/prefabeditor/components/PointLightComponent.js +19 -25
  27. package/dist/tools/prefabeditor/components/SpotLightComponent.js +27 -42
  28. package/dist/tools/prefabeditor/components/SpriteComponent.js +1 -0
  29. package/dist/tools/prefabeditor/components/TransformComponent.js +1 -0
  30. package/dist/tools/prefabeditor/modelPrefab.d.ts +16 -0
  31. package/dist/tools/prefabeditor/modelPrefab.js +180 -0
  32. package/dist/tools/prefabeditor/prefabStore.d.ts +1 -0
  33. package/dist/tools/prefabeditor/prefabStore.js +75 -42
  34. 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, useEffect } from "react";
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 color = merged.color;
38
- const intensity = merged.intensity;
39
- const angle = merged.angle;
40
- const penumbra = merged.penumbra;
41
- const distance = merged.distance;
42
- const decay = merged.decay;
43
- const castShadow = merged.castShadow;
44
- const shadowMapSize = merged.shadowMapSize;
45
- const shadowBias = merged.shadowBias;
46
- const shadowNormalBias = merged.shadowNormalBias;
47
- const shadowAutoUpdate = merged.shadowAutoUpdate;
48
- const shadowCameraNear = merged.shadowCameraNear;
49
- const shadowCameraFar = merged.shadowCameraFar;
50
- const targetOffset = merged.targetOffset;
51
- const textureMap = merged.map ? (_a = getTexture(merged.map)) !== null && _a !== void 0 ? _a : undefined : undefined;
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 helperTarget = editMode && isSelected && spotLightRef.current
55
- ? { current: spotLightRef.current }
56
- : null;
57
- useHelper(helperTarget, SpotLightHelper, color);
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',
@@ -17,6 +17,7 @@ function SpriteComponentEditor({ component, onUpdate, }) {
17
17
  }
18
18
  const SpriteComponent = {
19
19
  name: 'Sprite',
20
+ disableSiblingComposition: true,
20
21
  Editor: SpriteComponentEditor,
21
22
  defaultProperties: {
22
23
  center: [0.5, 0.5],
@@ -48,6 +48,7 @@ function TransformComponentEditor({ component, onUpdate }) {
48
48
  }
49
49
  const TransformComponent = {
50
50
  name: 'Transform',
51
+ disableSiblingComposition: true,
51
52
  Editor: TransformComponentEditor,
52
53
  defaultProperties: {}
53
54
  };
@@ -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 nextNodesById = Object.assign({}, state.nodesById);
51
- const nextChildIdsById = Object.assign({}, state.childIdsById);
52
- const nextParentIdById = Object.assign({}, state.parentIdById);
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: nextNodesById,
62
- childIdsById: nextChildIdsById,
63
- parentIdById: nextParentIdById,
64
- }, nextAssetRefCounts));
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 idsToDelete = collectSubtreeIds(id, state.childIdsById);
74
- const nextNodesById = Object.assign({}, state.nodesById);
75
- const nextChildIdsById = Object.assign({}, state.childIdsById);
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: nextNodesById,
95
- childIdsById: nextChildIdsById,
96
- parentIdById: nextParentIdById,
97
- }, nextAssetRefCounts));
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).forEach(ref => {
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-three-game",
3
- "version": "0.0.99",
3
+ "version": "0.0.101",
4
4
  "description": "high performance 3D game engine built in React",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",