Resolve container content from the template instead of copying it
A scene now stores only the container object; its content is resolved from the template on every read, with deviations kept as sparse instanceEdits on the container object. This drops the propagation bookkeeping entirely -- templateSync.ts is gone and the three structural gaps (add, remove, reparent) cannot diverge any more. Values stay editable on an instance, structure does not; Unpack writes the content into the scene, keeping ids. Play mode bakes containers into the throwaway copy before entities are built. No migration: formatVersion is 2 and the seeders write the new format. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9d93020bd8
commit
ead649eec3
37 files changed
+1878
-1006
No files matched your search
@@ -2,6 +2,7 @@
|
||||
import { createEmptyGraph, type Graph, type GraphEdge, type GraphNode, nodeDef } from '../graph';
|
||||
import type { Asset, TextureFrame } from '../model/Asset';
|
||||
import { isAssetType } from '../model/AssetTypes';
|
||||
import { emptyEdit, type InstanceEdit, isEmptyEdit } from '../model/instance';
|
||||
import type { PropertyBag, PropertyValue } from '../model/Properties';
|
||||
import { PROJECT_FORMAT_VERSION, type Project } from '../model/Project';
|
||||
import type { SceneObject } from '../model/SceneObject';
|
||||
@@ -108,7 +109,7 @@ function normalizeObjects(raw: unknown, schemas: Map<string, PropertySchema>): S
|
||||
overrides: normalizeBag(o.overrides, schema, true),
|
||||
// `ownerId` is the pre-hierarchy name of the same link.
|
||||
parentId: typeof o.parentId === 'string' ? o.parentId : typeof o.ownerId === 'string' ? o.ownerId : '',
|
||||
templateId: typeof o.templateId === 'string' ? o.templateId : '',
|
||||
instanceEdits: normalizeInstanceEdits(o.instanceEdits),
|
||||
});
|
||||
}
|
||||
// Parents must exist in the same scene, differ from the object itself and not form cycles.
|
||||
@@ -120,6 +121,32 @@ function normalizeObjects(raw: unknown, schemas: Map<string, PropertySchema>): S
|
||||
return objects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deviations of container content, keyed by template path. Values are not coerced against a schema:
|
||||
* which asset a path leads to depends on container assets that may not be built yet, and resolving
|
||||
* drops what the schema does not declare anyway.
|
||||
*/
|
||||
function normalizeInstanceEdits(raw: unknown): Record<string, InstanceEdit> {
|
||||
const edits: Record<string, InstanceEdit> = {};
|
||||
for (const [path, value] of Object.entries(asRecord(raw))) {
|
||||
if (!path) continue;
|
||||
const e = asRecord(value);
|
||||
const edit = emptyEdit();
|
||||
if (typeof e.name === 'string') edit.name = e.name;
|
||||
const position = asRecord(e.position);
|
||||
for (const axis of ['x', 'y', 'z'] as const)
|
||||
if (typeof position[axis] === 'number' && Number.isFinite(position[axis]))
|
||||
edit.position[axis] = position[axis];
|
||||
const offset = asRecord(e.editorOffset);
|
||||
if (typeof offset.x === 'number' || typeof offset.y === 'number')
|
||||
edit.editorOffset = { x: toNumber(offset.x), y: toNumber(offset.y) };
|
||||
for (const [key, v] of Object.entries(asRecord(e.overrides)))
|
||||
if (typeof v === 'number' || typeof v === 'string' || typeof v === 'boolean') edit.overrides[key] = v;
|
||||
if (!isEmptyEdit(edit)) edits[path] = edit;
|
||||
}
|
||||
return edits;
|
||||
}
|
||||
|
||||
function normalizeVariables(raw: unknown): PropertyDef[] {
|
||||
const defs: PropertyDef[] = [];
|
||||
const seen = new Set<string>();
|
||||
|
||||
Reference in new issue
Block a user