react-three-game 0.0.70 → 0.0.71
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 +10 -5
- package/dist/index.js +7 -2
- package/dist/tools/prefabeditor/EditorTree.js +2 -12
- package/dist/tools/prefabeditor/EditorTreeMenus.js +1 -19
- package/dist/tools/prefabeditor/EditorUI.js +2 -1
- package/dist/tools/prefabeditor/PrefabEditor.d.ts +1 -1
- package/dist/tools/prefabeditor/PrefabEditor.js +12 -21
- package/dist/tools/prefabeditor/PrefabRoot.d.ts +25 -12
- package/dist/tools/prefabeditor/PrefabRoot.js +61 -28
- package/dist/tools/prefabeditor/RefBridge.d.ts +24 -0
- package/dist/tools/prefabeditor/RefBridge.js +44 -0
- package/dist/tools/prefabeditor/components/AmbientLightComponent.js +10 -7
- package/dist/tools/prefabeditor/components/CameraComponent.js +8 -14
- package/dist/tools/prefabeditor/components/ClickComponent.js +2 -0
- package/dist/tools/prefabeditor/components/ComponentRegistry.d.ts +21 -1
- package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +124 -52
- package/dist/tools/prefabeditor/components/EnvironmentComponent.js +5 -3
- package/dist/tools/prefabeditor/components/MaterialComponent.js +9 -6
- package/dist/tools/prefabeditor/components/ModelComponent.js +4 -2
- package/dist/tools/prefabeditor/components/PhysicsComponent.js +5 -3
- package/dist/tools/prefabeditor/components/PointLightComponent.d.ts +3 -0
- package/dist/tools/prefabeditor/components/PointLightComponent.js +55 -0
- package/dist/tools/prefabeditor/components/SoundComponent.js +20 -16
- package/dist/tools/prefabeditor/components/SpotLightComponent.js +48 -24
- package/dist/tools/prefabeditor/components/index.js +2 -0
- package/dist/tools/prefabeditor/components/lightUtils.d.ts +13 -0
- package/dist/tools/prefabeditor/components/lightUtils.js +64 -0
- package/dist/tools/prefabeditor/prefab.d.ts +37 -0
- package/dist/tools/prefabeditor/prefab.js +229 -0
- package/dist/tools/prefabeditor/prefabStore.d.ts +3 -16
- package/dist/tools/prefabeditor/prefabStore.js +29 -168
- package/dist/tools/prefabeditor/{sceneApi.js → scene.js} +3 -14
- package/dist/tools/prefabeditor/utils.d.ts +0 -4
- package/dist/tools/prefabeditor/utils.js +0 -37
- package/package.json +1 -1
- /package/dist/tools/prefabeditor/{sceneApi.d.ts → scene.d.ts} +0 -0
|
@@ -1,57 +1,81 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { useHelper } from "@react-three/drei";
|
|
3
|
+
import { useRef, useEffect } from "react";
|
|
4
|
+
import { BooleanField, ColorField, Label, NumberField, Vector3Input } from "./Input";
|
|
4
5
|
import { SpotLightHelper } from "three";
|
|
6
|
+
import { useSceneRuntime } from "../PrefabRoot";
|
|
5
7
|
import { useFrame } from "@react-three/fiber";
|
|
6
8
|
import { TexturePicker } from "../../assetviewer/page";
|
|
9
|
+
import { LightSection, ShadowBiasField, mergeWithDefaults } from "./lightUtils";
|
|
7
10
|
const spotLightDefaults = {
|
|
8
11
|
color: '#ffffff',
|
|
9
|
-
intensity:
|
|
10
|
-
angle:
|
|
11
|
-
penumbra: 0
|
|
12
|
-
distance:
|
|
13
|
-
|
|
12
|
+
intensity: 1,
|
|
13
|
+
angle: Math.PI / 3,
|
|
14
|
+
penumbra: 0,
|
|
15
|
+
distance: 0,
|
|
16
|
+
decay: 2,
|
|
17
|
+
castShadow: false,
|
|
18
|
+
shadowMapSize: 512,
|
|
19
|
+
shadowBias: 0,
|
|
20
|
+
shadowNormalBias: 0,
|
|
21
|
+
shadowAutoUpdate: true,
|
|
22
|
+
shadowCameraNear: 0.5,
|
|
23
|
+
shadowCameraFar: 500,
|
|
24
|
+
targetOffset: [0, -5, 0],
|
|
25
|
+
map: undefined,
|
|
14
26
|
};
|
|
15
27
|
function SpotLightComponentEditor({ component, onUpdate, basePath = "" }) {
|
|
16
|
-
const values =
|
|
17
|
-
return (_jsxs(
|
|
28
|
+
const values = mergeWithDefaults(spotLightDefaults, component.properties);
|
|
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] })] }));
|
|
18
30
|
}
|
|
19
|
-
function SpotLightView({ properties, children, editMode, isSelected
|
|
20
|
-
|
|
31
|
+
function SpotLightView({ properties, children, editMode, isSelected }) {
|
|
32
|
+
var _a;
|
|
33
|
+
const { getTexture } = useSceneRuntime();
|
|
34
|
+
const merged = mergeWithDefaults(spotLightDefaults, properties);
|
|
21
35
|
const color = merged.color;
|
|
22
36
|
const intensity = merged.intensity;
|
|
23
37
|
const angle = merged.angle;
|
|
24
38
|
const penumbra = merged.penumbra;
|
|
25
39
|
const distance = merged.distance;
|
|
40
|
+
const decay = merged.decay;
|
|
26
41
|
const castShadow = merged.castShadow;
|
|
27
|
-
const
|
|
42
|
+
const shadowMapSize = merged.shadowMapSize;
|
|
43
|
+
const shadowBias = merged.shadowBias;
|
|
44
|
+
const shadowNormalBias = merged.shadowNormalBias;
|
|
45
|
+
const shadowAutoUpdate = merged.shadowAutoUpdate;
|
|
46
|
+
const shadowCameraNear = merged.shadowCameraNear;
|
|
47
|
+
const shadowCameraFar = merged.shadowCameraFar;
|
|
48
|
+
const targetOffset = merged.targetOffset;
|
|
49
|
+
const textureMap = merged.map ? (_a = getTexture(merged.map)) !== null && _a !== void 0 ? _a : undefined : undefined;
|
|
28
50
|
const spotLightRef = useRef(null);
|
|
29
51
|
const targetRef = useRef(null);
|
|
30
|
-
|
|
31
|
-
const spotLightHelper = useMemo(() => spotLight ? new SpotLightHelper(spotLight, color) : null, [spotLight, color]);
|
|
32
|
-
useEffect(() => {
|
|
33
|
-
return () => {
|
|
34
|
-
spotLightHelper === null || spotLightHelper === void 0 ? void 0 : spotLightHelper.dispose();
|
|
35
|
-
};
|
|
36
|
-
}, [spotLightHelper]);
|
|
52
|
+
useHelper(editMode && isSelected ? spotLightRef : null, SpotLightHelper, color);
|
|
37
53
|
useEffect(() => {
|
|
38
54
|
if (spotLightRef.current && targetRef.current) {
|
|
39
55
|
spotLightRef.current.target = targetRef.current;
|
|
40
|
-
setSpotLight(spotLightRef.current);
|
|
41
56
|
}
|
|
42
57
|
}, []);
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
var _a;
|
|
60
|
+
const shadow = (_a = spotLightRef.current) === null || _a === void 0 ? void 0 : _a.shadow;
|
|
61
|
+
if (!shadow)
|
|
62
|
+
return;
|
|
63
|
+
shadow.needsUpdate = true;
|
|
64
|
+
shadow.camera.updateProjectionMatrix();
|
|
65
|
+
}, [castShadow, shadowMapSize, shadowBias, shadowNormalBias, shadowAutoUpdate, shadowCameraNear, shadowCameraFar]);
|
|
43
66
|
useFrame(() => {
|
|
44
|
-
|
|
45
|
-
|
|
67
|
+
var _a;
|
|
68
|
+
if ((_a = spotLightRef.current) === null || _a === void 0 ? void 0 : _a.target) {
|
|
69
|
+
spotLightRef.current.target.updateMatrixWorld();
|
|
46
70
|
}
|
|
47
71
|
});
|
|
48
|
-
return (_jsxs(_Fragment, { children: [_jsx("spotLight", { ref: spotLightRef, color: color, intensity: intensity, angle: angle, penumbra: penumbra, distance: distance, map: textureMap, castShadow: castShadow, "shadow-mapSize-width":
|
|
72
|
+
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] }));
|
|
49
73
|
}
|
|
50
74
|
const SpotLightComponent = {
|
|
51
75
|
name: 'SpotLight',
|
|
52
76
|
Editor: SpotLightComponentEditor,
|
|
53
77
|
View: SpotLightView,
|
|
54
|
-
defaultProperties:
|
|
78
|
+
defaultProperties: {},
|
|
55
79
|
getAssetRefs: (properties) => {
|
|
56
80
|
if (properties.map)
|
|
57
81
|
return [{ type: 'texture', path: properties.map }];
|
|
@@ -3,6 +3,7 @@ import TransformComponent from './TransformComponent';
|
|
|
3
3
|
import MaterialComponent from './MaterialComponent';
|
|
4
4
|
import PhysicsComponent from './PhysicsComponent';
|
|
5
5
|
import SpotLightComponent from './SpotLightComponent';
|
|
6
|
+
import PointLightComponent from './PointLightComponent';
|
|
6
7
|
import DirectionalLightComponent from './DirectionalLightComponent';
|
|
7
8
|
import AmbientLightComponent from './AmbientLightComponent';
|
|
8
9
|
import ModelComponent from './ModelComponent';
|
|
@@ -17,6 +18,7 @@ export default [
|
|
|
17
18
|
MaterialComponent,
|
|
18
19
|
PhysicsComponent,
|
|
19
20
|
SpotLightComponent,
|
|
21
|
+
PointLightComponent,
|
|
20
22
|
DirectionalLightComponent,
|
|
21
23
|
AmbientLightComponent,
|
|
22
24
|
ModelComponent,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export declare function mergeWithDefaults<T extends Record<string, any>>(defaults: T, properties?: Record<string, any> | null): T;
|
|
3
|
+
export declare function LightSection({ title, children }: {
|
|
4
|
+
title: string;
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export declare function ShadowBiasField({ name, label, values, onChange, fallback, }: {
|
|
8
|
+
name: string;
|
|
9
|
+
label: string;
|
|
10
|
+
values: Record<string, any>;
|
|
11
|
+
onChange: (values: Record<string, any>) => void;
|
|
12
|
+
fallback?: number;
|
|
13
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { colors } from '../styles';
|
|
4
|
+
import { FieldGroup, FieldRow, NumberInput } from './Input';
|
|
5
|
+
export function mergeWithDefaults(defaults, properties) {
|
|
6
|
+
const merged = Object.assign({}, defaults);
|
|
7
|
+
if (!properties) {
|
|
8
|
+
return merged;
|
|
9
|
+
}
|
|
10
|
+
for (const [key, value] of Object.entries(properties)) {
|
|
11
|
+
if (value !== undefined) {
|
|
12
|
+
merged[key] = value;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return merged;
|
|
16
|
+
}
|
|
17
|
+
export function LightSection({ title, children }) {
|
|
18
|
+
return (_jsxs("div", { style: {
|
|
19
|
+
display: 'flex',
|
|
20
|
+
flexDirection: 'column',
|
|
21
|
+
gap: 8,
|
|
22
|
+
padding: '8px 10px',
|
|
23
|
+
border: `1px solid ${colors.border}`,
|
|
24
|
+
borderRadius: 6,
|
|
25
|
+
background: colors.bgSurface,
|
|
26
|
+
}, children: [_jsx("div", { style: {
|
|
27
|
+
fontSize: 10,
|
|
28
|
+
textTransform: 'uppercase',
|
|
29
|
+
letterSpacing: '0.08em',
|
|
30
|
+
color: colors.textMuted,
|
|
31
|
+
fontWeight: 600,
|
|
32
|
+
}, children: title }), _jsx(FieldGroup, { children: children })] }));
|
|
33
|
+
}
|
|
34
|
+
const shadowBiasSteps = [0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001];
|
|
35
|
+
function getBiasStep(value) {
|
|
36
|
+
var _a;
|
|
37
|
+
const absValue = Math.abs(value);
|
|
38
|
+
if (absValue === 0) {
|
|
39
|
+
return 0.001;
|
|
40
|
+
}
|
|
41
|
+
return (_a = shadowBiasSteps.find(step => absValue >= step)) !== null && _a !== void 0 ? _a : shadowBiasSteps[shadowBiasSteps.length - 1];
|
|
42
|
+
}
|
|
43
|
+
function formatBiasStep(step) {
|
|
44
|
+
return step.toLocaleString('en-US', {
|
|
45
|
+
minimumFractionDigits: 0,
|
|
46
|
+
maximumFractionDigits: 6,
|
|
47
|
+
useGrouping: false,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
export function ShadowBiasField({ name, label, values, onChange, fallback = 0, }) {
|
|
51
|
+
var _a;
|
|
52
|
+
const value = (_a = values[name]) !== null && _a !== void 0 ? _a : fallback;
|
|
53
|
+
const [step, setStep] = useState(() => getBiasStep(value));
|
|
54
|
+
return (_jsx(FieldRow, { label: label, children: _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 6 }, children: [_jsx(NumberInput, { value: value, onChange: nextValue => onChange({ [name]: nextValue }), step: step, min: -0.1, max: 0.1, style: { width: 92 } }), _jsx("select", { value: step.toString(), onChange: event => setStep(Number(event.target.value)), style: {
|
|
55
|
+
width: 78,
|
|
56
|
+
backgroundColor: colors.bgInput,
|
|
57
|
+
border: `1px solid ${colors.border}`,
|
|
58
|
+
color: colors.text,
|
|
59
|
+
borderRadius: 3,
|
|
60
|
+
fontSize: 11,
|
|
61
|
+
padding: '3px 6px',
|
|
62
|
+
fontFamily: 'monospace',
|
|
63
|
+
}, title: "Bias scrub step", children: shadowBiasSteps.map(option => (_jsx("option", { value: option, children: formatBiasStep(option) }, option))) })] }) }));
|
|
64
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ComponentData, GameObject, Prefab } from './types';
|
|
2
|
+
export type PrefabNodeRecord = Omit<GameObject, 'children'>;
|
|
3
|
+
export type PrefabAssetRefCounts = Record<string, number>;
|
|
4
|
+
export interface PrefabState {
|
|
5
|
+
prefabId?: string;
|
|
6
|
+
prefabName?: string;
|
|
7
|
+
rootId: string;
|
|
8
|
+
nodesById: Record<string, PrefabNodeRecord>;
|
|
9
|
+
childIdsById: Record<string, string[]>;
|
|
10
|
+
parentIdById: Record<string, string | null>;
|
|
11
|
+
revision: number;
|
|
12
|
+
assetManifestKey: string;
|
|
13
|
+
assetRefCounts: PrefabAssetRefCounts;
|
|
14
|
+
}
|
|
15
|
+
export declare function createDefaultComponentProperties(type: string): Record<string, any>;
|
|
16
|
+
export declare function createComponentData(type: string, properties?: Record<string, any>): ComponentData;
|
|
17
|
+
export declare function createNode(name: string, components?: Record<string, {
|
|
18
|
+
type: string;
|
|
19
|
+
properties?: Record<string, any>;
|
|
20
|
+
}>, options?: {
|
|
21
|
+
id?: string;
|
|
22
|
+
children?: GameObject[];
|
|
23
|
+
}): GameObject;
|
|
24
|
+
export declare function createEmptyNode(name?: string): GameObject;
|
|
25
|
+
export declare function createEmptyPrefab(): Prefab;
|
|
26
|
+
export declare function createModelNode(filename: string, name?: string): GameObject;
|
|
27
|
+
export declare function createImageNode(texturePath: string, name?: string): GameObject;
|
|
28
|
+
export declare function normalizePrefab(prefab: Prefab, revision?: number): PrefabState;
|
|
29
|
+
export declare function createPrefabPatch(state: PrefabState, patch: Partial<PrefabState>, nextAssetRefCounts?: PrefabAssetRefCounts): Partial<PrefabState>;
|
|
30
|
+
export declare function denormalizePrefab(state: Pick<PrefabState, 'prefabId' | 'prefabName' | 'rootId' | 'nodesById' | 'childIdsById'>): Prefab;
|
|
31
|
+
export declare function collectSubtreeIds(id: string, childIdsById: Record<string, string[]>): string[];
|
|
32
|
+
export declare function insertSubtree(node: GameObject, parentId: string | null, nodesById: Record<string, PrefabNodeRecord>, childIdsById: Record<string, string[]>, parentIdById: Record<string, string | null>): void;
|
|
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;
|
|
34
|
+
export declare function isDescendant(id: string, potentialAncestorId: string, parentIdById: Record<string, string | null>): boolean;
|
|
35
|
+
export declare function updateAssetRefsForNodeChange(assetRefCounts: PrefabAssetRefCounts, currentNode: PrefabNodeRecord, nextNode: PrefabNodeRecord): PrefabAssetRefCounts;
|
|
36
|
+
export declare function collectSubtreeAssetRefs(node: GameObject): string[];
|
|
37
|
+
export declare function collectAssetRefsForIds(ids: string[], nodesById: Record<string, PrefabNodeRecord>): string[];
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { getComponentAssetRefs, getComponentDef } from './components/ComponentRegistry';
|
|
13
|
+
function clonePrefabValue(value) {
|
|
14
|
+
if (Array.isArray(value)) {
|
|
15
|
+
return value.map(item => clonePrefabValue(item));
|
|
16
|
+
}
|
|
17
|
+
if (value && typeof value === 'object') {
|
|
18
|
+
const clone = {};
|
|
19
|
+
Object.entries(value).forEach(([key, entry]) => {
|
|
20
|
+
clone[key] = clonePrefabValue(entry);
|
|
21
|
+
});
|
|
22
|
+
return clone;
|
|
23
|
+
}
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
function createComponentMap(components) {
|
|
27
|
+
const componentMap = {
|
|
28
|
+
transform: createComponentData('Transform'),
|
|
29
|
+
};
|
|
30
|
+
Object.entries(components).forEach(([key, component]) => {
|
|
31
|
+
componentMap[key] = createComponentData(component.type, component.properties);
|
|
32
|
+
});
|
|
33
|
+
return componentMap;
|
|
34
|
+
}
|
|
35
|
+
function getNodeNameFromPath(path, name) {
|
|
36
|
+
return name !== null && name !== void 0 ? name : path.replace(/^.*[\/]/, '').replace(/\.[^.]+$/, '');
|
|
37
|
+
}
|
|
38
|
+
function getAssetManifestKey(assetRefCounts) {
|
|
39
|
+
return Object.keys(assetRefCounts).sort().join('|');
|
|
40
|
+
}
|
|
41
|
+
function sameStringArrays(left, right) {
|
|
42
|
+
if (left.length !== right.length)
|
|
43
|
+
return false;
|
|
44
|
+
return left.every((value, index) => value === right[index]);
|
|
45
|
+
}
|
|
46
|
+
function getAssetRefs(node) {
|
|
47
|
+
var _a;
|
|
48
|
+
const refs = [];
|
|
49
|
+
Object.values((_a = node === null || node === void 0 ? void 0 : node.components) !== null && _a !== void 0 ? _a : {}).forEach(component => {
|
|
50
|
+
var _a;
|
|
51
|
+
if (!(component === null || component === void 0 ? void 0 : component.type))
|
|
52
|
+
return;
|
|
53
|
+
for (const ref of getComponentAssetRefs(component.type, (_a = component.properties) !== null && _a !== void 0 ? _a : {})) {
|
|
54
|
+
refs.push(`${ref.type}:${ref.path}`);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
return refs.sort();
|
|
58
|
+
}
|
|
59
|
+
function addAssetRefs(assetRefCounts, refs) {
|
|
60
|
+
refs.forEach(ref => {
|
|
61
|
+
var _a;
|
|
62
|
+
assetRefCounts[ref] = ((_a = assetRefCounts[ref]) !== null && _a !== void 0 ? _a : 0) + 1;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
function removeAssetRefs(assetRefCounts, refs) {
|
|
66
|
+
refs.forEach(ref => {
|
|
67
|
+
var _a;
|
|
68
|
+
const nextCount = ((_a = assetRefCounts[ref]) !== null && _a !== void 0 ? _a : 0) - 1;
|
|
69
|
+
if (nextCount > 0) {
|
|
70
|
+
assetRefCounts[ref] = nextCount;
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
delete assetRefCounts[ref];
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
function createAssetRefCounts(nodesById) {
|
|
77
|
+
const assetRefCounts = {};
|
|
78
|
+
Object.values(nodesById).forEach(node => addAssetRefs(assetRefCounts, getAssetRefs(node)));
|
|
79
|
+
return assetRefCounts;
|
|
80
|
+
}
|
|
81
|
+
function denormalizeNode(id, nodesById, childIdsById) {
|
|
82
|
+
var _a;
|
|
83
|
+
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)) });
|
|
85
|
+
}
|
|
86
|
+
export function createDefaultComponentProperties(type) {
|
|
87
|
+
var _a, _b;
|
|
88
|
+
return clonePrefabValue((_b = (_a = getComponentDef(type)) === null || _a === void 0 ? void 0 : _a.defaultProperties) !== null && _b !== void 0 ? _b : {});
|
|
89
|
+
}
|
|
90
|
+
export function createComponentData(type, properties) {
|
|
91
|
+
return {
|
|
92
|
+
type,
|
|
93
|
+
properties: properties ? clonePrefabValue(properties) : createDefaultComponentProperties(type),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
export function createNode(name, components = {}, options) {
|
|
97
|
+
var _a;
|
|
98
|
+
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
|
+
}
|
|
100
|
+
export function createEmptyNode(name = 'New Node') {
|
|
101
|
+
return createNode(name);
|
|
102
|
+
}
|
|
103
|
+
export function createEmptyPrefab() {
|
|
104
|
+
return {
|
|
105
|
+
id: crypto.randomUUID(),
|
|
106
|
+
name: 'New Prefab',
|
|
107
|
+
root: createNode('Root', {}, { id: crypto.randomUUID(), children: [] }),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
export function createModelNode(filename, name) {
|
|
111
|
+
return createNode(getNodeNameFromPath(filename, name), {
|
|
112
|
+
model: {
|
|
113
|
+
type: 'Model',
|
|
114
|
+
properties: {
|
|
115
|
+
filename,
|
|
116
|
+
instanced: false,
|
|
117
|
+
repeat: false,
|
|
118
|
+
repeatAxes: [{ axis: 'x', count: 1, offset: 1 }],
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
export function createImageNode(texturePath, name) {
|
|
124
|
+
return createNode(getNodeNameFromPath(texturePath, name), {
|
|
125
|
+
geometry: {
|
|
126
|
+
type: 'Geometry',
|
|
127
|
+
properties: { geometryType: 'plane', args: [1, 1] },
|
|
128
|
+
},
|
|
129
|
+
material: {
|
|
130
|
+
type: 'Material',
|
|
131
|
+
properties: { color: '#ffffff', texture: texturePath },
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
export function normalizePrefab(prefab, revision = 0) {
|
|
136
|
+
const nodesById = {};
|
|
137
|
+
const childIdsById = {};
|
|
138
|
+
const parentIdById = {};
|
|
139
|
+
insertSubtree(prefab.root, null, nodesById, childIdsById, parentIdById);
|
|
140
|
+
const assetRefCounts = createAssetRefCounts(nodesById);
|
|
141
|
+
return {
|
|
142
|
+
prefabId: prefab.id,
|
|
143
|
+
prefabName: prefab.name,
|
|
144
|
+
rootId: prefab.root.id,
|
|
145
|
+
nodesById,
|
|
146
|
+
childIdsById,
|
|
147
|
+
parentIdById,
|
|
148
|
+
revision,
|
|
149
|
+
assetManifestKey: getAssetManifestKey(assetRefCounts),
|
|
150
|
+
assetRefCounts,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
export function createPrefabPatch(state, patch, nextAssetRefCounts = state.assetRefCounts) {
|
|
154
|
+
const assetRefsChanged = nextAssetRefCounts !== state.assetRefCounts;
|
|
155
|
+
return Object.assign(Object.assign(Object.assign({}, patch), { revision: state.revision + 1 }), (assetRefsChanged ? {
|
|
156
|
+
assetRefCounts: nextAssetRefCounts,
|
|
157
|
+
assetManifestKey: getAssetManifestKey(nextAssetRefCounts),
|
|
158
|
+
} : null));
|
|
159
|
+
}
|
|
160
|
+
export function denormalizePrefab(state) {
|
|
161
|
+
return {
|
|
162
|
+
id: state.prefabId,
|
|
163
|
+
name: state.prefabName,
|
|
164
|
+
root: denormalizeNode(state.rootId, state.nodesById, state.childIdsById),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
export function collectSubtreeIds(id, childIdsById) {
|
|
168
|
+
var _a;
|
|
169
|
+
const ids = [id];
|
|
170
|
+
for (const childId of (_a = childIdsById[id]) !== null && _a !== void 0 ? _a : []) {
|
|
171
|
+
ids.push(...collectSubtreeIds(childId, childIdsById));
|
|
172
|
+
}
|
|
173
|
+
return ids;
|
|
174
|
+
}
|
|
175
|
+
export function insertSubtree(node, parentId, nodesById, childIdsById, parentIdById) {
|
|
176
|
+
var _a;
|
|
177
|
+
const { children } = node, nodeRecord = __rest(node, ["children"]);
|
|
178
|
+
nodesById[node.id] = nodeRecord;
|
|
179
|
+
childIdsById[node.id] = (_a = children === null || children === void 0 ? void 0 : children.map(child => child.id)) !== null && _a !== void 0 ? _a : [];
|
|
180
|
+
parentIdById[node.id] = parentId;
|
|
181
|
+
children === null || children === void 0 ? void 0 : children.forEach(child => insertSubtree(child, node.id, nodesById, childIdsById, parentIdById));
|
|
182
|
+
}
|
|
183
|
+
export function cloneSubtree(id, parentId, source, nodesById, childIdsById, parentIdById) {
|
|
184
|
+
var _a, _b;
|
|
185
|
+
const originalNode = source.nodesById[id];
|
|
186
|
+
if (!originalNode)
|
|
187
|
+
return null;
|
|
188
|
+
const clonedId = crypto.randomUUID();
|
|
189
|
+
const clonedNode = Object.assign(Object.assign({}, originalNode), { id: clonedId, name: `${(_a = originalNode.name) !== null && _a !== void 0 ? _a : originalNode.id} Copy` });
|
|
190
|
+
nodesById[clonedId] = clonedNode;
|
|
191
|
+
parentIdById[clonedId] = parentId;
|
|
192
|
+
const clonedChildIds = ((_b = source.childIdsById[id]) !== null && _b !== void 0 ? _b : [])
|
|
193
|
+
.map(childId => cloneSubtree(childId, clonedId, source, nodesById, childIdsById, parentIdById))
|
|
194
|
+
.filter((childId) => Boolean(childId));
|
|
195
|
+
childIdsById[clonedId] = clonedChildIds;
|
|
196
|
+
return clonedId;
|
|
197
|
+
}
|
|
198
|
+
export function isDescendant(id, potentialAncestorId, parentIdById) {
|
|
199
|
+
let currentId = id;
|
|
200
|
+
while (currentId) {
|
|
201
|
+
if (currentId === potentialAncestorId)
|
|
202
|
+
return true;
|
|
203
|
+
currentId = parentIdById[currentId];
|
|
204
|
+
}
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
export function updateAssetRefsForNodeChange(assetRefCounts, currentNode, nextNode) {
|
|
208
|
+
const currentRefs = getAssetRefs(currentNode);
|
|
209
|
+
const nextRefs = getAssetRefs(nextNode);
|
|
210
|
+
if (sameStringArrays(currentRefs, nextRefs)) {
|
|
211
|
+
return assetRefCounts;
|
|
212
|
+
}
|
|
213
|
+
const nextAssetRefCounts = Object.assign({}, assetRefCounts);
|
|
214
|
+
removeAssetRefs(nextAssetRefCounts, currentRefs);
|
|
215
|
+
addAssetRefs(nextAssetRefCounts, nextRefs);
|
|
216
|
+
return nextAssetRefCounts;
|
|
217
|
+
}
|
|
218
|
+
export function collectSubtreeAssetRefs(node) {
|
|
219
|
+
var _a;
|
|
220
|
+
const refs = getAssetRefs(node);
|
|
221
|
+
(_a = node.children) === null || _a === void 0 ? void 0 : _a.forEach(child => refs.push(...collectSubtreeAssetRefs(child)));
|
|
222
|
+
return refs;
|
|
223
|
+
}
|
|
224
|
+
export function collectAssetRefsForIds(ids, nodesById) {
|
|
225
|
+
return ids.reduce((refs, id) => {
|
|
226
|
+
refs.push(...getAssetRefs(nodesById[id]));
|
|
227
|
+
return refs;
|
|
228
|
+
}, []);
|
|
229
|
+
}
|
|
@@ -1,20 +1,8 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
2
|
import { type StoreApi } from "zustand/vanilla";
|
|
3
3
|
import { GameObject, Prefab } from "./types";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
type PrefabDocumentSnapshot = {
|
|
7
|
-
prefabId?: string;
|
|
8
|
-
prefabName?: string;
|
|
9
|
-
rootId: string;
|
|
10
|
-
nodesById: Record<string, PrefabNodeRecord>;
|
|
11
|
-
childIdsById: Record<string, string[]>;
|
|
12
|
-
parentIdById: Record<string, string | null>;
|
|
13
|
-
revision: number;
|
|
14
|
-
assetManifestKey: string;
|
|
15
|
-
assetRefCounts: PrefabAssetRefCounts;
|
|
16
|
-
};
|
|
17
|
-
export interface PrefabStoreState extends PrefabDocumentSnapshot {
|
|
4
|
+
import { denormalizePrefab, PrefabState, PrefabNodeRecord } from "./prefab";
|
|
5
|
+
export interface PrefabStoreState extends PrefabState {
|
|
18
6
|
replacePrefab: (prefab: Prefab) => void;
|
|
19
7
|
updateNode: (id: string, update: (node: PrefabNodeRecord) => PrefabNodeRecord) => void;
|
|
20
8
|
updateNodes: (updates: Array<{
|
|
@@ -39,5 +27,4 @@ export declare function usePrefabRootId(): string;
|
|
|
39
27
|
export declare function usePrefabNode(nodeId: string | null | undefined): PrefabNodeRecord | null;
|
|
40
28
|
export declare function usePrefabChildIds(nodeId: string | null | undefined): string[];
|
|
41
29
|
export declare function createPrefabStore(prefab: Prefab): PrefabStoreApi;
|
|
42
|
-
export declare
|
|
43
|
-
export {};
|
|
30
|
+
export declare const prefabStoreToPrefab: typeof denormalizePrefab;
|