"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.BackgroundMapDrape = void 0;
/** @packageDocumentation
 * @module WebGL
 */
const core_bentley_1 = require("@itwin/core-bentley");
const core_geometry_1 = require("@itwin/core-geometry");
const core_common_1 = require("@itwin/core-common");
const internal_1 = require("../../tile/internal");
const FeatureSymbology_1 = require("../FeatureSymbology");
const BatchState_1 = require("./BatchState");
const BranchStack_1 = require("./BranchStack");
const FrameBuffer_1 = require("./FrameBuffer");
const GL_1 = require("./GL");
const PlanarTextureProjection_1 = require("./PlanarTextureProjection");
const RenderCommands_1 = require("./RenderCommands");
const System_1 = require("./System");
const Texture_1 = require("./Texture");
const TextureDrape_1 = require("./TextureDrape");
/** @internal */
class BackgroundMapDrape extends TextureDrape_1.TextureDrape {
    constructor(drapedTree, mapTree) {
        super();
        this._graphics = [];
        this._width = 0;
        this._height = 0;
        this._debugFrustumGraphic = undefined;
        this._symbologyOverrides = new FeatureSymbology_1.FeatureSymbology.Overrides();
        this._bgColor = core_common_1.ColorDef.from(0, 0, 0, 255);
        this._plane = core_geometry_1.Plane3dByOriginAndUnitNormal.create(core_geometry_1.Point3d.createZero(), core_geometry_1.Vector3d.create(0, 0, 1));
        this._drapedTree = drapedTree;
        this._mapTree = mapTree;
    }
    get isDisposed() { return super.isDisposed && undefined === this._fbo; }
    dispose() {
        super.dispose();
        this._fbo = (0, core_bentley_1.dispose)(this._fbo);
    }
    addGraphic(graphic) {
        this._graphics.push(graphic);
    }
    static create(draped, map) {
        return new BackgroundMapDrape(draped, map);
    }
    collectGraphics(context) {
        this._graphics.length = 0;
        if (undefined === context.viewingSpace)
            return;
        const viewState = context.viewingSpace.view;
        if (undefined === viewState)
            return;
        const tileTree = this._mapTree.treeOwner.load();
        if (undefined === tileTree || !this._mapTree.initializeLayers(context))
            return;
        const requiredWidth = 2 * Math.max(context.target.viewRect.width, context.target.viewRect.height); // TBD - Size to textured area.
        const requiredHeight = requiredWidth;
        if (requiredWidth !== this._width || requiredHeight !== this._height)
            this.dispose();
        this._width = requiredWidth;
        this._height = requiredHeight;
        const targetTree = this._drapedTree.treeOwner.tileTree;
        const args = this._drapedTree.createDrawArgs(context);
        if (!targetTree || !args)
            return;
        const targetTiles = targetTree.selectTiles(args);
        const projection = PlanarTextureProjection_1.PlanarTextureProjection.computePlanarTextureProjection(this._plane, context, { tiles: targetTiles, location: args.location }, [this._mapTree], viewState, this._width, this._height, core_geometry_1.Range3d.createNull());
        if (!projection.textureFrustum || !projection.projectionMatrix || !projection.worldToViewMap)
            return;
        this._frustum = projection.textureFrustum;
        this._debugFrustum = projection.debugFrustum;
        this._projectionMatrix = projection.projectionMatrix;
        const drawArgs = internal_1.GraphicsCollectorDrawArgs.create(context, this, this._mapTree, core_common_1.FrustumPlanes.fromFrustum(this._frustum), projection.worldToViewMap);
        if (undefined !== drawArgs)
            tileTree.draw(drawArgs);
        if (context.target.debugControl && context.target.debugControl.displayDrapeFrustum) {
            this._debugFrustumGraphic = (0, core_bentley_1.dispose)(this._debugFrustumGraphic);
            const builder = context.createSceneGraphicBuilder();
            builder.setSymbology(core_common_1.ColorDef.green, core_common_1.ColorDef.green, 1);
            builder.addFrustum(context.viewingSpace.getFrustum());
            builder.setSymbology(core_common_1.ColorDef.red, core_common_1.ColorDef.red, 1);
            builder.addFrustum(this._debugFrustum);
            builder.setSymbology(core_common_1.ColorDef.white, core_common_1.ColorDef.white, 1);
            builder.addFrustum(this._frustum);
            this._debugFrustumGraphic = builder.finish();
        }
    }
    draw(target) {
        if (undefined !== this._debugFrustumGraphic)
            target.graphics.foreground.push(this._debugFrustumGraphic);
        if (undefined === this._frustum || this._graphics.length === 0)
            return;
        if (undefined === this._fbo) {
            const colorTextureHandle = Texture_1.TextureHandle.createForAttachment(this._width, this._height, GL_1.GL.Texture.Format.Rgba, GL_1.GL.Texture.DataType.UnsignedByte);
            if (undefined === colorTextureHandle) {
                (0, core_bentley_1.assert)(false, "Failed to create planar texture");
                return;
            }
            this._texture = new Texture_1.Texture({ ownership: "external", type: core_common_1.RenderTexture.Type.TileSection, handle: colorTextureHandle, transparency: core_common_1.TextureTransparency.Opaque });
            this._fbo = FrameBuffer_1.FrameBuffer.create([colorTextureHandle]);
        }
        if (undefined === this._fbo) {
            (0, core_bentley_1.assert)(false, "unable to create frame buffer object");
            return;
        }
        System_1.System.instance.glTimer.beginOperation("Terrain Projection");
        const prevState = System_1.System.instance.currentRenderState.clone();
        System_1.System.instance.context.viewport(0, 0, this._width, this._height);
        const prevPlan = target.plan;
        const drawingParams = PlanarTextureProjection_1.PlanarTextureProjection.getTextureDrawingParams(target);
        const stack = new BranchStack_1.BranchStack();
        stack.changeRenderPlan(drawingParams.viewFlags, prevPlan.is3d, prevPlan.hline);
        stack.setSymbologyOverrides(this._symbologyOverrides);
        const batchState = new BatchState_1.BatchState(stack);
        System_1.System.instance.applyRenderState(drawingParams.state);
        target.uniforms.style.changeBackgroundColor(this._bgColor); // Avoid white on white reversal. Will be reset below in changeRenderPlan().
        target.changeFrustum(this._frustum, this._frustum.getFraction(), true);
        const prevProjMatrix = target.uniforms.frustum.projectionMatrix;
        target.uniforms.frustum.changeProjectionMatrix(BackgroundMapDrape._postProjectionMatrix.multiplyMatrixMatrix(prevProjMatrix));
        target.uniforms.branch.pushState(stack.top);
        const renderCommands = new RenderCommands_1.RenderCommands(target, stack, batchState);
        renderCommands.addGraphics(this._graphics, 5 /* RenderPass.OpaqueGeneral */);
        const system = System_1.System.instance;
        const gl = system.context;
        system.frameBufferStack.execute(this._fbo, true, false, () => {
            gl.clearColor(0, 0, 0, 0);
            gl.clear(GL_1.GL.BufferBit.Color);
            target.techniques.execute(target, renderCommands.getCommands(5 /* RenderPass.OpaqueGeneral */), 19 /* RenderPass.PlanarClassification */); // Draw these with RenderPass.PlanarClassification (rather than Opaque...) so that the pick ordering is avoided.
        });
        target.uniforms.branch.pop();
        batchState.reset(); // Reset the batch Ids...
        target.changeRenderPlan(prevPlan);
        system.applyRenderState(prevState);
        gl.viewport(0, 0, target.viewRect.width, target.viewRect.height); // Restore viewport
        system.glTimer.endOperation();
    }
}
exports.BackgroundMapDrape = BackgroundMapDrape;
BackgroundMapDrape._postProjectionMatrix = core_geometry_1.Matrix4d.createRowValues(0, 1, 0, 0, 0, 0, -1, 0, 1, 0, 0, 0, 0, 0, 0, 1);
//# sourceMappingURL=BackgroundMapDrape.js.map