"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module WebGL
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.BranchUniforms = void 0;
const core_bentley_1 = require("@itwin/core-bentley");
const core_geometry_1 = require("@itwin/core-geometry");
const BranchStack_1 = require("./BranchStack");
const BatchState_1 = require("./BatchState");
const Matrix_1 = require("./Matrix");
const RenderCommands_1 = require("./RenderCommands");
const Sync_1 = require("./Sync");
const ClipStack_1 = require("./ClipStack");
const IModelApp_1 = require("../../../IModelApp");
function equalXYZs(a, b) {
    if (a === b)
        return true;
    if ((undefined === a) !== (undefined === b))
        return false;
    if (undefined !== a && undefined !== b)
        return a.isExactEqual(b);
    (0, core_bentley_1.assert)(undefined === a && undefined === b);
    return true;
}
/** Maintains uniform variable state associated with the Branch currently being drawn by a Target.
 * @internal
 */
class BranchUniforms {
    clipStack;
    _viewClipEnabled = false;
    // The model-view and model-view-projection matrices depend on the frustum.
    syncToken;
    syncKey = 0;
    _stack = new BranchStack_1.BranchStack();
    _target;
    // Parameters that affect synchronization.
    _isInstanced = false;
    _viewIndependentOrigin;
    // CPU state
    _mv = core_geometry_1.Matrix4d.createIdentity();
    _mvp = core_geometry_1.Matrix4d.createIdentity();
    // GPU state
    _mv32 = new Matrix_1.Matrix4();
    _mvp32 = new Matrix_1.Matrix4();
    _m32 = new Matrix_1.Matrix4();
    _v32 = new Matrix_1.Matrix3();
    // Working state
    _scratchTransform = core_geometry_1.Transform.createIdentity();
    _scratchTransform2 = core_geometry_1.Transform.createIdentity();
    _scratchViewToWorld = core_geometry_1.Matrix3d.createIdentity();
    _scratchVIModelMatrix = core_geometry_1.Transform.createIdentity();
    _zeroPoint = new core_geometry_1.Point3d(0, 0, 0);
    get stack() { return this._stack; }
    constructor(target) {
        this._target = target;
        this.clipStack = new ClipStack_1.ClipStack(() => target.uniforms.frustum.viewMatrix, () => this._viewClipEnabled && this.top.viewFlags.clipVolume);
    }
    createBatchState() {
        return new BatchState_1.BatchState(this._stack);
    }
    createRenderCommands(batchState) {
        return new RenderCommands_1.RenderCommands(this._target, this._stack, batchState);
    }
    get modelViewMatrix() {
        return this._mv;
    }
    get top() {
        return this._stack.top;
    }
    get length() {
        return this._stack.length;
    }
    pushBranch(branch) {
        (0, Sync_1.desync)(this);
        this._stack.pushBranch(branch);
        this.setClipStyle(this.top.disableClipStyle);
        if (this.top.clipVolume)
            this.clipStack.push(this.top.clipVolume);
        if (branch.branch.realityModelDisplaySettings)
            this._target.uniforms.realityModel.update(branch.branch.realityModelDisplaySettings);
    }
    pushState(state) {
        (0, Sync_1.desync)(this);
        this._stack.pushState(state);
        if (this.top.clipVolume)
            this.clipStack.push(this.top.clipVolume);
        if (state.realityModelDisplaySettings)
            this._target.uniforms.realityModel.update(state.realityModelDisplaySettings);
    }
    pop() {
        (0, Sync_1.desync)(this);
        if (this.top.clipVolume)
            this.clipStack.pop();
        this._stack.pop();
        this.setClipStyle(this.top.disableClipStyle);
    }
    pushViewClip() {
        (0, core_bentley_1.assert)(!this._viewClipEnabled);
        this._viewClipEnabled = true;
        // Target.readPixels() pushes another BranchState before pushing view clip...
        (0, core_bentley_1.assert)((this._target.isReadPixelsInProgress ? 2 : 1) === this._stack.length);
    }
    popViewClip() {
        (0, core_bentley_1.assert)(this._viewClipEnabled);
        this._viewClipEnabled = false;
        (0, core_bentley_1.assert)((this._target.isReadPixelsInProgress ? 2 : 1) === this._stack.length);
    }
    changeRenderPlan(vf, is3d, hline, contourLine) {
        this._stack.changeRenderPlan(vf, is3d, hline, contourLine);
    }
    updateViewClip(clip, style) {
        this.clipStack.setViewClip(clip, style);
    }
    overrideFeatureSymbology(ovr) {
        this._stack.setSymbologyOverrides(ovr);
    }
    bindModelViewMatrix(uniform, geom, isViewCoords) {
        if (this.update(uniform, geom, isViewCoords))
            uniform.setMatrix4(this._mv32);
    }
    bindModelViewProjectionMatrix(uniform, geom, isViewCoords) {
        if (this.update(uniform, geom, isViewCoords))
            uniform.setMatrix4(this._mvp32);
    }
    bindModelToWorldTransform(uniform, geom, isViewCoords) {
        if (this.update(uniform, geom, isViewCoords))
            uniform.setMatrix4(this._m32);
    }
    bindWorldToViewNTransform(uniform, geom, isViewCoords) {
        if (this.update(uniform, geom, isViewCoords))
            uniform.setMatrix3(this._v32);
    }
    update(uniform, geometry, isViewCoords) {
        const uniforms = this._target.uniforms[isViewCoords ? "viewRect" : "frustum"];
        if (!(0, Sync_1.sync)(uniforms, this))
            (0, Sync_1.desync)(this);
        const instancedGeom = geometry.asInstanced;
        if (undefined !== instancedGeom || this._isInstanced) {
            this._isInstanced = undefined !== instancedGeom;
            (0, Sync_1.desync)(this);
        }
        const vio = geometry.viewIndependentOrigin;
        if (!equalXYZs(vio, this._viewIndependentOrigin)) {
            this._viewIndependentOrigin = vio;
            (0, Sync_1.desync)(this);
        }
        if ((0, Sync_1.sync)(this, uniform))
            return false;
        let mv;
        const modelMatrix = this._target.currentTransform;
        if (isViewCoords) {
            // Zero out Z for silly clipping tools...
            mv = modelMatrix.clone(this._scratchTransform);
            mv.matrix.coffs[2] = mv.matrix.coffs[5] = mv.matrix.coffs[8] = 0.0;
            if (instancedGeom)
                mv = instancedGeom.getRtcModelTransform(mv);
            // Scale based on device-pixel ratio.
            const scale = this._target.devicePixelRatio;
            const viewMatrix = core_geometry_1.Transform.createScaleAboutPoint(this._zeroPoint, scale, this._scratchTransform2);
            viewMatrix.multiplyTransformTransform(mv, mv);
        }
        else {
            const viewMatrix = this._target.uniforms.frustum.viewMatrix;
            if (undefined !== instancedGeom) {
                // For instanced geometry, the "model view" matrix is really a transform from center of instanced geometry range to view.
                // Shader will compute final model-view matrix based on this and the per-instance transform.
                if (vio) {
                    const viewToWorldRot = (0, core_bentley_1.expectDefined)(viewMatrix.matrix.inverse(this._scratchViewToWorld));
                    const rotateAboutOrigin = core_geometry_1.Transform.createFixedPointAndMatrix(vio, viewToWorldRot, this._scratchTransform2);
                    const viModelMatrix = rotateAboutOrigin.multiplyTransformTransform(instancedGeom.getRtcModelTransform(modelMatrix), this._scratchVIModelMatrix);
                    mv = viewMatrix.multiplyTransformTransform(viModelMatrix, this._scratchTransform);
                }
                else {
                    mv = viewMatrix.multiplyTransformTransform(instancedGeom.getRtcModelTransform(modelMatrix), this._scratchTransform);
                }
            }
            else {
                if (undefined !== vio) {
                    const viewToWorldRot = (0, core_bentley_1.expectDefined)(viewMatrix.matrix.inverse(this._scratchViewToWorld));
                    const rotateAboutOrigin = core_geometry_1.Transform.createFixedPointAndMatrix(vio, viewToWorldRot, this._scratchTransform2);
                    const viModelMatrix = rotateAboutOrigin.multiplyTransformTransform(modelMatrix, this._scratchVIModelMatrix);
                    mv = viewMatrix.multiplyTransformTransform(viModelMatrix, this._scratchTransform);
                }
                else {
                    mv = viewMatrix.multiplyTransformTransform(modelMatrix, this._scratchTransform);
                }
            }
        }
        if (this._target.wantThematicDisplay) {
            this._m32.initFromTransform(modelMatrix);
            this._v32.initFromMatrix3d(this._target.uniforms.frustum.viewMatrix.matrix);
        }
        else if (undefined !== geometry.asSurface?.mesh.constantLodVParams || this._target.uniforms.batch.wantContourLines) {
            this._m32.initFromTransform(modelMatrix);
        }
        core_geometry_1.Matrix4d.createTransform(mv, this._mv);
        this._mv32.initFromTransform(mv);
        // Don't bother computing mvp for instanced geometry - it's not used.
        if (!this._isInstanced) {
            uniforms.projectionMatrix.multiplyMatrixMatrix(this._mv, this._mvp);
            this._mvp32.initFromMatrix4d(this._mvp);
        }
        return true;
    }
    // set the clip style based on disableClipStyle
    setClipStyle(disableClipStyle) {
        const vp = IModelApp_1.IModelApp.viewManager.selectedView;
        if (vp) {
            const style = vp.view.displayStyle.settings.clipStyle;
            this.clipStack.insideColor.alpha = disableClipStyle ? 0 : (style.insideColor ? 1 : 0);
            this.clipStack.outsideColor.alpha = disableClipStyle ? 0 : (style.outsideColor ? 1 : 0);
            this.clipStack.intersectionStyle.alpha = disableClipStyle ? 0 : (style.intersectionStyle ? style.intersectionStyle.width : 0);
        }
    }
}
exports.BranchUniforms = BranchUniforms;
//# sourceMappingURL=BranchUniforms.js.map