import { Entity, EntityManager, IEntity } from "./entity"; import { Camera } from "./camera"; import { RenderObject } from "./render-object"; import { Light } from "./light"; import { EventDefinitions, EventEmitter, IEventSource, EventKeys } from "zogra-renderer"; import { ConstructorType } from "../utils/util"; import { IAsset, AssetManager } from "zogra-renderer"; import { IPhysicsSystem, IPhysicsSystemClass, UnknownPhysics } from "../physics/physics-generic"; import { Physics2D } from "../2d/physics/physics-2d"; import { Time, ZograEngine } from "./engine"; interface SceneEvents extends EventDefinitions { "entity-add": (entity: Entity, parent: Entity | null) => void; "entity-remove": (entity: Entity, parent: Entity | null) => void; } export class Scene extends EntityManager implements IAsset, IEventSource { readonly assetID: number; name: string; physics: Physics; //private managers = new Map(); /** @internal */ engine: ZograEngine | undefined = undefined; private eventEmitter = new EventEmitter(); private addsNextFrame: Map = new Map(); private removesNextFrame: Set = new Set(); constructor(PhysicsSystem: ConstructorType) { super(); this.assetID = AssetManager.newAssetID(this); this.name = `Scene_${this.assetID}`; this.physics = new PhysicsSystem(); } add(entity: Entity, parent: Entity | null = null) { if (entity.destroyed) { console.error("Attempt to add destroyed entity"); return; } this.addsNextFrame.set(entity, parent); for (const child of entity.children) this.add(child as Entity, entity); } remove(entity: Entity) { this.removesNextFrame.add(entity); } rootEntities() { return this._entities.filter(entity => entity.parent === null); } getEntities() { return this._entities; } getEntitiesOfType(type: ConstructorType): T[] { return this.entities.filter(entity => entity instanceof type) as any as T[]; // return (this.managers.get(type)?.entities ?? []) as any as T[]; } withPhysics(physics: Physics) { (this as unknown as Scene).physics = physics; return (this as unknown as Scene); } on>(event: T, listener: SceneEvents[T]) { this.eventEmitter.on(event, listener); } off>(event: T, listener: SceneEvents[T]) { this.eventEmitter.off(event, listener); } clearAll() { const time = this.engine?.time ?? { time: 0, deltaTime: 0 }; for (const entity of this._entities) { entity.destroy(); } this.addsNextFrame.clear(); this.removesNextFrame.clear(); this.removePendingEntites(time); this._entities = []; this.entityMap.clear(); } destroy(): void { this.clearAll(); } /** @internal */ __update(time: Time) { this.addPendingEntities(time); this.removePendingEntites(time); const entities = this.rootEntities(); for (const entity of entities) entity.__updateRecursive(time); this.physics?.update(time); } private addPendingEntities(time: Time) { const adds = this.addsNextFrame; this.addsNextFrame = new Map(); for (const [entity, parent] of adds) { super.add(entity); entity.__addToScene(this); const type = entity.constructor; // if (!this.managers.has(type)) // this.managers.set(type, new EntityManager()); // this.managers.get(type)?.add(entity); if (parent) entity.parent = parent; this.eventEmitter.emit("entity-add", entity, parent ? parent : null); } for (const [entity, _] of adds) entity.__start(time); } private removePendingEntites(time: Time) { const removes = this.removesNextFrame; this.removesNextFrame = new Set(); for (const entity of removes) { super.remove(entity); entity.__removeFromScene(this); this.eventEmitter.emit("entity-remove", entity, entity.parent as Entity); } for (const entity of removes) entity.__exit(time); } }