"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.lateVertexDiscard = exports.vertexDiscard = exports.earlyVertexDiscard = exports.unquantizePosition = void 0;
exports.addSamplePosition = addSamplePosition;
exports.addModelViewProjectionMatrix = addModelViewProjectionMatrix;
exports.addProjectionMatrix = addProjectionMatrix;
exports.addInstancedRtcMatrix = addInstancedRtcMatrix;
exports.addModelViewMatrix = addModelViewMatrix;
exports.addNormalMatrix = addNormalMatrix;
exports.addPosition = addPosition;
exports.addAlpha = addAlpha;
exports.addLineWeight = addLineWeight;
exports.replaceLineWeight = replaceLineWeight;
exports.addLineCode = addLineCode;
exports.replaceLineCode = replaceLineCode;
const core_bentley_1 = require("@itwin/core-bentley");
const Matrix_1 = require("../Matrix");
const RenderFlags_1 = require("../RenderFlags");
const Decode_1 = require("./Decode");
const Instancing_1 = require("./Instancing");
const LookupTable_1 = require("./LookupTable");
const initializeVertLUTCoords = `
  g_vertexLUTIndex = decodeUInt24(qpos);
  g_vertexBaseCoords = compute_vert_coords(g_vertexLUTIndex);
`;
/** @internal */
exports.unquantizePosition = `
vec4 unquantizePosition(vec3 pos, vec3 origin, vec3 scale) { return vec4(origin + scale * pos, 1.0); }
`;
const computeQuantizedPosition = `
vec4 computeVertexPosition(vec3 pos) { return unquantizePosition(pos, u_qOrigin, u_qScale); }
`;
// Need to read 2 rgba values to obtain 6 16-bit integers for position
const computeVertexPositionFromLUT = `
vec4 computeVertexPosition(vec3 encodedIndex) {
  vec3 qpos = vec3(decodeUInt16(g_vertLutData0.xy), decodeUInt16(g_vertLutData0.zw), decodeUInt16(g_vertLutData1.xy));
  g_featureAndMaterialIndex = g_vertLutData2;
  return unquantizePosition(qpos, u_qOrigin, u_qScale);
}
`;
const computeUnquantizedPosition = `
vec4 computeVertexPosition(vec3 encodedIndex) {
  uvec3 vux = uvec3(g_vertLutData0.xyz);
  g_featureAndMaterialIndex.x = g_vertLutData0.w;
  uvec3 vuy = uvec3(g_vertLutData1.xyz);
  g_featureAndMaterialIndex.y = g_vertLutData1.w;
  uvec3 vuz = uvec3(g_vertLutData2.xyz);
  g_featureAndMaterialIndex.z = g_vertLutData2.w;
  uvec3 vuw = uvec3(g_vertLutData3.xyz);
  g_featureAndMaterialIndex.w = g_vertLutData3.w;
  uvec3 u = (vuw << 24) | (vuz << 16) | (vuy << 8) | vux;
  return vec4(uintBitsToFloat(u), 1.0);
}
`;
const computeLineWeight = "\nfloat computeLineWeight() { return g_lineWeight; }\n";
const computeLineCode = "\nfloat computeLineCode() { return g_lineCode; }\n";
function addSamplePosition(vert) {
    vert.addFunction(getSamplePosition(vert.positionType));
}
const getSamplePositionPrelude = `
vec4 samplePosition(float index) {
  vec2 tc = compute_vert_coords(index);`;
const getSamplePositionQuantizedPostlude = `
  vec4 e0 = floor(TEXTURE(u_vertLUT, tc) * 255.0 + 0.5);
  tc.x += g_vert_stepX;
  vec4 e1 = floor(TEXTURE(u_vertLUT, tc) * 255.0 + 0.5);
  vec3 qpos = vec3(decodeUInt16(e0.xy), decodeUInt16(e0.zw), decodeUInt16(e1.xy));
  return unquantizePosition(qpos, u_qOrigin, u_qScale);
}
`;
const getSamplePositionUnquantizedPostlude = `
  uvec3 vux = uvec3(floor(TEXTURE(u_vertLUT, tc).xyz * 255.0 + 0.5));
  tc.x += g_vert_stepX;
  uvec3 vuy = uvec3(floor(TEXTURE(u_vertLUT, tc).xyz * 255.0 + 0.5));
  tc.x += g_vert_stepX;
  uvec3 vuz = uvec3(floor(TEXTURE(u_vertLUT, tc).xyz * 255.0 + 0.5));
  tc.x += g_vert_stepX;
  uvec3 vuw = uvec3(floor(TEXTURE(u_vertLUT, tc).xyz * 255.0 + 0.5));
  uvec3 u = (vuw << 24) | (vuz << 16) | (vuy << 8) | vux;
  return vec4(uintBitsToFloat(u), 1.0);
}
`;
function getSamplePosition(type) {
    return `${getSamplePositionPrelude}${"quantized" === type ? getSamplePositionQuantizedPostlude : getSamplePositionUnquantizedPostlude}`;
}
/** @internal */
function addModelViewProjectionMatrix(vert) {
    if (vert.usesInstancedGeometry) {
        addModelViewMatrix(vert);
        addProjectionMatrix(vert);
        vert.addGlobal("g_mvp", 7 /* VariableType.Mat4 */);
        vert.addInitializer("g_mvp = u_proj * g_mv;");
    }
    else {
        vert.addUniform("u_mvp", 7 /* VariableType.Mat4 */, (prog) => {
            prog.addGraphicUniform("u_mvp", (uniform, params) => {
                params.target.uniforms.branch.bindModelViewProjectionMatrix(uniform, params.geometry, params.isViewCoords);
            });
        });
    }
}
/** @internal */
function addProjectionMatrix(vert) {
    vert.addUniform("u_proj", 7 /* VariableType.Mat4 */, (prog) => {
        prog.addProgramUniform("u_proj", (uniform, params) => {
            params.bindProjectionMatrix(uniform);
        });
    });
}
const computeInstancedRtcMatrix = `
  g_instancedRtcMatrix = u_instanced_rtc * g_modelMatrixRTC;
`;
/** @internal */
function addInstancedRtcMatrix(vert) {
    if (!vert.usesInstancedGeometry)
        return;
    (0, core_bentley_1.assert)(undefined !== vert.find("g_modelMatrixRTC")); // set up in VertexShaderBuilder constructor...
    vert.addUniform("u_instanced_rtc", 7 /* VariableType.Mat4 */, (prog) => {
        prog.addGraphicUniform("u_instanced_rtc", (uniform, params) => {
            const modelt = params.geometry.asInstanced.getRtcOnlyTransform();
            uniform.setMatrix4(Matrix_1.Matrix4.fromTransform(modelt));
        });
    });
    vert.addGlobal("g_instancedRtcMatrix", 7 /* VariableType.Mat4 */);
    vert.addInitializer(computeInstancedRtcMatrix);
}
/** @internal */
function addModelViewMatrix(vert) {
    const bind = (uniform, params) => {
        params.target.uniforms.branch.bindModelViewMatrix(uniform, params.geometry, params.isViewCoords);
    };
    if (vert.usesInstancedGeometry) {
        vert.addUniform("u_instanced_modelView", 7 /* VariableType.Mat4 */, (prog) => {
            prog.addGraphicUniform("u_instanced_modelView", bind);
        });
        vert.addGlobal("g_mv", 7 /* VariableType.Mat4 */);
        vert.addInitializer("g_mv = u_instanced_modelView * g_modelMatrixRTC;");
    }
    else {
        vert.addUniform("u_mv", 7 /* VariableType.Mat4 */, (prog) => {
            // ###TODO: We only need 3 rows, not 4...
            prog.addGraphicUniform("u_mv", bind);
        });
    }
}
const computeNormalMatrix = `
  g_nmx = transpose(inverse(mat3(MAT_MV)));
  g_nmx[0][0] *= u_frustumScale.x;
  g_nmx[1][1] *= u_frustumScale.y;
`;
/** @internal */
function addNormalMatrix(vert) {
    vert.addGlobal("g_nmx", 6 /* VariableType.Mat3 */);
    vert.addUniform("u_frustumScale", 3 /* VariableType.Vec2 */, (prog) => {
        prog.addGraphicUniform("u_frustumScale", (uniform, params) => {
            const scale = params.target.uniforms.branch.top.frustumScale;
            uniform.setUniform2fv([scale.x, scale.y]);
        });
    });
    vert.addInitializer(computeNormalMatrix);
}
function readVertexData(index) {
    return `g_vertLutData${index} = floor(TEXTURE(u_vertLUT, tc) * 255.0 + 0.5);`;
}
const nextVertexData = "tc.x += g_vert_stepX;";
function readNextVertexData(index) {
    return `
  ${nextVertexData}
  ${readVertexData(index)}`;
}
const prereadVertexDataPrelude = `
  vec2 tc = g_vertexBaseCoords;
  ${readVertexData(0)}
  ${readNextVertexData(1)}
  ${readNextVertexData(2)}
`;
const prereadQuantizedVertexData = `${prereadVertexDataPrelude}
  if (3.0 < u_vertParams.z) {
    ${readNextVertexData(3)}
  }
`;
const prereadUnquantizedVertexData = `${prereadVertexDataPrelude}
  ${readNextVertexData(3)}
  ${readNextVertexData(4)}
  if (5.0 < u_vertParams.z) {
    ${readNextVertexData(5)}
  }
`;
const scratchLutParams = new Float32Array(4);
function addPositionFromLUT(vert) {
    vert.addGlobal("g_vertexLUTIndex", 2 /* VariableType.Float */);
    vert.addGlobal("g_vertexBaseCoords", 3 /* VariableType.Vec2 */);
    const unquantized = "unquantized" === vert.positionType;
    const maxRgbaPerVert = unquantized ? 6 : 4;
    for (let i = 0; i < maxRgbaPerVert; i++)
        vert.addGlobal(`g_vertLutData${i}`, 5 /* VariableType.Vec4 */);
    vert.addFunction(Decode_1.decodeUint24);
    vert.addFunction(Decode_1.decodeUint16);
    vert.addFunction(unquantized ? computeUnquantizedPosition : computeVertexPositionFromLUT);
    vert.addUniform("u_vertLUT", 8 /* VariableType.Sampler2D */, (prog) => {
        prog.addGraphicUniform("u_vertLUT", (uniform, params) => {
            (params.geometry.asLUT).lut.texture.bindSampler(uniform, RenderFlags_1.TextureUnit.VertexLUT);
        });
    });
    vert.addUniform("u_vertParams", 5 /* VariableType.Vec4 */, (prog) => {
        prog.addGraphicUniform("u_vertParams", (uniform, params) => {
            (0, core_bentley_1.assert)(undefined !== params.geometry.asLUT);
            const lut = params.geometry.asLUT.lut;
            const lutParams = scratchLutParams;
            lutParams[0] = lut.texture.width;
            lutParams[1] = lut.texture.height;
            lutParams[2] = lut.numRgbaPerVertex;
            lutParams[3] = lut.numVertices;
            uniform.setUniform4fv(lutParams);
        });
    });
    (0, LookupTable_1.addLookupTable)(vert, "vert", "u_vertParams.z");
    vert.addInitializer(initializeVertLUTCoords);
    vert.addGlobal("g_featureAndMaterialIndex", 5 /* VariableType.Vec4 */);
    // Read the vertex data from the vertex table up front.  Yields a consistent (if unexplainable) small performance boost.
    vert.addInitializer(unquantized ? prereadUnquantizedVertexData : prereadQuantizedVertexData);
}
/** @internal */
function addPosition(vert, fromLUT) {
    if (!fromLUT || "quantized" === vert.positionType) {
        vert.addFunction(exports.unquantizePosition);
        vert.addUniform("u_qScale", 4 /* VariableType.Vec3 */, (prog) => {
            prog.addGraphicUniform("u_qScale", (uniform, params) => {
                (0, core_bentley_1.assert)(params.geometry.usesQuantizedPositions);
                uniform.setUniform3fv(params.geometry.qScale);
            });
        });
        vert.addUniform("u_qOrigin", 4 /* VariableType.Vec3 */, (prog) => {
            prog.addGraphicUniform("u_qOrigin", (uniform, params) => {
                (0, core_bentley_1.assert)(params.geometry.usesQuantizedPositions);
                uniform.setUniform3fv(params.geometry.qOrigin);
            });
        });
    }
    if (!fromLUT) {
        vert.addFunction(computeQuantizedPosition);
    }
    else {
        addPositionFromLUT(vert);
    }
}
/** @internal */
function addAlpha(vert) {
    vert.addUniform("u_hasAlpha", 2 /* VariableType.Float */, (prog) => {
        prog.addGraphicUniform("u_hasAlpha", (uniform, params) => {
            uniform.setUniform1f(RenderFlags_1.Pass.rendersTranslucent(params.geometry.getPass(params.target)) ? 1 : 0);
        });
    });
}
/** @internal */
function addLineWeight(vert) {
    vert.addUniform("u_lineWeight", 2 /* VariableType.Float */, (prog) => {
        prog.addGraphicUniform("u_lineWeight", (attr, params) => {
            attr.setUniform1f(params.geometry.getLineWeight(params.programParams));
        });
    });
    vert.addGlobal("g_lineWeight", 2 /* VariableType.Float */);
    if (vert.usesInstancedGeometry) {
        (0, Instancing_1.addInstanceOverrides)(vert);
        vert.addInitializer("g_lineWeight = mix(u_lineWeight, a_instanceOverrides.g, extractInstanceBit(kOvrBit_Weight));");
    }
    else {
        vert.addInitializer("g_lineWeight = u_lineWeight;");
    }
    vert.addFunction(computeLineWeight);
}
/** @internal */
function replaceLineWeight(vert, func) {
    vert.replaceFunction(computeLineWeight, func);
}
/** @internal */
function addLineCode(vert) {
    vert.addUniform("u_lineCode", 2 /* VariableType.Float */, (prog) => {
        prog.addGraphicUniform("u_lineCode", (attr, params) => {
            attr.setUniform1f(params.geometry.getLineCode(params.programParams));
        });
    });
    vert.addGlobal("g_lineCode", 2 /* VariableType.Float */);
    if (vert.usesInstancedGeometry) {
        (0, Instancing_1.addInstanceOverrides)(vert);
        vert.addInitializer("g_lineCode = mix(u_lineCode, a_instanceOverrides.b, extractInstanceBit(kOvrBit_LineCode));");
    }
    else {
        vert.addInitializer("g_lineCode = u_lineCode;");
    }
    vert.addFunction(computeLineCode);
}
/** @internal */
function replaceLineCode(vert, func) {
    vert.replaceFunction(computeLineCode, func);
}
// This vertex belongs to a triangle which should not be rendered. Produce a degenerate triangle.
// Also place it outside NDC range (for GL_POINTS)
const discardVertex = ` {
    gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
    return;
  }
`;
/** @internal */
exports.earlyVertexDiscard = `  if (checkForEarlyDiscard(rawPosition))${discardVertex}`;
/** @internal */
exports.vertexDiscard = `  if (checkForDiscard())${discardVertex}`;
/** @internal */
exports.lateVertexDiscard = `  if (checkForLateDiscard())${discardVertex}`;
//# sourceMappingURL=Vertex.js.map