/**
 * Copyright (c) 2019-2026 mol* contributors, licensed under MIT, See LICENSE file for more info.
 *
 * @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
 * @author Alexander Rose <alexander.rose@weirdbyte.de>
 * @author Gianluca Tomasello <giagitom@gmail.com>
 * @author Sebastian Bittrich <sebastian.m.bittrich@gmail.com>
 */
export declare const outlines_frag = "\nprecision highp float;\nprecision highp int;\nprecision highp sampler2D;\n\nuniform sampler2D tDepthOpaque;\nuniform sampler2D tDepthTransparent;\nuniform vec2 uTexSize;\n\nuniform float uNear;\nuniform float uFar;\nuniform mat4 uInvProjection;\n\nuniform float uOutlineThreshold;\n\n#include common\n\nfloat getViewZ(const in float depth) {\n    #if dOrthographic == 1\n        return orthographicDepthToViewZ(depth, uNear, uFar);\n    #else\n        return perspectiveDepthToViewZ(depth, uNear, uFar);\n    #endif\n}\n\nfloat getDepthOpaque(const in vec2 coords) {\n    #ifdef depthTextureSupport\n        return texture2D(tDepthOpaque, coords).r;\n    #else\n        return unpackRGBAToDepth(texture2D(tDepthOpaque, coords));\n    #endif\n}\n\nvec2 getDepthTransparentWithAlpha(const in vec2 coords) {\n    #ifdef dTransparentOutline\n        return unpackRGBAToDepthWithAlpha(texture2D(tDepthTransparent, coords));\n    #else\n        return vec2(1.0, 0.0);\n    #endif\n}\n\nbool isBackground(const in float depth) {\n    return depth == 1.0;\n}\n\nfloat getPixelSize(const in vec2 coords, const in float depth) {\n    vec3 viewPos0 = screenSpaceToViewSpace(vec3(coords, depth), uInvProjection);\n    vec3 viewPos1 = screenSpaceToViewSpace(vec3(coords + vec2(1.0, 0.0) / uTexSize, depth), uInvProjection);\n    return distance(viewPos0, viewPos1);\n}\n\nvoid main(void) {\n    float backgroundViewZ = 2.0 * uFar;\n\n    vec2 coords = gl_FragCoord.xy / uTexSize;\n    vec2 invTexSize = 1.0 / uTexSize;\n\n    float selfDepthOpaque = getDepthOpaque(coords);\n    float selfViewZOpaque = isBackground(selfDepthOpaque) ? backgroundViewZ : getViewZ(selfDepthOpaque);\n    float pixelSizeOpaque = getPixelSize(coords, selfDepthOpaque) * uOutlineThreshold;\n\n    vec2 selfDepthTransparentWithAlpha = getDepthTransparentWithAlpha(coords);\n    float selfDepthTransparent = selfDepthTransparentWithAlpha.x;\n    float selfViewZTransparent = isBackground(selfDepthTransparent) ? backgroundViewZ : getViewZ(selfDepthTransparent);\n    float pixelSizeTransparent = getPixelSize(coords, selfDepthTransparent) * uOutlineThreshold;\n\n    float bestOpaqueDepth = 1.0;\n    float bestTransparentDepth = 1.0;\n    float bestTransparentAlpha = 0.0;\n\n    float opaqueOutlineFlag = 0.0;\n    float transparentOutlineFlag = 0.0;\n\n    for (int y = -1; y <= 1; y++) {\n        for (int x = -1; x <= 1; x++) {\n            vec2 sampleCoords = coords + vec2(float(x), float(y)) * invTexSize;\n\n            // Opaque\n            float sampleDepthOpaque = getDepthOpaque(sampleCoords);\n            float sampleViewZOpaque = isBackground(sampleDepthOpaque) ? backgroundViewZ : getViewZ(sampleDepthOpaque);\n            if (abs(selfViewZOpaque - sampleViewZOpaque) > pixelSizeOpaque && selfDepthOpaque > sampleDepthOpaque && sampleDepthOpaque <= bestOpaqueDepth) {\n                bestOpaqueDepth = sampleDepthOpaque;\n                opaqueOutlineFlag = 1.0;\n            }\n\n            // Transparent\n            vec2 sampleDepthTransparentWithAlpha = getDepthTransparentWithAlpha(sampleCoords);\n            float sampleDepthTransparent = sampleDepthTransparentWithAlpha.x;\n            float sampleAlphaTransparent = sampleDepthTransparentWithAlpha.y;\n            float sampleViewZTransparent = isBackground(sampleDepthTransparent) ? backgroundViewZ : getViewZ(sampleDepthTransparent);\n            if (abs(selfViewZTransparent - sampleViewZTransparent) > pixelSizeTransparent && selfDepthTransparent > sampleDepthTransparent && sampleDepthTransparent <= bestTransparentDepth) {\n                bestTransparentDepth = sampleDepthTransparent;\n                bestTransparentAlpha = sampleAlphaTransparent;\n                transparentOutlineFlag = 1.0;\n            }\n        }\n    }\n\n    if (transparentOutlineFlag > 0.0 && abs(selfViewZOpaque - getViewZ(bestTransparentDepth)) < pixelSizeOpaque) { // Disable transparent outline close to opaque elements\n        transparentOutlineFlag = 0.0;\n    }\n\n    // lazy curvature veto: only compute if an outline was found and center is not background\n    const float kCurvatureGate = 0.75;\n    vec2 dx = vec2(invTexSize.x, 0.0);\n    vec2 dy = vec2(0.0, invTexSize.y);\n\n    if (opaqueOutlineFlag > 0.0 && !isBackground(selfDepthOpaque)) {\n        float dL = getDepthOpaque(coords - dx);\n        float dR = getDepthOpaque(coords + dx);\n        float dU = getDepthOpaque(coords + dy);\n        float dD = getDepthOpaque(coords - dy);\n\n        float vzL = isBackground(dL) ? backgroundViewZ : getViewZ(dL);\n        float vzR = isBackground(dR) ? backgroundViewZ : getViewZ(dR);\n        float vzU = isBackground(dU) ? backgroundViewZ : getViewZ(dU);\n        float vzD = isBackground(dD) ? backgroundViewZ : getViewZ(dD);\n\n        float ddx = abs(vzL + vzR - 2.0 * selfViewZOpaque);\n        float ddy = abs(vzU + vzD - 2.0 * selfViewZOpaque);\n        float curvOpaque = max(ddx, ddy);\n\n        if (curvOpaque < pixelSizeOpaque * kCurvatureGate) {\n            opaqueOutlineFlag = 0.0;\n            bestOpaqueDepth = 1.0;\n        }\n    }\n\n    if (transparentOutlineFlag > 0.0 && !isBackground(selfDepthTransparent)) {\n        vec2 daL = getDepthTransparentWithAlpha(coords - dx);\n        vec2 daR = getDepthTransparentWithAlpha(coords + dx);\n        vec2 daU = getDepthTransparentWithAlpha(coords + dy);\n        vec2 daD = getDepthTransparentWithAlpha(coords - dy);\n\n        float dL = daL.x;\n        float dR = daR.x;\n        float dU = daU.x;\n        float dD = daD.x;\n\n        float vzL = isBackground(dL) ? backgroundViewZ : getViewZ(dL);\n        float vzR = isBackground(dR) ? backgroundViewZ : getViewZ(dR);\n        float vzU = isBackground(dU) ? backgroundViewZ : getViewZ(dU);\n        float vzD = isBackground(dD) ? backgroundViewZ : getViewZ(dD);\n\n        float ddx = abs(vzL + vzR - 2.0 * selfViewZTransparent);\n        float ddy = abs(vzU + vzD - 2.0 * selfViewZTransparent);\n        float curvTransparent = max(ddx, ddy);\n\n        if (curvTransparent < pixelSizeTransparent * kCurvatureGate) {\n            transparentOutlineFlag = 0.0;\n            bestTransparentDepth = 1.0;\n            bestTransparentAlpha = 0.0;\n        }\n    }\n\n    vec2 depthPacked; // Pack depth in G/B channels\n    float outlineTypeFlag = 0.0;\n    if (opaqueOutlineFlag > 0.0 && transparentOutlineFlag > 0.0) {\n        outlineTypeFlag = 0.75; // Both\n        depthPacked = packUnitIntervalToRG(bestOpaqueDepth);\n    } else if (transparentOutlineFlag > 0.0) {\n        outlineTypeFlag = 0.5;  // Transparent only\n        depthPacked = packUnitIntervalToRG(bestTransparentDepth);\n    } else if (opaqueOutlineFlag > 0.0) {\n        outlineTypeFlag = 0.25; // Opaque only\n        depthPacked = packUnitIntervalToRG(bestOpaqueDepth);\n    }\n\n    float alpha = clamp(bestTransparentAlpha, 0.0, 0.5) * 2.0; // limiting to range [0.0, 0.5] to improve alpha precision since we don't need a wider range\n    float packedFlagWithAlpha = pack2x4(vec2(outlineTypeFlag, alpha)); // pack outlineType with alpha\n    gl_FragColor = vec4(packedFlagWithAlpha, depthPacked.x, depthPacked.y, bestTransparentDepth);\n}\n";
