/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { expectDefined } from "@itwin/core-bentley";
import { Matrix3d, Point3d } from "@itwin/core-geometry";
import { Matrix3, Matrix4 } from "./Matrix";
import { desync, sync } from "./Sync";
export const MAX_SAMPLE_POINTS = 40; // Maximum number of sample points to be used for the in-scattering and out-scattering computations.
export class AtmosphereUniforms {
    _atmosphere;
    _ellipsoid;
    // Main shader uniforms
    _earthScaleMatrix = new Matrix3d(new Float64Array([1, 0, 0, 0, 1, 0, 0, 0, 1]));
    _inverseEllipsoidRotationMatrix = new Matrix3d(new Float64Array([1, 0, 0, 0, 1, 0, 0, 0, 1]));
    _atmosphereScaleMatrix = new Matrix3d(new Float64Array([1, 0, 0, 0, 1, 0, 0, 0, 1]));
    _atmosphereData = new Matrix4();
    /**
     * uniform mat3 u_atmosphereData;
     *   { { atmosphereRadiusScaleFactor, atmosphereMaxDensityThresholdScaleFactor, densityFalloff, 0 },
     *   { numViewRaySamples, numSunRaySamples, 0, 0 },
     *   { earthCenter.x, earthCenter.y, earthCenter.z, 0 },
     *   { scatteringCoefficients.x, scatteringCoefficients.y, scatteringCoefficients.z, 0 } }
     */
    get atmosphereData() { return this._atmosphereData; }
    // Fragment shader uniforms
    _exposure = 0.0;
    // utility
    syncKey = 0;
    _scratchMatrix3d = new Matrix3d();
    _scratchPoint3d = new Point3d();
    update(target) {
        const atmosphereHasNotChanged = this._atmosphere && target.plan.atmosphere && this._atmosphere.equals(target.plan.atmosphere);
        const ellipsoidHasNotChanged = this._ellipsoid && target.plan.ellipsoid && this._ellipsoid.equals(target.plan.ellipsoid);
        if (atmosphereHasNotChanged && ellipsoidHasNotChanged) {
            return;
        }
        this._atmosphere = target.plan.atmosphere;
        this._ellipsoid = target.plan.ellipsoid;
        desync(this);
        if (!this._atmosphere || !this._ellipsoid) {
            return;
        }
        this._updateAtmosphereScaleMatrix(this._atmosphere.atmosphereHeightAboveEarth);
        this._updateExposure(this._atmosphere.exposure);
        this._updateDensityFalloff(this._atmosphere.densityFalloff);
        this._updateEarthCenter(this._ellipsoid.ellipsoidCenter, target.uniforms.frustum.viewMatrix);
        this._updateEarthScaleMatrix(this._ellipsoid.ellipsoidRadii);
        this._updateInverseEllipsoidRotationMatrix(this._ellipsoid.ellipsoidRotation, target.uniforms.frustum.viewMatrix.matrix);
        this._updateAtmosphereRadiusScaleFactor(this._atmosphere.atmosphereHeightAboveEarth);
        this._updateAtmosphereMaxDensityThresholdScaleFactor(this._atmosphere.depthBelowEarthForMaxDensity);
        this._updateNumViewRaySamples(this._atmosphere.numViewRaySamples);
        this._updateNumSunRaySamples(this._atmosphere.numSunRaySamples);
        this._updateScatteringCoefficients(this._atmosphere.scatteringStrength, this._atmosphere.wavelengths);
    }
    _updateEarthCenter(earthCenter, viewMatrix) {
        viewMatrix.multiplyPoint3d(earthCenter, this._scratchPoint3d);
        this._atmosphereData.data[8] = this._scratchPoint3d.x;
        this._atmosphereData.data[9] = this._scratchPoint3d.y;
        this._atmosphereData.data[10] = this._scratchPoint3d.z;
    }
    _updateInverseEllipsoidRotationMatrix(ellipsoidRotation, viewRotation) {
        viewRotation.inverse(this._scratchMatrix3d);
        ellipsoidRotation.multiplyMatrixInverseMatrix(this._scratchMatrix3d, this._inverseEllipsoidRotationMatrix);
    }
    _updateEarthScaleMatrix(earthRadii) {
        this._earthScaleMatrix.setAt(0, 0, earthRadii.x);
        this._earthScaleMatrix.setAt(1, 1, earthRadii.y);
        this._earthScaleMatrix.setAt(2, 2, earthRadii.z);
    }
    _updateAtmosphereScaleMatrix(heightAboveSurface) {
        const earthPolarRadius = this._earthScaleMatrix.at(2, 2);
        const scaleFactor = earthPolarRadius === 0 ? 1.0 : (earthPolarRadius + heightAboveSurface) / earthPolarRadius;
        this._earthScaleMatrix.scale(scaleFactor, this._atmosphereScaleMatrix);
    }
    _updateAtmosphereRadiusScaleFactor(atmosphereHeightAboveEarth) {
        const earthPolarRadius = this._earthScaleMatrix.at(2, 2);
        const minDensityThresholdRadius = earthPolarRadius + atmosphereHeightAboveEarth;
        const atmosphereRadiusScaleFactor = (earthPolarRadius === 0)
            ? 1
            : (minDensityThresholdRadius / earthPolarRadius);
        this.atmosphereData.data[0] = atmosphereRadiusScaleFactor;
    }
    _updateAtmosphereMaxDensityThresholdScaleFactor(maxDensityDepthBelowEarth) {
        const earthPolarRadius = this._earthScaleMatrix.at(2, 2);
        const maxDensityThresholdRadius = earthPolarRadius - maxDensityDepthBelowEarth;
        const atmosphereMaxDensityThresholdScaleFactor = (earthPolarRadius === 0)
            ? 1
            : (maxDensityThresholdRadius / earthPolarRadius);
        this.atmosphereData.data[1] = atmosphereMaxDensityThresholdScaleFactor;
    }
    _updateDensityFalloff(densityFalloff) {
        this.atmosphereData.data[2] = densityFalloff;
    }
    _updateScatteringCoefficients(scatteringStrength, wavelengths) {
        // Rayleigh scattering strength is inversely related to the 4th power of the wavelength -> 1/pow(wavelength, 4)
        // Because this produces very small values when the wavelengths are taken in nanometers,
        //   we attempt to normalize them around 1 by taking the smallest wavelength of visible light as a baseline (violet light - 400nm)
        const violetLightWavelength = 400.0;
        this.atmosphereData.data[12] = ((violetLightWavelength / wavelengths.r) ** 4.0) * scatteringStrength;
        this.atmosphereData.data[13] = ((violetLightWavelength / wavelengths.g) ** 4.0) * scatteringStrength;
        this.atmosphereData.data[14] = ((violetLightWavelength / wavelengths.b) ** 4.0) * scatteringStrength;
    }
    _updateExposure(exposure) {
        this._exposure = exposure;
    }
    bindExposure(uniform) {
        if (!sync(this, uniform)) {
            uniform.setUniform1f(this._exposure);
        }
    }
    _updateNumViewRaySamples(_numViewRaySamples) {
        const numViewRaySamples = Math.max(0, Math.min(MAX_SAMPLE_POINTS, _numViewRaySamples));
        this.atmosphereData.data[4] = numViewRaySamples;
    }
    _updateNumSunRaySamples(_numSunRaySamples) {
        const numSunRaySamples = Math.max(0, Math.min(MAX_SAMPLE_POINTS, _numSunRaySamples));
        this.atmosphereData.data[5] = numSunRaySamples;
    }
    bindInverseRotationInverseEarthScaleMatrix(uniform) {
        if (!sync(this, uniform)) {
            this._earthScaleMatrix.multiplyMatrixInverseMatrix(this._inverseEllipsoidRotationMatrix, this._scratchMatrix3d);
            uniform.setMatrix3(Matrix3.fromMatrix3d(this._scratchMatrix3d));
        }
    }
    bindInverseRotationInverseAtmosphereScaleMatrix(uniform) {
        if (!sync(this, uniform)) {
            this._atmosphereScaleMatrix.multiplyMatrixInverseMatrix(this._inverseEllipsoidRotationMatrix, this._scratchMatrix3d);
            uniform.setMatrix3(Matrix3.fromMatrix3d(this._scratchMatrix3d));
        }
    }
    bindEarthScaleMatrix(uniform) {
        if (!sync(this, uniform))
            uniform.setMatrix3(Matrix3.fromMatrix3d(this._earthScaleMatrix));
    }
    bindAtmosphereScaleMatrix(uniform) {
        if (!sync(this, uniform))
            uniform.setMatrix3(Matrix3.fromMatrix3d(this._atmosphereScaleMatrix));
    }
    bindInverseEarthScaleMatrix(uniform) {
        if (!sync(this, uniform))
            uniform.setMatrix3(Matrix3.fromMatrix3d(expectDefined(this._earthScaleMatrix.inverse())));
    }
    bindInverseAtmosphereScaleMatrix(uniform) {
        if (!sync(this, uniform))
            uniform.setMatrix3(Matrix3.fromMatrix3d(expectDefined(this._atmosphereScaleMatrix.inverse())));
    }
    get isDisposed() {
        return true;
    }
    [Symbol.dispose]() { }
    /** @deprecated in 5.0 - will not be removed until after 2026-06-13. Use [Symbol.dispose] instead. */
    dispose() {
        this[Symbol.dispose]();
    }
}
//# sourceMappingURL=AtmosphereUniforms.js.map