// lib/props/composite.ts // Prop composites — assemble several mounted PropInstances into one group with // relative transforms (e.g. a lamp = post + bulb + light, or a tree cluster). // The composite owns its parts: one dispose() tears all of them down. import * as THREE from 'three' import type { PropInstance } from '../types.js' /** One prop's placement inside a {@link createPropComposite} group: local position, Euler rotation, and uniform or per-axis scale. */ export interface CompositePart { prop: PropInstance position?: [number, number, number] rotation?: [number, number, number] scale?: number | [number, number, number] } /** A group of placed prop instances. `dispose()` disposes every part. */ export interface PropComposite { object: THREE.Group parts: PropInstance[] dispose (): void } /** * Assemble several mounted props into one `Group`, applying each part's * position/rotation/scale. Useful for authoring compound set-pieces from * simple props. * * @param parts - Props with optional local transforms. * @returns A {@link PropComposite} whose `object` is ready to add to the scene. */ export function createPropComposite (parts: CompositePart[]): PropComposite { const group = new THREE.Group() const instances = parts.map(p => p.prop) for (const part of parts) { const obj = part.prop.object if (part.position) obj.position.fromArray(part.position) if (part.rotation) obj.rotation.fromArray(part.rotation as [number, number, number]) if (part.scale !== undefined) { if (typeof part.scale === 'number') obj.scale.setScalar(part.scale) else obj.scale.fromArray(part.scale) } group.add(obj) } return { object: group, parts: instances, dispose () { for (const inst of instances) inst.dispose() }, } } // perf: a composite is just a Group — no extra draw cost. Each part keeps its // own controller; disposing the composite disposes every part exactly once.