"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.createSkySphereBuilder = createSkySphereBuilder;
const core_geometry_1 = require("@itwin/core-geometry");
const core_common_1 = require("@itwin/core-common");
const AttributeMap_1 = require("../AttributeMap");
const FrustumUniforms_1 = require("../FrustumUniforms");
const RenderFlags_1 = require("../RenderFlags");
const System_1 = require("../System");
const Fragment_1 = require("./Fragment");
const ViewportQuad_1 = require("./ViewportQuad");
const Atmosphere_1 = require("./Atmosphere");
const computeGradientValue = `
  // For the gradient sky it's good enough to calculate these in the vertex shader.
  vec3 eyeToVert = a_worldPos - u_worldEye;
  float radius = sqrt(eyeToVert.x * eyeToVert.x + eyeToVert.y * eyeToVert.y);
  float zValue = eyeToVert.z - radius * u_zOffset;
  float d = atan(zValue, radius);
  if (u_skyParams.x < 0.0) { // 2-color gradient
    d = 0.5 - d / 3.14159265359;
    return vec4(d, 0.0, 0.0, 0.0);
  }
  d = d / 1.570796326795;
  return vec4(d, 1.0 - (d - horizonSize) / (1.0 - horizonSize), 1.0 - (-d - horizonSize) / (1.0 - horizonSize), (d + horizonSize) / (horizonSize * 2.0));
`;
const computeSkySphereColorGradient = `
  if (u_skyParams.x < 0.0) // 2-color
    return vec4(mix(u_zenithColor, u_nadirColor, v_gradientValue.x), 1.0);

  if (v_gradientValue.x > horizonSize) // above horizon
    return vec4(mix(u_zenithColor, u_skyColor, pow(v_gradientValue.y, u_skyParams.y)), 1.0);
  else if (v_gradientValue.x < -horizonSize) // below horizon
    return vec4(mix(u_nadirColor, u_groundColor, pow(v_gradientValue.z, u_skyParams.z)), 1.0);

  return vec4(mix(u_groundColor, u_skyColor, v_gradientValue.w), 1.0);
`;
const computeSkySphereColorAtmosphere = `
  return vec4(0.0, 0.0, 0.0, 1.0);
`;
const computeEyeToVert = "v_eyeToVert = a_worldPos - u_worldEye;";
const computeSkySphereColorTexture = `
  // For the texture we must calculate these per pixel.  Alternatively we could use a finer mesh.
  float radius = sqrt(v_eyeToVert.x * v_eyeToVert.x + v_eyeToVert.y * v_eyeToVert.y);
  float zValue = v_eyeToVert.z - radius * u_zOffset;
  float u = 0.25 - (atan(v_eyeToVert.y, v_eyeToVert.x) + u_rotation) / 6.28318530718;
  float v = 0.5 - atan(zValue, radius) / 3.14159265359;
  if (u < 0.0)
    u += 1.0;
  if (v < 0.0)
    v += 1.0;
  return TEXTURE(s_skyTxtr, vec2(u, v));
`;
/**
 * Eye Space for the SkySphere is unique because the ViewportQuad is already aligned with the view.
 * For this reason, the modelView matrix is not useful in calculating eyeSpace.
 * Instead, we can calculate the eyeSpace coordinates via the frustum values directly.
 */
const computeEyeSpace = `
vec3 computeEyeSpace(vec4 rawPos) {
  vec3 pos01 = rawPos.xyz * 0.5 + 0.5;

  float top = u_frustumPlanes.x;
  float bottom = u_frustumPlanes.y;
  float left = u_frustumPlanes.z;
  float right = u_frustumPlanes.w;

  return vec3(
    mix(left, right, pos01.x),
    mix(bottom, top, pos01.y),
    -u_frustum.x
  );
}`;
const scratch3Floats = new Float32Array(3);
const scratchVec3 = new core_geometry_1.Vector3d();
const scratchPoint3 = new core_geometry_1.Point3d();
/** @internal */
function modulateColor(colorIn, t, colorOut) {
    const b = 1.0 - t;
    colorOut[0] = colorIn[0] * b;
    colorOut[1] = colorIn[1] * b;
    colorOut[2] = colorIn[2] * b;
}
/** @internal */
function addGradientUniforms(builder) {
    builder.vert.addUniform("u_skyParams", 4 /* VariableType.Vec3 */, (shader) => {
        shader.addGraphicUniform("u_skyParams", (uniform, params) => {
            const geom = params.geometry;
            uniform.setUniform3fv(geom.typeAndExponents);
        });
    });
    builder.vert.addUniform("u_zOffset", 2 /* VariableType.Float */, (shader) => {
        shader.addGraphicUniform("u_zOffset", (uniform, params) => {
            const geom = params.geometry;
            uniform.setUniform1f(geom.zOffset);
        });
    });
    builder.frag.addUniform("u_skyParams", 4 /* VariableType.Vec3 */, (shader) => {
        shader.addGraphicUniform("u_skyParams", (uniform, params) => {
            const geom = params.geometry;
            uniform.setUniform3fv(geom.typeAndExponents);
        });
    });
    builder.frag.addUniform("u_zenithColor", 4 /* VariableType.Vec3 */, (shader) => {
        shader.addGraphicUniform("u_zenithColor", (uniform, params) => {
            const geom = params.geometry;
            const plan = params.target.plan;
            if (plan.backgroundMapOn && plan.isGlobeMode3D) {
                modulateColor(geom.zenithColor, plan.globalViewTransition, scratch3Floats);
                uniform.setUniform3fv(scratch3Floats);
            }
            else
                uniform.setUniform3fv(geom.zenithColor);
        });
    });
    builder.frag.addUniform("u_skyColor", 4 /* VariableType.Vec3 */, (shader) => {
        shader.addGraphicUniform("u_skyColor", (uniform, params) => {
            const geom = params.geometry;
            const plan = params.target.plan;
            if (plan.backgroundMapOn && plan.isGlobeMode3D) {
                modulateColor(geom.skyColor, plan.globalViewTransition, scratch3Floats);
                uniform.setUniform3fv(scratch3Floats);
            }
            else
                uniform.setUniform3fv(geom.skyColor);
        });
    });
    builder.frag.addUniform("u_groundColor", 4 /* VariableType.Vec3 */, (shader) => {
        shader.addGraphicUniform("u_groundColor", (uniform, params) => {
            const geom = params.geometry;
            const plan = params.target.plan;
            if (plan.backgroundMapOn) {
                let clr = geom.skyColor;
                if (-1 === geom.typeAndExponents[0]) // 2-color gradient
                    clr = geom.zenithColor;
                if (plan.isGlobeMode3D) {
                    modulateColor(clr, plan.globalViewTransition, scratch3Floats);
                    uniform.setUniform3fv(scratch3Floats);
                }
                else
                    uniform.setUniform3fv(clr);
            }
            else {
                uniform.setUniform3fv(geom.groundColor);
            }
        });
    });
    builder.frag.addUniform("u_nadirColor", 4 /* VariableType.Vec3 */, (shader) => {
        shader.addGraphicUniform("u_nadirColor", (uniform, params) => {
            const geom = params.geometry;
            const plan = params.target.plan;
            if (plan.backgroundMapOn) {
                let clr = geom.skyColor;
                if (-1 === geom.typeAndExponents[0]) // 2-color gradient
                    clr = geom.nadirColor;
                if (plan.isGlobeMode3D) {
                    modulateColor(clr, plan.globalViewTransition, scratch3Floats);
                    uniform.setUniform3fv(scratch3Floats);
                }
                else
                    uniform.setUniform3fv(clr);
            }
            else {
                uniform.setUniform3fv(geom.nadirColor);
            }
        });
    });
}
/** @internal */
function addTextureUniforms(builder) {
    builder.frag.addUniform("s_skyTxtr", 8 /* VariableType.Sampler2D */, (shader) => {
        shader.addGraphicUniform("s_skyTxtr", (uniform, params) => {
            const geom = params.geometry;
            if (undefined !== geom.skyTexture)
                geom.skyTexture.texture.bindSampler(uniform, RenderFlags_1.TextureUnit.Zero);
            else
                System_1.System.instance.ensureSamplerBound(uniform, RenderFlags_1.TextureUnit.FeatureSymbology);
        });
    });
    builder.frag.addUniform("u_zOffset", 2 /* VariableType.Float */, (shader) => {
        shader.addGraphicUniform("u_zOffset", (uniform, params) => {
            const geom = params.geometry;
            uniform.setUniform1f(geom.zOffset);
        });
    });
    builder.frag.addUniform("u_rotation", 2 /* VariableType.Float */, (shader) => {
        shader.addGraphicUniform("u_rotation", (uniform, params) => {
            const geom = params.geometry;
            uniform.setUniform1f(geom.rotation);
        });
    });
}
/** @internal */
function createSkySphereBuilder(isGradient, flags) {
    const attrMap = AttributeMap_1.AttributeMap.findAttributeMap(isGradient ? 24 /* TechniqueId.SkySphereGradient */ : 25 /* TechniqueId.SkySphereTexture */, false);
    const builder = (0, ViewportQuad_1.createViewportQuadBuilder)(false, attrMap);
    const vert = builder.vert;
    const frag = builder.frag;
    vert.addFunction(computeEyeSpace);
    builder.addInlineComputedVarying("v_eyeSpace", 4 /* VariableType.Vec3 */, "v_eyeSpace = computeEyeSpace(rawPosition);");
    vert.addUniform("u_frustumPlanes", 5 /* VariableType.Vec4 */, (prg) => {
        prg.addGraphicUniform("u_frustumPlanes", (uniform, params) => {
            uniform.setUniform4fv(params.target.uniforms.frustum.planes); // { top, bottom, left, right }
        });
    });
    vert.addUniform("u_frustum", 4 /* VariableType.Vec3 */, (prg) => {
        prg.addGraphicUniform("u_frustum", (uniform, params) => {
            uniform.setUniform3fv(params.target.uniforms.frustum.frustum); // { near, far, type }
        });
    });
    frag.set(17 /* FragmentShaderComponent.AssignFragData */, Fragment_1.assignFragColor);
    if (flags.enableAtmosphere) {
        frag.set(1 /* FragmentShaderComponent.ComputeBaseColor */, computeSkySphereColorAtmosphere);
        (0, Atmosphere_1.addAtmosphericScatteringEffect)(builder, true, true);
        return builder;
    }
    builder.vert.addUniform("u_worldEye", 4 /* VariableType.Vec3 */, (shader) => {
        shader.addGraphicUniform("u_worldEye", (uniform, params) => {
            const frustum = params.target.planFrustum;
            if (2 /* FrustumUniformType.Perspective */ === params.target.uniforms.frustum.type) {
                // compute eye point from frustum.
                const farLowerLeft = frustum.getCorner(core_common_1.Npc.LeftBottomRear);
                const nearLowerLeft = frustum.getCorner(core_common_1.Npc.LeftBottomFront);
                const scale = 1.0 / (1.0 - params.target.planFraction);
                const zVec = core_geometry_1.Vector3d.createStartEnd(farLowerLeft, nearLowerLeft, scratchVec3);
                const cameraPosition = (0, FrustumUniforms_1.fromSumOf)(farLowerLeft, zVec, scale, scratchPoint3);
                scratch3Floats[0] = cameraPosition.x;
                scratch3Floats[1] = cameraPosition.y;
                scratch3Floats[2] = cameraPosition.z;
                uniform.setUniform3fv(scratch3Floats);
            }
            else {
                const delta = core_geometry_1.Vector3d.createStartEnd(frustum.getCorner(core_common_1.Npc.LeftBottomRear), frustum.getCorner(core_common_1.Npc.LeftBottomFront), scratchVec3);
                const pseudoCameraHalfAngle = 22.5;
                const diagonal = frustum.getCorner(core_common_1.Npc.LeftBottomRear).distance(frustum.getCorner(core_common_1.Npc.RightTopRear));
                const focalLength = diagonal / (2 * Math.atan(pseudoCameraHalfAngle * core_geometry_1.Angle.radiansPerDegree));
                let zScale = focalLength / delta.magnitude();
                if (zScale < 1.000001)
                    zScale = 1.000001; // prevent worldEye front being on or inside the frustum front plane
                const worldEye = core_geometry_1.Point3d.createAdd3Scaled(frustum.getCorner(core_common_1.Npc.LeftBottomRear), .5, frustum.getCorner(core_common_1.Npc.RightTopRear), .5, delta, zScale, scratchPoint3);
                scratch3Floats[0] = worldEye.x;
                scratch3Floats[1] = worldEye.y;
                scratch3Floats[2] = worldEye.z;
                uniform.setUniform3fv(scratch3Floats);
            }
        });
    });
    if (isGradient) {
        addGradientUniforms(builder);
        builder.addGlobal("horizonSize", 2 /* VariableType.Float */, 3 /* ShaderType.Both */, "0.0015", true);
        builder.addFunctionComputedVarying("v_gradientValue", 5 /* VariableType.Vec4 */, "computeGradientValue", computeGradientValue);
        frag.set(1 /* FragmentShaderComponent.ComputeBaseColor */, computeSkySphereColorGradient);
    }
    else {
        addTextureUniforms(builder);
        builder.addInlineComputedVarying("v_eyeToVert", 4 /* VariableType.Vec3 */, computeEyeToVert);
        frag.set(1 /* FragmentShaderComponent.ComputeBaseColor */, computeSkySphereColorTexture);
    }
    return builder;
}
//# sourceMappingURL=SkySphere.js.map