control/depthCamera/DepthCameraHandler.tstypescript
import * as math from "../../math";
import { Control, IControlParams } from "../Control";
import { DepthCamera } from "./DepthCamera";
import { depth_camera } from "./depth_camera";
import { Vector } from "../../layer";

const DEPTH_CAMERA_HANDLER_PREDRAW_PRIORITY = 0;
const POLYLINE_DEPTH_OFFSET = -100;

export interface IDepthCameraHandlerParams extends IControlParams {
    depthCameras?: DepthCamera[];
}

export class DepthCameraHandler extends Control {
    protected _depthCameras: DepthCamera[];

    public readonly cameraFootprintLayer: Vector;
    public readonly cameraFrustumLayer: Vector;

    constructor(params: IDepthCameraHandlerParams = {}) {
        super({
            name: "DepthCameraHandler",
            autoActivate: true,
            ...params
        });

        this._depthCameras = [];

        this.cameraFootprintLayer = new Vector(`depthCameraFootprintLayer`, {
            entities: [],
            pickingEnabled: false,
            receiveProjectors: false,
            receiveShadows: false,
            depthOffset: POLYLINE_DEPTH_OFFSET,
            hideInLayerSwitcher: true,
            clampToGround: true,
            visibility: true
        });

        this.cameraFrustumLayer = new Vector(`depthCameraFrustumLayer`, {
            entities: [],
            pickingEnabled: true,
            receiveProjectors: false,
            receiveShadows: false,
            shadeMode: "phong",
            hideInLayerSwitcher: true,
            scaleByDistance: [100, 1000000, math.MAX32]
        });

        if (params.depthCameras) {
            for (let i = 0; i < params.depthCameras.length; i++) {
                this.add(params.depthCameras[i]);
            }
        }
    }

    public get depthCameras(): DepthCamera[] {
        return [...this._depthCameras];
    }

    public add(depthCamera: DepthCamera): DepthCamera {
        if (depthCamera._handlerIndex !== -1) {
            return depthCamera;
        }

        depthCamera._handler = this;
        depthCamera._handlerIndex = this._depthCameras.length;
        this._depthCameras.push(depthCamera);
        this._addDepthCameraEntities(depthCamera);

        if (this.renderer?.isInitialized() && this.planet) {
            this._initDepthCamera(depthCamera);
        }

        return depthCamera;
    }

    public override remove(): void;
    public override remove(depthCamera: DepthCamera): boolean;
    public override remove(depthCamera?: DepthCamera): void | boolean {
        if (!depthCamera) {
            super.remove();
            return;
        }

        const index = depthCamera._handlerIndex;
        if (index === -1 || this._depthCameras[index] !== depthCamera) {
            return false;
        }

        this._removeDepthCameraEntities(depthCamera);
        depthCamera.destroy();

        const last = this._depthCameras.length - 1;
        if (index !== last) {
            const moved = this._depthCameras[last];
            this._depthCameras[index] = moved;
            moved._handlerIndex = index;
        }
        this._depthCameras.pop();

        depthCamera._handler = null;
        depthCamera._handlerIndex = -1;

        return true;
    }

    public clear(): void {
        while (this._depthCameras.length) {
            this.remove(this._depthCameras[this._depthCameras.length - 1]);
        }
    }

    public override oninit(): void {
        super.oninit();

        if (!this.renderer || !this.planet) return;

        this.renderer.handler.addProgram(depth_camera());

        this.planet.addLayer(this.cameraFootprintLayer);
        this.planet.addLayer(this.cameraFrustumLayer);

        for (let i = 0; i < this._depthCameras.length; i++) {
            this._initDepthCamera(this._depthCameras[i]);
        }
    }

    public override onactivate(): void {
        if (this.renderer) {
            this.renderer.events.on("predraw", this._onDraw, this, DEPTH_CAMERA_HANDLER_PREDRAW_PRIORITY);
        }
    }

    public override ondeactivate(): void {
        if (this.renderer) {
            this.renderer.events.off("predraw", this._onDraw);
        }
    }

    public override onremove(): void {
        this.clear();
        this.cameraFootprintLayer.remove();
        this.cameraFrustumLayer.remove();
    }

    protected _initDepthCamera(depthCamera: DepthCamera): void {
        if (!this.renderer || !this.planet || depthCamera.initialized) return;

        depthCamera.init(this.planet, this.renderer);
    }

    protected _addDepthCameraEntities(depthCamera: DepthCamera): void {
        const cameraFootprintEntity = depthCamera.cameraFootprintEntity;
        if (depthCamera.showFootprint && cameraFootprintEntity) {
            this.cameraFootprintLayer.add(cameraFootprintEntity);
        }

        const cameraFrustumEntity = depthCamera.cameraFrustumEntity;
        if (depthCamera.showFrustum && cameraFrustumEntity) {
            this.cameraFrustumLayer.add(cameraFrustumEntity);
        }
    }

    protected _removeDepthCameraEntities(depthCamera: DepthCamera): void {
        const cameraFootprintEntity = depthCamera.cameraFootprintEntity;
        if (cameraFootprintEntity) {
            this.cameraFootprintLayer.removeEntity(cameraFootprintEntity);
        }

        const cameraFrustumEntity = depthCamera.cameraFrustumEntity;
        if (cameraFrustumEntity) {
            this.cameraFrustumLayer.removeEntity(cameraFrustumEntity);
        }
    }

    protected _onDraw = (): void => {
        if (!this.renderer || !this.planet) return;

        for (let i = 0; i < this._depthCameras.length; i++) {
            this._depthCameras[i].frame();
        }
    };
}