Spaces:
Running
Running
| 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: <T extends Object3D>(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 = <T extends Object3D>(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 }; | |