Scene hierarchy: parentId with local positions, tree panel, reparenting and Parent node

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
rootandClaude Fable 5.1 committed 2026-09-07 15:04:56 +02:00
1 parent 551a50ffc9
commit 26d5edc496
22 files changed
+729 -166

No files matched your search

+20 -3
View File
@@ -1,3 +1,4 @@
import { worldPositionOf } from '../core/graph';
import { findAsset, findScene, type Project } from '../core/model/Project';
import { resolveSceneObjectProps } from '../core/properties/resolve';
import type { Entity } from './Entity';
@@ -27,10 +28,11 @@ export class World {
if (!scene) throw new Error(`Scene ${sceneId} not found`);
const background = scene.props.backgroundColor;
const world = new World(sceneId, typeof background === 'number' ? background : 0x000000);
const parentIds = new Map<Entity, string>();
for (const object of scene.objects) {
const asset = findAsset(project, object.assetId);
if (!asset || asset.type === 'scene') continue;
world.entities.push({
const entity: Entity = {
id: `e-${object.id}`,
objectId: object.id,
assetId: asset.id,
@@ -40,12 +42,25 @@ export class World {
y: object.position.y,
z: object.position.z,
props: resolveSceneObjectProps(asset, object),
ownerObjectId: object.ownerId,
});
parent: null,
};
world.entities.push(entity);
if (object.parentId) parentIds.set(entity, object.parentId);
}
// Link parents once every entity exists (storage order is not guaranteed to be parent-first).
for (const [entity, parentId] of parentIds) entity.parent = world.findByObjectId(parentId) ?? null;
return world;
}
/** World position of an entity: local position plus those of all ancestors. */
worldPosition(entity: Entity): { x: number; y: number; z: number } {
return worldPositionOf(entity);
}
childrenOf(entity: Entity): Entity[] {
return this.entities.filter((e) => e.parent === entity);
}
addSystem(system: System): this {
this.systems.push(system);
system.start?.(this);
@@ -74,7 +89,9 @@ export class World {
return this.entities.find((e) => e.type === type);
}
/** Removes an entity and, recursively, everything parented to it. */
remove(entity: Entity): void {
for (const child of this.childrenOf(entity)) this.remove(child);
const index = this.entities.indexOf(entity);
if (index >= 0) this.entities.splice(index, 1);
}