import { ComponentEvents, __internalAddComponentLifecycleEventListener, __internalRemoveComponentLifecycleEventListener } from "@needle-tools/engine/src/engine/engine_components_internal"; import { IComponent } from "@needle-tools/engine/src/engine/engine_types"; import { $originalGuid } from "@needle-tools/engine/src/engine/engine_gltf_builtin_components"; import { debug } from "./common"; import { Material, Mesh } from "three"; import { $editorKey } from "./types"; import { NEEDLE_editor } from "./NEEDLE_editor"; import { notifySceneChanged } from "./live_sync"; export enum RegistryEvent { Added = "added", } export class Registry { static map: Map = new Map(); static get(id: string): object[] | undefined { return Registry.map.get(id); } static set(id: string, arr: object[]) { Registry.map.set(id, arr); } static add(id: string, obj: object) { if (id === undefined || id === null || id === "") { if (debug) console.warn("unknown id", obj); return; } id = getId(id); let instances = Registry.map.get(id) as object[]; if (!instances) { instances = []; Registry.map.set(id, instances); } instances.push(obj); this.addClones(id, obj, instances); Registry.dispatchEvent(RegistryEvent.Added); } static remove(id: string, comp: IComponent) { id = getId(id); const instances = Registry.map.get(id) as object[]; if (!instances) return; const index = instances.indexOf(comp); if (index < 0) return; instances.splice(index, 1); } private static addClones(id: string, obj: object, array: object[]) { const clones = clonedObjects.get(obj); if (clones) { for (const clone of clones) { clone[$editorKey] = id; array.push(clone); } clonedObjects.delete(obj); } } private static _eventListeners: Map = new Map(); static addEventListener(key: RegistryEvent, fn: Function) { let arr = this._eventListeners.get(key); if (!arr) { arr = []; this._eventListeners.set(key, arr); } arr.push(fn); } private static dispatchEvent(evt: string) { const arr = this._eventListeners.get(evt as RegistryEvent); if (!arr) return; for (const fn of arr) { fn(); } } } __internalAddComponentLifecycleEventListener(ComponentEvents.Added, comp => { if (!comp) return; Registry.add(comp[$originalGuid], comp); }) __internalAddComponentLifecycleEventListener(ComponentEvents.Removing, comp => { if (!comp) return; Registry.remove(comp[$originalGuid], comp); }) function getId(id: string): string { // component ids may end with instance ids // e.g. 3896570108830029497_543534543534636356565 // this may currently happen on export in e.g. unity because prefabs are instantiated in the scene once // the following is a hack to remove the instance guid that we dont have when we edit a component in unity in prefab mode // so the id becomes 3896570108830029497 then // Update: the above comment is invalid since we now get the component from the original prefab // if (typeof id === "string" && id.length > 19 && id[19] === "_") { // return id.substring(0, 19); // } return id; } const clonedObjects = new WeakMap(); function wrapClone(prototype) { const originalClone = prototype.clone; Object.defineProperty(prototype, "clone", { value: function (this: object) { const clone = originalClone.call(this); if (this[$editorKey]) { const key = this[$editorKey]; clone[$editorKey] = key; Registry.add(key, clone); notifySceneChanged(); } else if (NEEDLE_editor.isLoading) { const arr = clonedObjects.get(this) || []; arr.push(clone); clonedObjects.set(this, arr); } else { } return clone; } }); } wrapClone(Material.prototype); const $materialProperty = Symbol("materialProperty"); Object.defineProperty(Mesh.prototype, "material", { set: function (this: Mesh, value: Material | Material[]) { this[$materialProperty] = value; }, get: function (this: Mesh) { return this[$materialProperty]; } });