/*---------------------------------------------------------------------------------------------
* 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
 */
import { Vector3d } from "@itwin/core-geometry";
import { BatchUniforms } from "./BatchUniforms";
import { BranchUniforms } from "./BranchUniforms";
import { FrustumUniforms } from "./FrustumUniforms";
import { HiliteUniforms } from "./HiliteUniforms";
import { LightingUniforms } from "./LightingUniforms";
import { ShadowUniforms } from "./ShadowUniforms";
import { StyleUniforms } from "./StyleUniforms";
import { desync, sync } from "./Sync";
import { ThematicUniforms } from "./ThematicUniforms";
import { ViewRectUniforms } from "./ViewRectUniforms";
import { RealityModelUniforms } from "./RealityModelUniforms";
import { AtmosphereUniforms } from "./AtmosphereUniforms";
import { ContourUniforms } from "./ContourUniforms";
class PixelWidthFactor {
    constructor() {
        /** The pixel width factor depends on both the frustum and the view rect. It also depends on the frustum scale associated with the current Branch. */
        this._rectSync = {};
        this._frustumSync = {};
        this._branchSync = {};
        this._factor = 0;
        this.syncKey = 0;
    }
    bind(uniform, uniforms) {
        if (!sync(uniforms.frustum, this._frustumSync) || !sync(uniforms.viewRect, this._rectSync) || !sync(uniforms.branch, this._branchSync))
            this.compute(uniforms.frustum, uniforms.viewRect.width, uniforms.viewRect.height, uniforms.branch.top.frustumScale);
        if (!sync(this, uniform))
            uniform.setUniform1f(this._factor);
    }
    compute(frustumUniforms, width, height, scale) {
        desync(this);
        const frustumPlanes = frustumUniforms.planes;
        const top = frustumPlanes[0];
        const bottom = frustumPlanes[1];
        const left = frustumPlanes[2];
        const right = frustumPlanes[3];
        let halfPixelWidth;
        let halfPixelHeight;
        const frustum = frustumUniforms.frustum;
        if (2 /* FrustumUniformType.Perspective */ === frustumUniforms.type) {
            const inverseNear = 1.0 / frustum[0];
            const tanTheta = top * inverseNear;
            halfPixelHeight = scale.x * tanTheta / height;
            halfPixelWidth = scale.y * tanTheta / width;
        }
        else {
            halfPixelWidth = scale.x * 0.5 * (right - left) / width;
            halfPixelHeight = scale.y * 0.5 * (top - bottom) / height;
        }
        this._factor = Math.sqrt(halfPixelWidth * halfPixelWidth + halfPixelHeight * halfPixelHeight);
    }
}
// Direction in view space used if solar shadows are disabled and LightSettings.useSolarLighting is false.
const defaultSunDirectionView = new Vector3d(0.272166, 0.680414, 0.680414);
class SunDirection {
    constructor() {
        this.syncKey = 0;
        this._haveWorldDir = false;
        this._worldDir = Vector3d.unitZ();
        this._viewDir = defaultSunDirectionView.clone();
        this._viewDir32 = new Float32Array(3);
        this._updated = true;
    }
    update(sunDir) {
        const haveWorldDir = undefined !== sunDir;
        if (haveWorldDir !== this._haveWorldDir || (sunDir && !sunDir.isExactEqual(this._worldDir))) {
            this._updated = true;
            desync(this);
            this._haveWorldDir = haveWorldDir;
            if (sunDir) {
                sunDir.clone(this._worldDir);
                this._worldDir.normalizeInPlace();
            }
        }
    }
    bind(uniform, uniforms) {
        if (!sync(uniforms.frustum, this) || this._updated) {
            if (this._haveWorldDir) {
                uniforms.frustum.viewMatrix.multiplyVector(this._worldDir, this._viewDir);
                this._viewDir.negate(this._viewDir);
            }
            else {
                defaultSunDirectionView.clone(this._viewDir);
            }
            this._viewDir.normalizeInPlace();
            this._viewDir32[0] = this._viewDir.x;
            this._viewDir32[1] = this._viewDir.y;
            this._viewDir32[2] = this._viewDir.z;
            desync(this);
            this._updated = false;
        }
        if (!sync(this, uniform))
            uniform.setUniform3fv(this._viewDir32);
    }
}
/** Holds state for commonly-used uniforms to avoid unnecessary recomputation, owned by a Target.
 * DO NOT directly modify exposed members of the objects exposed by this class. Use their APIs.
 * e.g., code like `target.uniforms.frustum.projectionMatrix.setFrom(someOtherMatrix)` or `target.uniforms.branch.top.setViewFlags(blah)` will cause bugs.
 * @internal
 */
export class TargetUniforms {
    constructor(target) {
        this.viewRect = new ViewRectUniforms();
        this.hilite = new HiliteUniforms();
        this.style = new StyleUniforms();
        this.lights = new LightingUniforms();
        this.thematic = new ThematicUniforms();
        this.contours = new ContourUniforms();
        this.realityModel = new RealityModelUniforms();
        this.atmosphere = new AtmosphereUniforms();
        this._pixelWidthFactor = new PixelWidthFactor();
        this._sunDirection = new SunDirection();
        this.frustum = new FrustumUniforms();
        this.branch = new BranchUniforms(target);
        this.batch = new BatchUniforms(target, this.branch.createBatchState());
        this.shadow = new ShadowUniforms(target);
    }
    getProjectionMatrix(forViewCoords) {
        return forViewCoords ? this.viewRect.projectionMatrix : this.frustum.projectionMatrix;
    }
    getProjectionMatrix32(forViewCoords) {
        return forViewCoords ? this.viewRect.projectionMatrix32 : this.frustum.projectionMatrix32;
    }
    bindProjectionMatrix(uniform, forViewCoords) {
        if (forViewCoords)
            this.viewRect.bindProjectionMatrix(uniform);
        else
            this.frustum.bindProjectionMatrix(uniform);
    }
    bindPixelWidthFactor(uniform) {
        this._pixelWidthFactor.bind(uniform, this);
    }
    bindSunDirection(uniform) {
        this._sunDirection.bind(uniform, this);
    }
    updateRenderPlan(plan) {
        this.style.update(plan);
        this.hilite.update(plan.hiliteSettings, plan.emphasisSettings);
        let sunDir;
        if (plan.lights) {
            this.lights.update(plan.lights);
            const useSunDir = plan.viewFlags.shadows || plan.lights.solar.alwaysEnabled;
            if (useSunDir)
                sunDir = plan.lights.solar.direction;
        }
        this._sunDirection.update(sunDir);
    }
}
//# sourceMappingURL=TargetUniforms.js.map