import { Object3D } from "./Object3D"; import { SplatData } from "../splats/SplatData"; import { Splat } from "../splats/Splat"; import { EventDispatcher } from "../events/EventDispatcher"; import { ObjectAddedEvent, ObjectRemovedEvent } from "../events/Events"; import { Converter } from "../utils/Converter"; class Scene extends EventDispatcher { private _objects: Object3D[] = []; addObject: (object: Object3D) => void; removeObject: (object: Object3D) => void; findObject: (predicate: (object: Object3D) => boolean) => Object3D | undefined; findObjectOfType: (type: { new (): T }) => T | undefined; reset: () => void; constructor() { super(); this.addObject = (object: Object3D) => { this.objects.push(object); this.dispatchEvent(new ObjectAddedEvent(object)); }; this.removeObject = (object: Object3D) => { const index = this.objects.indexOf(object); if (index < 0) { throw new Error("Object not found in scene"); } this.objects.splice(index, 1); this.dispatchEvent(new ObjectRemovedEvent(object)); }; this.findObject = (predicate: (object: Object3D) => boolean) => { for (const object of this.objects) { if (predicate(object)) { return object; } } return undefined; }; this.findObjectOfType = (type: { new (): T }) => { for (const object of this.objects) { if (object instanceof type) { return object; } } return undefined; }; this.reset = () => { const objectsToRemove = this.objects.slice(); for (const object of objectsToRemove) { this.removeObject(object); } }; this.reset(); } getMergedSceneDataBuffer(format: "splat" | "ply" = "splat"): ArrayBuffer { const buffers: Uint8Array[] = []; let vertexCount = 0; for (const object of this.objects) { if (object instanceof Splat) { const splatClone = object.clone() as Splat; splatClone.applyRotation(); splatClone.applyScale(); splatClone.applyPosition(); const buffer = splatClone.data.serialize(); buffers.push(buffer); vertexCount += splatClone.data.vertexCount; } } const mergedSplatData = new Uint8Array(vertexCount * SplatData.RowLength); let offset = 0; for (const buffer of buffers) { mergedSplatData.set(buffer, offset); offset += buffer.length; } if (format === "ply") { return Converter.SplatToPLY(mergedSplatData.buffer, vertexCount); } return mergedSplatData.buffer; } saveToFile(name: string | null = null, format: "splat" | "ply" = "splat") { if (!document) return; if (!name) { const now = new Date(); name = `scene-${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}.${format}`; } const mergedData = this.getMergedSceneDataBuffer(format); const blob = new Blob([mergedData], { type: "application/octet-stream" }); const link = document.createElement("a"); link.download = name; link.href = URL.createObjectURL(blob); link.click(); } get objects() { return this._objects; } } export { Scene };