"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.mixFeatureColor = exports.readDepthAndOrder = void 0;
exports.addOvrFlagConstants = addOvrFlagConstants;
exports.addFeatureIndex = addFeatureIndex;
exports.addMaxAlpha = addMaxAlpha;
exports.addSurfaceHiliter = addSurfaceHiliter;
exports.addHiliter = addHiliter;
exports.addRenderOrderConstants = addRenderOrderConstants;
exports.addRenderOrder = addRenderOrder;
exports.addPixelWidthFactor = addPixelWidthFactor;
exports.addFeatureId = addFeatureId;
exports.addSurfaceDiscard = addSurfaceDiscard;
exports.addClassifierFlash = addClassifierFlash;
exports.addFeatureSymbology = addFeatureSymbology;
exports.addUniformHiliter = addUniformHiliter;
exports.addUniformFeatureSymbology = addUniformFeatureSymbology;
const core_bentley_1 = require("@itwin/core-bentley");
const RenderFlags_1 = require("../RenderFlags");
const Common_1 = require("./Common");
const Decode_1 = require("./Decode");
const Fragment_1 = require("./Fragment");
const LookupTable_1 = require("./LookupTable");
const RenderPass_1 = require("./RenderPass");
const Vertex_1 = require("./Vertex");
/** @internal */
function addOvrFlagConstants(builder) {
    // NB: These are the bit positions of each flag in OvrFlags enum - not the flag values
    builder.addBitFlagConstant("kOvrBit_LineRgb", 0);
    builder.addBitFlagConstant("kOvrBit_Rgb", 1);
    builder.addBitFlagConstant("kOvrBit_Alpha", 2);
    builder.addBitFlagConstant("kOvrBit_LineAlpha", 3);
    builder.addBitFlagConstant("kOvrBit_Flashed", 4);
    builder.addBitFlagConstant("kOvrBit_NonLocatable", 5);
    builder.addBitFlagConstant("kOvrBit_LineCode", 6);
    builder.addBitFlagConstant("kOvrBit_Weight", 7);
    // NB: We treat the 16-bit flags as 2 bytes - so subtract 8 from each of these bit indices.
    builder.addBitFlagConstant("kOvrBit_Hilited", 0);
    builder.addBitFlagConstant("kOvrBit_Emphasized", 1);
    builder.addBitFlagConstant("kOvrBit_ViewIndependentTransparency", 2);
    builder.addBitFlagConstant("kOvrBit_InvisibleDuringPick", 3);
    builder.addBitFlagConstant("kOvrBit_Visibility", 4);
    builder.addBitFlagConstant("kOvrBit_IgnoreMaterial", 5);
}
const computeLUTFeatureIndex = `g_featureAndMaterialIndex.xyz`;
const computeInstanceFeatureIndex = `g_isAreaPattern ? u_patternFeatureId : a_featureId`;
function computeFeatureIndex(vertex) {
    if (vertex.usesInstancedGeometry) {
        vertex.addUniform("u_patternFeatureId", 4 /* VariableType.Vec3 */, (prog) => {
            prog.addGraphicUniform("u_patternFeatureId", (uniform, params) => {
                const id = params.geometry.asInstanced?.patternFeatureId;
                (0, core_bentley_1.assert)(undefined !== id);
                if (id)
                    uniform.setUniform3fv(id);
            });
        });
        return `g_featureIndex = ${computeInstanceFeatureIndex};`;
    }
    return vertex.usesVertexTable ? `g_featureIndex = ${computeLUTFeatureIndex};` : "";
}
function getFeatureIndex(vertex) {
    return `
float getFeatureIndex() {
  ${computeFeatureIndex(vertex)}
  return decodeUInt24(g_featureIndex);
}
`;
}
const nthFeatureBitSet = `
bool nthFeatureBitSet(float flags, uint n) {
  return 0u == (u_globalOvrFlags & n) && nthBitSet(flags, n);
}
`;
const extractNthFeatureBit = `
float extractNthFeatureBit(float flags, uint n) {
  return 0u == (u_globalOvrFlags & n) && nthBitSet(flags, n) ? 1.0 : 0.0;
}
`;
const computeFeatureTextureCoords = `
vec2 computeFeatureTextureCoords() { return compute_feature_coords(getFeatureIndex()); }
`;
const getFirstFeatureRgba = `
vec4 getFirstFeatureRgba() {
  feature_texCoord = computeFeatureTextureCoords();
  return TEXTURE(u_featureLUT, feature_texCoord);
}
`;
const getSecondFeatureRgba = `
vec4 getSecondFeatureRgba(bool isLinear) {
  vec2 coord = feature_texCoord;
  coord.x += g_feature_stepX * (isLinear ? 2.0 : 1.0);
  return TEXTURE(u_featureLUT, coord);
}
`;
const computeLineWeight = `
float computeLineWeight() {
  return linear_feature_overrides.x > 0.5 ? linear_feature_overrides.y : g_lineWeight;
}
`;
const computeLineCode = `
float computeLineCode() {
  return linear_feature_overrides.z > 0.5 ? linear_feature_overrides.w : g_lineCode;
}
`;
function addFeatureIndex(vert) {
    vert.addGlobal("g_featureIndex", 4 /* VariableType.Vec3 */);
    vert.addFunction(Decode_1.decodeUint24);
    vert.addFunction(getFeatureIndex(vert));
}
// Discards vertex if feature is invisible; or rendering opaque during translucent pass or vice-versa
// (The latter occurs when some translucent feature is overridden to be opaque, or vice-versa)
const checkVertexDiscard = `
  if (feature_invisible)
    return true;

  bool hasAlpha = 1.0 == u_hasAlpha;
  if (feature_alpha > 0.0)
    hasAlpha = feature_alpha <= s_maxAlpha;

  int discardFlags = u_transparencyDiscardFlags;
  bool discardViewIndependentDuringOpaque = discardFlags >= 4;
  if (discardViewIndependentDuringOpaque)
    discardFlags = discardFlags - 4;

  bool isOpaquePass = (kRenderPass_OpaqueLinear <= u_renderPass && kRenderPass_OpaqueGeneral >= u_renderPass);
  bool discardTranslucentDuringOpaquePass = 1 == discardFlags || 3 == discardFlags || (feature_viewIndependentTransparency && discardViewIndependentDuringOpaque);
  if (isOpaquePass && !discardTranslucentDuringOpaquePass)
    return false;

  bool isTranslucentPass = kRenderPass_Translucent == u_renderPass;
  bool discardOpaqueDuringTranslucentPass = 2 == discardFlags || 3 == discardFlags;
  if (isTranslucentPass && !discardOpaqueDuringTranslucentPass)
    return false;

  return (isOpaquePass && hasAlpha) || (isTranslucentPass && !hasAlpha);
`;
function addTransparencyDiscardFlags(vert) {
    // Even when transparency view flag is off, we need to allow features to override transparency, because it
    // is used when applying transparency threshold. However, we need to ensure we don't DISCARD transparent stuff during
    // opaque pass if transparency is off (see checkVertexDiscard). Especially important for transparency threshold and readPixels().
    // Also, if we override raster text to be opaque we must still draw it in the translucent pass.
    // Finally, if the transparency override is view-independent (i.e., ignores view flags and render mode) we want to discard it during opaque pass
    // unless we're reading pixels.
    // So we have a bit field:
    // 1: discard translucent during opaque.
    // 2: discard opaque during translucent.
    // 4: discard view-independent translucent during opaque.
    vert.addUniform("u_transparencyDiscardFlags", 1 /* VariableType.Int */, (prog) => {
        prog.addGraphicUniform("u_transparencyDiscardFlags", (uniform, params) => {
            let flags = 0;
            // Textured surfaces may render in both passes. If so, it's up to fragment shader to handle discard.
            const pass = params.geometry.getPass(params.target);
            if (!RenderFlags_1.Pass.rendersOpaqueAndTranslucent(pass)) {
                // During readPixels() we force transparency off. Make sure to ignore a Branch that turns it back on.
                if (!params.target.isReadPixelsInProgress)
                    flags = params.target.currentViewFlags.transparency ? 1 : 4;
                if (!params.geometry.alwaysRenderTranslucent)
                    flags += 2;
            }
            uniform.setUniform1i(flags);
        });
    });
}
function addCommon(builder, mode, opts, wantGlobalOvrFlags = true) {
    if (0 /* FeatureMode.None */ === mode)
        return false;
    const vert = builder.vert;
    addFeatureIndex(vert);
    const haveOverrides = 0 /* FeatureSymbologyOptions.None */ !== (opts & 4 /* FeatureSymbologyOptions.HasOverrides */);
    if (!haveOverrides) {
        // For pick output we must compute g_featureIndex...
        if (1 /* FeatureMode.Pick */ === mode)
            vert.set(3 /* VertexShaderComponent.ComputeFeatureOverrides */, computeFeatureIndex(vert));
        return true;
    }
    const wantWeight = 0 /* FeatureSymbologyOptions.None */ !== (opts & 1 /* FeatureSymbologyOptions.Weight */);
    const wantLineCode = 0 /* FeatureSymbologyOptions.None */ !== (opts & 2 /* FeatureSymbologyOptions.LineCode */);
    const wantColor = 0 /* FeatureSymbologyOptions.None */ !== (opts & 8 /* FeatureSymbologyOptions.Color */);
    const wantAlpha = 0 /* FeatureSymbologyOptions.None */ !== (opts & 16 /* FeatureSymbologyOptions.Alpha */);
    (0, core_bentley_1.assert)(wantColor || !wantAlpha);
    (0, Common_1.addExtractNthBit)(vert);
    addOvrFlagConstants(vert);
    vert.addGlobal("linear_feature_overrides", 5 /* VariableType.Vec4 */, "vec4(0.0)");
    vert.addGlobal("feature_ignore_material", 0 /* VariableType.Boolean */, "false");
    if (wantWeight || wantLineCode) {
        if (wantLineCode)
            (0, Vertex_1.replaceLineCode)(vert, computeLineCode);
        if (wantWeight) {
            (0, Vertex_1.replaceLineWeight)(vert, computeLineWeight);
        }
    }
    if (wantGlobalOvrFlags) {
        const bitmapType = 10 /* VariableType.Uint */;
        vert.addFunction(nthFeatureBitSet);
        vert.addFunction(extractNthFeatureBit);
        vert.addUniform("u_globalOvrFlags", bitmapType, (prog) => {
            prog.addGraphicUniform("u_globalOvrFlags", (uniform, params) => {
                let flags = 0.0;
                if (params.geometry.isEdge) {
                    const settings = params.target.currentEdgeSettings;
                    flags = settings.computeOvrFlags(params.renderPass, params.target.currentViewFlags);
                }
                if (!params.geometry.allowColorOverride)
                    flags |= 6 /* OvrFlags.Rgba */;
                uniform.setUniformBitflags(flags);
            });
        });
    }
    (0, LookupTable_1.addLookupTable)(vert, "feature", "3.0");
    vert.addGlobal("feature_texCoord", 3 /* VariableType.Vec2 */);
    vert.addFunction(computeFeatureTextureCoords);
    vert.addFunction(getFirstFeatureRgba);
    vert.addUniform("u_featureLUT", 8 /* VariableType.Sampler2D */, (prog) => {
        prog.addGraphicUniform("u_featureLUT", (uniform, params) => {
            params.target.uniforms.batch.bindLUT(uniform);
        });
    });
    vert.addUniform("u_featureParams", 3 /* VariableType.Vec2 */, (prog) => {
        prog.addGraphicUniform("u_featureParams", (uniform, params) => {
            params.target.uniforms.batch.bindLUTParams(uniform);
        });
    });
    if (wantColor) {
        vert.addFunction(getSecondFeatureRgba);
        if (wantAlpha) {
            addMaxAlpha(vert);
            (0, RenderPass_1.addRenderPass)(vert);
            (0, Vertex_1.addAlpha)(vert);
            addTransparencyDiscardFlags(vert);
            vert.set(9 /* VertexShaderComponent.CheckForDiscard */, checkVertexDiscard);
        }
    }
    return true;
}
function addMaxAlpha(builder) {
    const minTransparency = 15.0; // NB: See DisplayParams.getMinTransparency() - this must match!
    const maxAlpha = (255 - minTransparency) / 255;
    builder.addConstant("s_maxAlpha", 2 /* VariableType.Float */, maxAlpha.toString());
}
/** @internal */
function addEmphasisFlags(builder) {
    // Must be kept in sync with EmphasisFlags enum.
    builder.addBitFlagConstant("kEmphBit_Hilite", 0);
    builder.addBitFlagConstant("kEmphBit_Emphasize", 1);
    builder.addBitFlagConstant("kEmphBit_Flash", 2);
    builder.addBitFlagConstant("kEmphBit_NonLocatable", 3);
    builder.addConstant("kEmphFlag_Hilite", 2 /* VariableType.Float */, "1.0");
    builder.addConstant("kEmphFlag_Emphasize", 2 /* VariableType.Float */, "2.0");
    builder.addConstant("kEmphFlag_Flash", 2 /* VariableType.Float */, "4.0");
    builder.addConstant("kEmphFlag_NonLocatable", 2 /* VariableType.Float */, "8.0");
}
function addHiliteSettings(frag, wantFlashMode) {
    frag.addUniform("u_hilite_settings", 6 /* VariableType.Mat3 */, (prog) => {
        prog.addProgramUniform("u_hilite_settings", (uniform, params) => {
            params.target.uniforms.hilite.bindFeatureSettings(uniform);
        });
    });
    if (wantFlashMode) {
        frag.addUniform("u_flash_mode", 2 /* VariableType.Float */, (prog) => {
            prog.addGraphicUniform("u_flash_mode", (uniform, params) => {
                uniform.setUniform1f(params.geometry.getFlashMode(params));
            });
        });
    }
}
// If feature is not hilited, discard it.
const checkVertexHiliteDiscard = "return 0.0 == v_feature_hilited;";
// The result is a mask in which each pixel's r=1 if hilited and g=1 if emphasized (and not hilited).
const computeHiliteColor = `
  float flags = floor(v_feature_hilited + 0.5);
  float hilited = extractNthBit(flags, kEmphBit_Hilite);
  float emphasized = extractNthBit(flags, kEmphBit_Emphasize);
  return vec4(hilited, emphasized, 0.0, 0.0);
`;
const computeSurfaceHiliteColor = `
  if (isSurfaceBitSet(kSurfaceBit_HasTexture) && TEXTURE(s_texture, v_texCoord).a <= 0.15)
    return vec4(0.0);
${computeHiliteColor}`;
const computeHiliteOverrides = `
  vec4 value = getFirstFeatureRgba();
  float emphFlags = value.g * 256.0;
  v_feature_hilited = kEmphFlag_Hilite * extractNthBit(emphFlags, kOvrBit_Hilited) + kEmphFlag_Emphasize * extractNthBit(emphFlags, kOvrBit_Emphasized);
`;
const computeHiliteOverridesWithWeight = `${computeHiliteOverrides}
  float flags = value.r * 256.0;
  linear_feature_overrides = vec4(nthFeatureBitSet(flags, kOvrBit_Weight),
  value.a * 256.0,
  nthFeatureBitSet(flags, kOvrBit_LineCode),
  value.b * 256.0);
`;
/** @internal */
function addSurfaceHiliter(builder, wantWeight = false) {
    addHiliter(builder, wantWeight);
    builder.frag.set(1 /* FragmentShaderComponent.ComputeBaseColor */, computeSurfaceHiliteColor);
}
/** @internal */
function addHiliter(builder, wantWeight = false) {
    let opts = 4 /* FeatureSymbologyOptions.HasOverrides */;
    if (wantWeight)
        opts |= 1 /* FeatureSymbologyOptions.Weight */; // hiliter never needs line code or color...
    if (!addCommon(builder, 2 /* FeatureMode.Overrides */, opts, wantWeight))
        return;
    builder.addVarying("v_feature_hilited", 2 /* VariableType.Float */);
    addEmphasisFlags(builder.vert);
    builder.vert.set(3 /* VertexShaderComponent.ComputeFeatureOverrides */, wantWeight ? computeHiliteOverridesWithWeight : computeHiliteOverrides);
    builder.vert.set(9 /* VertexShaderComponent.CheckForDiscard */, checkVertexHiliteDiscard);
    addEmphasisFlags(builder.frag);
    (0, Common_1.addExtractNthBit)(builder.frag);
    builder.frag.set(1 /* FragmentShaderComponent.ComputeBaseColor */, computeHiliteColor);
    builder.frag.set(17 /* FragmentShaderComponent.AssignFragData */, Fragment_1.assignFragColor);
}
function addSamplers(frag, testFeatureId) {
    if (testFeatureId) {
        frag.addUniform("u_pickFeatureId", 8 /* VariableType.Sampler2D */, (prog) => {
            prog.addProgramUniform("u_pickFeatureId", (uniform, params) => {
                params.target.compositor.featureIds.bindSampler(uniform, RenderFlags_1.TextureUnit.PickFeatureId);
            });
        }, 3 /* VariablePrecision.High */);
    }
    frag.addUniform("u_pickDepthAndOrder", 8 /* VariableType.Sampler2D */, (prog) => {
        prog.addProgramUniform("u_pickDepthAndOrder", (uniform, params) => {
            params.target.compositor.depthAndOrder.bindSampler(uniform, RenderFlags_1.TextureUnit.PickDepthAndOrder);
        });
    }, 3 /* VariablePrecision.High */);
}
/** @internal */
exports.readDepthAndOrder = `
vec2 readDepthAndOrder(vec2 tc) {
  vec4 pdo = TEXTURE(u_pickDepthAndOrder, tc);
  float order = floor(pdo.x * 16.0 + 0.5);
  return vec2(order, decodeDepthRgb(pdo.yzw));
}
`;
const checkForEarlySurfaceDiscard = `
  float factor = float(u_renderPass <= kRenderPass_Translucent); // never discard during specific passes
  float term = 0.0;

  vec2 tc = windowCoordsToTexCoords(gl_FragCoord.xy);
  vec2 depthAndOrder = readDepthAndOrder(tc);

  float surfaceDepth = computeLinearDepth(v_eyeSpace.z);
  term += float(depthAndOrder.x > u_renderOrder && abs(depthAndOrder.y - surfaceDepth) < 4.0e-5);
  return factor * term > 0.0;
`;
const checkForEarlySurfaceDiscardWithFeatureID = `
  // No normals => unlt => reality model => no edges.
  if (u_renderPass > kRenderPass_Translucent || u_renderPass == kRenderPass_Layers || !u_surfaceFlags[kSurfaceBitIndex_HasNormals])
    return false;

  vec2 tc = windowCoordsToTexCoords(gl_FragCoord.xy);
  vec2 depthAndOrder = readDepthAndOrder(tc);

  if (depthAndOrder.x <= u_renderOrder)
    return false;

  // Calculate depthTolerance for letting edges show through their own surfaces
  float perspectiveFrustum = step(kFrustumType_Perspective, u_frustum.z);
  vec4 eyeDirAndWidthFactor = mix(vec4(0.0, 0.0, 1.0, u_pixelWidthFactor), vec4(normalize(-v_eyeSpace.xyz), -v_eyeSpace.z * u_pixelWidthFactor), perspectiveFrustum);
  vec3 eyeDir = eyeDirAndWidthFactor.xyz;
  float dtWidthFactor = eyeDirAndWidthFactor.w;

  // Compute depth tolerance based on angle of triangle to screen
  float isSilhouette = float(depthAndOrder.x == kRenderOrder_Silhouette);
  float dSq = dot(eyeDir, v_n);
  dSq *= 0.5 + 0.4 * (1.0 - isSilhouette);
  dSq = dSq * dSq;
  dSq = max(dSq, 0.0001);
  dSq = min(dSq, 0.999);

  float depthTolerance = dtWidthFactor * v_lineWeight * sqrt((1.0 - dSq) / dSq);
  depthTolerance *= 1.0 + .333 * isSilhouette;

  // Make sure stuff behind camera doesn't get pushed in front of it
  depthTolerance = max(depthTolerance, 0.0);

  // Convert depthTolerance from eye space to linear depth
  depthTolerance /= (u_frustum.y - u_frustum.x);

  float surfaceDepth = computeLinearDepth(v_eyeSpace.z);
  float depthDelta = abs(depthAndOrder.y - surfaceDepth);
  if (depthDelta > depthTolerance)
    return false;

  // Does pick buffer contain same feature?
  vec4 featId = TEXTURE(u_pickFeatureId, tc);

  // Converting to ints to test since varying floats can be interpolated incorrectly
  ivec4 featId_i = ivec4(featId * 255.0 + 0.5);
  ivec4 feature_id_i = ivec4(feature_id * 255.0 + 0.5);
  if (featId_i == feature_id_i)
    return true;

  // In 2d, display priority controls draw order of different elements.
  if (!u_checkInterElementDiscard)
    return false;

  // Use a tighter tolerance for two different elements since we're only fighting roundoff error.
  return depthDelta <= 4.0e-5;
`;
// This only adds the constants that are actually used in shader code.
function addRenderOrderConstants(builder) {
    builder.addConstant("kRenderOrder_BlankingRegion", 2 /* VariableType.Float */, 2 /* RenderOrder.BlankingRegion */.toFixed(1));
    builder.addConstant("kRenderOrder_Linear", 2 /* VariableType.Float */, 5 /* RenderOrder.Linear */.toFixed(1));
    builder.addConstant("kRenderOrder_PlanarLinear", 2 /* VariableType.Float */, 13 /* RenderOrder.PlanarLinear */.toFixed(1));
    builder.addConstant("kRenderOrder_Edge", 2 /* VariableType.Float */, 6 /* RenderOrder.Edge */.toFixed(1));
    builder.addConstant("kRenderOrder_PlanarEdge", 2 /* VariableType.Float */, 14 /* RenderOrder.PlanarEdge */.toFixed(1));
    builder.addConstant("kRenderOrder_Silhouette", 2 /* VariableType.Float */, 7 /* RenderOrder.Silhouette */.toFixed(1));
    builder.addConstant("kRenderOrder_PlanarSilhouette", 2 /* VariableType.Float */, 15 /* RenderOrder.PlanarSilhouette */.toFixed(1));
    builder.addConstant("kRenderOrder_UnlitSurface", 2 /* VariableType.Float */, 3 /* RenderOrder.UnlitSurface */.toFixed(1));
    builder.addConstant("kRenderOrder_LitSurface", 2 /* VariableType.Float */, 4 /* RenderOrder.LitSurface */.toFixed(1));
    builder.addConstant("kRenderOrder_PlanarUnlitSurface", 2 /* VariableType.Float */, 11 /* RenderOrder.PlanarUnlitSurface */.toFixed(1));
    builder.addConstant("kRenderOrder_PlanarLitSurface", 2 /* VariableType.Float */, 12 /* RenderOrder.PlanarLitSurface */.toFixed(1));
    builder.addConstant("kRenderOrder_PlanarBit", 2 /* VariableType.Float */, 8 /* RenderOrder.PlanarBit */.toFixed(1));
    builder.addConstant("kRenderOrder_Background", 2 /* VariableType.Float */, 1 /* RenderOrder.Background */.toFixed(1));
}
/** @internal */
function addRenderOrder(builder) {
    builder.addUniform("u_renderOrder", 2 /* VariableType.Float */, (prog) => {
        prog.addGraphicUniform("u_renderOrder", (uniform, params) => {
            const order = params.target.drawingBackgroundForReadPixels ? 1 /* RenderOrder.Background */ : params.geometry.renderOrder;
            uniform.setUniform1f(order);
        });
    });
}
function addPixelWidthFactor(builder) {
    builder.addUniform("u_pixelWidthFactor", 2 /* VariableType.Float */, (prog) => {
        prog.addGraphicUniform("u_pixelWidthFactor", (uniform, params) => {
            params.target.uniforms.bindPixelWidthFactor(uniform);
        });
    });
}
function addBatchId(builder) {
    builder.addUniform("u_batch_id", 5 /* VariableType.Vec4 */, (prog) => {
        prog.addGraphicUniform("u_batch_id", (uniform, params) => {
            params.target.uniforms.batch.bindBatchId(uniform);
        });
    }, 3 /* VariablePrecision.High */);
}
const computeIdVert = `v_feature_id = addUInt32s(u_batch_id, vec4(g_featureIndex, 0.0)) / 255.0;`;
const computeIdFrag = `
  vec4 featureIndex = vec4(floor(v_feature_index + 0.5), 0.0);
  feature_id = addUInt32s(u_batch_id, featureIndex) / 255.0;
`;
/** @internal */
function addFeatureId(builder, computeInFrag) {
    const vert = builder.vert;
    const frag = builder.frag;
    frag.addGlobal("feature_id", 5 /* VariableType.Vec4 */);
    if (!computeInFrag) {
        vert.addFunction(Common_1.addUInt32s);
        addBatchId(vert);
        builder.addInlineComputedVarying("v_feature_id", 5 /* VariableType.Vec4 */, computeIdVert);
        frag.addInitializer("feature_id = v_feature_id;");
    }
    else {
        frag.addFunction(Common_1.addUInt32s);
        builder.addInlineComputedVarying("v_feature_index", 4 /* VariableType.Vec3 */, "v_feature_index = g_featureIndex;");
        addBatchId(frag);
        frag.addInitializer(computeIdFrag);
    }
}
// Discard vertex if transparency is less than the display style's transparency threshold, IFF the specific bit is set. The bit is set if:
//  - Solid Fill or Hidden Line mode; or
//  - Shaded mode and generating shadow map (sufficiently transparent surfaces receive but do not cast shadows).
const isBelowTransparencyThreshold = `
  return v_color.a < u_transparencyThreshold && u_surfaceFlags[kSurfaceBitIndex_TransparencyThreshold];
`;
/** @internal */
function addSurfaceDiscard(builder, flags) {
    const feat = flags.featureMode;
    const isEdgeTestNeeded = flags.isEdgeTestNeeded;
    const isClassified = flags.isClassified;
    const computeIdInFrag = !flags.isTranslucent && 0 !== flags.isClassified && 2 /* FeatureMode.Overrides */ === feat;
    const frag = builder.frag;
    const vert = builder.vert;
    vert.set(12 /* VertexShaderComponent.CheckForLateDiscard */, isBelowTransparencyThreshold);
    vert.addUniform("u_transparencyThreshold", 2 /* VariableType.Float */, (prog) => {
        prog.addGraphicUniform("u_transparencyThreshold", (uniform, params) => {
            uniform.setUniform1f(params.target.currentTransparencyThreshold);
        });
    });
    if (isEdgeTestNeeded) {
        (0, Fragment_1.addWindowToTexCoords)(frag);
        if (!flags.isHilite)
            (0, Common_1.addEyeSpace)(builder);
        if (0 /* FeatureMode.None */ === feat) {
            addSamplers(frag, false);
            frag.addFunction(Fragment_1.computeLinearDepth);
            frag.addFunction(Decode_1.decodeDepthRgb);
            frag.addFunction(exports.readDepthAndOrder);
            frag.set(0 /* FragmentShaderComponent.CheckForEarlyDiscard */, checkForEarlySurfaceDiscard);
        }
        else {
            frag.addUniform("u_checkInterElementDiscard", 0 /* VariableType.Boolean */, (prog) => {
                prog.addGraphicUniform("u_checkInterElementDiscard", (uniform, params) => {
                    uniform.setUniform1i(params.target.uniforms.branch.top.is3d ? 1 : 0);
                });
            });
            addFeatureIndex(vert);
            (0, Vertex_1.addLineWeight)(vert);
            addSamplers(frag, true);
            addRenderOrderConstants(frag);
            addPixelWidthFactor(frag);
            frag.addFunction(Fragment_1.computeLinearDepth);
            frag.addFunction(Decode_1.decodeDepthRgb);
            frag.addFunction(exports.readDepthAndOrder);
            frag.set(0 /* FragmentShaderComponent.CheckForEarlyDiscard */, checkForEarlySurfaceDiscardWithFeatureID);
            builder.addInlineComputedVarying("v_lineWeight", 2 /* VariableType.Float */, "v_lineWeight = computeLineWeight();");
            addFeatureId(builder, computeIdInFrag);
        }
        addRenderOrder(frag);
        (0, RenderPass_1.addRenderPass)(frag);
    }
    else if (isClassified && 0 /* FeatureMode.None */ !== feat) {
        addFeatureIndex(vert);
        addFeatureId(builder, computeIdInFrag);
        if (!flags.isTranslucent)
            addRenderOrder(frag);
    }
}
// bool feature_invisible = false;
// vec3 feature_rgb; // if not overridden, .r < 0; else rgb color override
// float feature_alpha // alpha if overridden, else < 0
// varying float v_feature_emphasis // bitmask - see kEmph_* constants
// vec4 linear_feature_overrides; // x: weight overridden y: weight z: line code overridden w: line code
const computeFeatureOverrides = `
  feature_rgb = vec3(-1.0);
  feature_alpha = -1.0;
  vec4 value = getFirstFeatureRgba();

  float emphFlags = value.y * 256.0;
  if (nthFeatureBitSet(emphFlags, kOvrBit_InvisibleDuringPick)) {
    feature_invisible = true;
    return;
  }
    
  v_feature_emphasis = kEmphFlag_Hilite * extractNthBit(emphFlags, kOvrBit_Hilited) + kEmphFlag_Emphasize * extractNthBit(emphFlags, kOvrBit_Emphasized);

  float flags = value.x * 256.0;
  if (0.0 == flags && 0.0 == emphFlags)
    return; // nothing overridden for this feature

  bool nonLocatable = (u_shaderFlags[kShaderBit_IgnoreNonLocatable] ? nthFeatureBitSet(flags, kOvrBit_NonLocatable) : false);
  v_feature_emphasis += kEmphFlag_NonLocatable * float(nthFeatureBitSet(flags, kOvrBit_NonLocatable));
  bool invisible = nthFeatureBitSet(emphFlags, kOvrBit_Visibility);
  feature_invisible = invisible || nonLocatable;
  if (feature_invisible)
    return;

  bool isLinear = u_renderOrder == kRenderOrder_Linear || u_renderOrder == kRenderOrder_PlanarLinear || u_renderOrder == kRenderOrder_PlanarEdge;
  bool rgbOverridden = isLinear ? nthFeatureBitSet(flags, kOvrBit_LineRgb) : nthFeatureBitSet(flags, kOvrBit_Rgb);
  bool alphaOverridden = isLinear ? nthFeatureBitSet(flags, kOvrBit_LineAlpha) : nthFeatureBitSet(flags, kOvrBit_Alpha);
  if (alphaOverridden || rgbOverridden) {
    vec4 rgba = getSecondFeatureRgba(isLinear);
    if (rgbOverridden)
      feature_rgb = rgba.rgb;

    if (alphaOverridden) {
      feature_alpha = rgba.a;
      feature_viewIndependentTransparency = nthFeatureBitSet(emphFlags, kOvrBit_ViewIndependentTransparency);
    }
  }

  linear_feature_overrides = vec4(nthFeatureBitSet(flags, kOvrBit_Weight),
                                  value.w * 256.0,
                                  nthFeatureBitSet(flags, kOvrBit_LineCode),
                                  value.z * 256.0);

  feature_ignore_material = nthFeatureBitSet(emphFlags, kOvrBit_IgnoreMaterial);
  use_material = use_material && !feature_ignore_material;

  v_feature_emphasis += kEmphFlag_Flash * extractNthFeatureBit(flags, kOvrBit_Flashed);
`;
// feature_rgb.r = -1.0 if rgb color not overridden for feature.
// feature_alpha = -1.0 if alpha not overridden for feature.
const applyFeatureColor = `
  vec3 rgb = mix(baseColor.rgb, feature_rgb.rgb, step(0.0, feature_rgb.r));
  float alpha = mix(baseColor.a, feature_alpha, step(0.0, feature_alpha));
  return vec4(rgb, alpha);
`;
// feature_rgb.r = -1.0 if rgb color not overridden for feature, else mix based on u_overrrideColorMix.
// feature_alpha = -1.0 if alpha not overridden for feature.
exports.mixFeatureColor = `
  vec3 rgb = mix(baseColor.rgb, mix(baseColor.rgb, feature_rgb.rgb, u_overrideColorMix), step(0.0, feature_rgb.r));
  float alpha = mix(baseColor.a, feature_alpha, step(0.0, feature_alpha));
  return vec4(rgb, alpha);
  `;
const applyFlash = `
  float flashHilite = floor(v_feature_emphasis + 0.5);
  return doApplyFlash(flashHilite, baseColor);
`;
const doApplyFlash = `
vec4 doApplyFlash(float flags, vec4 baseColor) {
  bool isFlashed = nthBitSet(flags, kEmphBit_Flash);
  bool isHilited = nthBitSet(flags, kEmphBit_Hilite);
  bool isEmphasized = !isHilited && nthBitSet(flags, kEmphBit_Emphasize);
  vec3 hiliteRgb = isEmphasized ? u_hilite_settings[1] : u_hilite_settings[0];

  isHilited = isEmphasized || isHilited;
  float hiliteRatio = isHilited ? (isEmphasized ? u_hilite_settings[2][1] : u_hilite_settings[2][0]) : 0.0;
  baseColor.rgb = mix(baseColor.rgb, hiliteRgb, hiliteRatio);

  const float maxBrighten = 0.2;
  float brighten = isFlashed ? u_flash_intensity * maxBrighten : 0.0;
  vec3 brightRgb = baseColor.rgb + brighten;

  const float maxTween = 0.75;
  float hiliteFraction = isFlashed ? u_flash_intensity * maxTween : 0.0;
  vec3 tweenRgb = baseColor.rgb * (1.0 - hiliteFraction);
  tweenRgb += u_hilite_settings[0] * hiliteFraction;

  return vec4(mix(tweenRgb, brightRgb, u_flash_mode), baseColor.a);
}
`;
const doClassifierFlash = `
vec4 applyClassifierFlash(vec4 baseColor) {
  const float maxBrighten = 0.2;
  float brighten = u_flash_intensity * maxBrighten;
  vec3 brightRgb = baseColor.rgb + brighten;
  return vec4(brightRgb, baseColor.a);
}
`;
/** @internal */
function addClassifierFlash(frag) {
    addFlashIntensity(frag);
    addHiliteSettings(frag, false);
    frag.addFunction(doClassifierFlash);
}
function addFlashIntensity(frag) {
    frag.addUniform("u_flash_intensity", 2 /* VariableType.Float */, (prog) => {
        prog.addProgramUniform("u_flash_intensity", (uniform, params) => {
            uniform.setUniform1f(params.target.flashIntensity);
        });
    });
}
function addApplyFlash(frag) {
    addHiliteSettings(frag, true);
    addEmphasisFlags(frag);
    (0, Common_1.addExtractNthBit)(frag);
    frag.addFunction(doApplyFlash);
    frag.set(12 /* FragmentShaderComponent.ApplyFlash */, applyFlash);
    addFlashIntensity(frag);
}
/** @internal */
function addFeatureSymbology(builder, feat, opts) {
    if (!addCommon(builder, feat, opts) || 0 /* FeatureSymbologyOptions.None */ === opts)
        return;
    (0, core_bentley_1.assert)((4 /* FeatureSymbologyOptions.HasOverrides */ | 8 /* FeatureSymbologyOptions.Color */) === (opts & (4 /* FeatureSymbologyOptions.HasOverrides */ | 8 /* FeatureSymbologyOptions.Color */)));
    builder.addGlobal("feature_rgb", 4 /* VariableType.Vec3 */);
    builder.addGlobal("feature_alpha", 2 /* VariableType.Float */);
    builder.addVarying("v_feature_emphasis", 2 /* VariableType.Float */);
    const vert = builder.vert;
    vert.addGlobal("feature_invisible", 0 /* VariableType.Boolean */, "false");
    vert.addGlobal("feature_viewIndependentTransparency", 0 /* VariableType.Boolean */, "false");
    addEmphasisFlags(vert);
    vert.addGlobal("use_material", 0 /* VariableType.Boolean */, "true");
    addRenderOrder(vert);
    addRenderOrderConstants(vert);
    vert.set(3 /* VertexShaderComponent.ComputeFeatureOverrides */, computeFeatureOverrides);
    vert.set(7 /* VertexShaderComponent.ApplyFeatureColor */, applyFeatureColor);
    addApplyFlash(builder.frag);
}
/** If we're running the hilite shader for a uniform feature, it follows that the feature must be hilited.
 * So the hilite shader simply needs to output '1' for every fragment.
 * @internal
 */
function addUniformHiliter(builder) {
    builder.frag.addUniform("v_feature_hilited", 2 /* VariableType.Float */, (prog) => {
        prog.addGraphicUniform("v_feature_hilited", (uniform, params) => {
            params.target.uniforms.batch.bindUniformSymbologyFlags(uniform);
        });
    });
    addEmphasisFlags(builder.frag);
    (0, Common_1.addExtractNthBit)(builder.frag);
    builder.frag.set(1 /* FragmentShaderComponent.ComputeBaseColor */, computeHiliteColor);
    builder.frag.set(17 /* FragmentShaderComponent.AssignFragData */, Fragment_1.assignFragColor);
}
/** For a uniform feature table, the feature ID output to pick buffers is equal to the batch ID.
 * The following symbology overrides are supported:
 *  - Visibility - implcitly, because if the feature is invisible its geometry will never be drawn.
 *  - Flash
 *  - Hilite
 *  - Color and Transparency- only for point clouds currently which set addFeatureColor to true.
 * This shader could be simplified, but want to share code with the non-uniform versions...hence uniforms/globals with "v_" prefix typically used for varyings on no prefix...
 * @internal
 */
function addUniformFeatureSymbology(builder, addFeatureColor) {
    builder.vert.addGlobal("g_featureIndex", 4 /* VariableType.Vec3 */, "vec3(0.0)", true);
    builder.frag.addUniform("v_feature_emphasis", 2 /* VariableType.Float */, (prog) => {
        prog.addGraphicUniform("v_feature_emphasis", (uniform, params) => {
            params.target.uniforms.batch.bindUniformSymbologyFlags(uniform);
        });
    });
    if (addFeatureColor) {
        builder.vert.addUniform("feature_rgb", 4 /* VariableType.Vec3 */, (prog) => {
            prog.addGraphicUniform("feature_rgb", (uniform, params) => {
                params.target.uniforms.batch.bindUniformColorOverride(uniform);
            });
        });
        builder.vert.addUniform("feature_alpha", 2 /* VariableType.Float */, (prog) => {
            prog.addGraphicUniform("feature_alpha", (uniform, params) => {
                params.target.uniforms.batch.bindUniformTransparencyOverride(uniform);
            });
        });
        builder.vert.set(7 /* VertexShaderComponent.ApplyFeatureColor */, applyFeatureColor);
        (0, Vertex_1.addAlpha)(builder.vert);
        addMaxAlpha(builder.vert);
        (0, RenderPass_1.addRenderPass)(builder.vert);
        addTransparencyDiscardFlags(builder.vert);
        builder.vert.set(9 /* VertexShaderComponent.CheckForDiscard */, checkVertexDiscard);
    }
    else {
        builder.vert.set(9 /* VertexShaderComponent.CheckForDiscard */, "return feature_invisible;");
    }
    // Non-Locatable...  Discard if picking
    builder.vert.addUniform("feature_invisible", 0 /* VariableType.Boolean */, (prog) => {
        prog.addGraphicUniform("feature_invisible", (uniform, params) => {
            params.target.uniforms.batch.bindUniformNonLocatable(uniform, params.target.drawNonLocatable);
        });
    });
    builder.vert.addGlobal("feature_viewIndependentTransparency", 0 /* VariableType.Boolean */, "false");
    addApplyFlash(builder.frag);
}
//# sourceMappingURL=FeatureSymbology.js.map