"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.SurfaceGeometry = void 0;
exports.wantMaterials = wantMaterials;
const core_bentley_1 = require("@itwin/core-bentley");
const core_common_1 = require("@itwin/core-common");
const SurfaceParams_1 = require("../../common/internal/render/SurfaceParams");
const AttributeMap_1 = require("./AttributeMap");
const GL_1 = require("./GL");
const AttributeBuffers_1 = require("./AttributeBuffers");
const System_1 = require("./System");
const MeshGeometry_1 = require("./MeshGeometry");
/** @internal */
function wantMaterials(vf) {
    return vf.materials && core_common_1.RenderMode.SmoothShade === vf.renderMode;
}
function wantLighting(vf) {
    return core_common_1.RenderMode.SmoothShade === vf.renderMode && vf.lighting;
}
/** @internal */
class SurfaceGeometry extends MeshGeometry_1.MeshGeometry {
    get lutBuffers() { return this._buffers; }
    static create(mesh, indices) {
        const indexBuffer = AttributeBuffers_1.BufferHandle.createArrayBuffer(indices.data);
        return undefined !== indexBuffer ? new SurfaceGeometry(indexBuffer, indices.length, mesh) : undefined;
    }
    get isDisposed() {
        return this._buffers.isDisposed
            && this._indices.isDisposed;
    }
    dispose() {
        (0, core_bentley_1.dispose)(this._buffers);
        (0, core_bentley_1.dispose)(this._indices);
    }
    collectStatistics(stats) {
        stats.addSurface(this._indices.bytesUsed);
    }
    get isLit() { return SurfaceParams_1.SurfaceType.Lit === this.surfaceType || SurfaceParams_1.SurfaceType.TexturedLit === this.surfaceType; }
    get isTexturedType() { return SurfaceParams_1.SurfaceType.Textured === this.surfaceType || SurfaceParams_1.SurfaceType.TexturedLit === this.surfaceType; }
    get hasTexture() { return this.isTexturedType && undefined !== this.texture; }
    get hasNormalMap() { return this.isLit && this.isTexturedType && undefined !== this.normalMap; }
    get isGlyph() { return this.mesh.isGlyph; }
    get alwaysRenderTranslucent() { return this.isGlyph; }
    get isTileSection() { return undefined !== this.texture && this.texture.isTileSection; }
    get isClassifier() { return SurfaceParams_1.SurfaceType.VolumeClassifier === this.surfaceType; }
    get supportsThematicDisplay() {
        return !this.isGlyph;
    }
    get allowColorOverride() {
        // Text background color should not be overridden by feature symbology overrides - otherwise it becomes unreadable...
        // We don't actually know if we have text.
        // We do know that text background color uses blanking fill. So do ImageGraphics, so they're also going to forbid overriding their color.
        return core_common_1.FillFlags.Blanking !== (this.fillFlags & core_common_1.FillFlags.Blanking);
    }
    get asSurface() { return this; }
    get asEdge() { return undefined; }
    get asSilhouette() { return undefined; }
    _draw(numInstances, instanceBuffersContainer) {
        const system = System_1.System.instance;
        // If we can't write depth in the fragment shader, use polygonOffset to force blanking regions to draw behind.
        const offset = 2 /* RenderOrder.BlankingRegion */ === this.renderOrder && !system.supportsLogZBuffer;
        if (offset) {
            system.context.enable(GL_1.GL.POLYGON_OFFSET_FILL);
            system.context.polygonOffset(1.0, 1.0);
        }
        const bufs = instanceBuffersContainer !== undefined ? instanceBuffersContainer : this._buffers;
        bufs.bind();
        system.drawArrays(GL_1.GL.PrimitiveType.Triangles, 0, this._numIndices, numInstances);
        bufs.unbind();
        if (offset)
            system.context.disable(GL_1.GL.POLYGON_OFFSET_FILL);
    }
    wantMixMonochromeColor(target) {
        // Text relies on white-on-white reversal.
        return !this.isGlyph && (this.isLitSurface || this.wantTextures(target, this.hasTexture));
    }
    get techniqueId() { return 0 /* TechniqueId.Surface */; }
    get isLitSurface() { return this.isLit; }
    get hasBakedLighting() { return this.mesh.hasBakedLighting; }
    get renderOrder() {
        if (core_common_1.FillFlags.Behind === (this.fillFlags & core_common_1.FillFlags.Behind))
            return 2 /* RenderOrder.BlankingRegion */;
        let order = this.isLit ? 4 /* RenderOrder.LitSurface */ : 3 /* RenderOrder.UnlitSurface */;
        if (this.isPlanar)
            order = order | 8 /* RenderOrder.PlanarBit */;
        return order;
    }
    getColor(target) {
        if (core_common_1.FillFlags.Background === (this.fillFlags & core_common_1.FillFlags.Background))
            return target.uniforms.style.backgroundColorInfo;
        else
            return this.colorInfo;
    }
    getPass(target) {
        // Classifiers have a dedicated pass
        if (this.isClassifier)
            return "classification";
        let opaquePass = this.isPlanar ? "opaque-planar" : "opaque";
        // When reading pixels, glyphs are always opaque. Otherwise always transparent (for anti-aliasing).
        if (this.isGlyph)
            return target.isReadPixelsInProgress ? opaquePass : "translucent";
        const vf = target.currentViewFlags;
        // When rendering thematic isolines, we need translucency because they have anti-aliasing.
        const thematic = target.wantThematicDisplay && this.supportsThematicDisplay ? target.uniforms.thematic.thematicDisplay : undefined;
        if (thematic && target.uniforms.thematic.wantIsoLines)
            return "translucent";
        // In wireframe, unless fill is explicitly enabled for planar region, surface does not draw
        if (core_common_1.RenderMode.Wireframe === vf.renderMode && !this.mesh.isTextureAlwaysDisplayed) {
            const fillFlags = this.fillFlags;
            const showFill = core_common_1.FillFlags.Always === (fillFlags & core_common_1.FillFlags.Always) || (vf.fill && core_common_1.FillFlags.ByView === (fillFlags & core_common_1.FillFlags.ByView));
            if (!showFill)
                return "none";
        }
        // If transparency disabled by render mode or view flag, always draw opaque.
        if (!vf.transparency || core_common_1.RenderMode.SolidFill === vf.renderMode || core_common_1.RenderMode.HiddenLine === vf.renderMode)
            return opaquePass;
        // A gradient texture applied by analysis style always fully determines the transparency of the surface.
        if (this.hasScalarAnimation && undefined !== target.analysisTexture) {
            (0, core_bentley_1.assert)(undefined !== target.analysisStyle?.thematic);
            switch (target.analysisStyle.thematic.thematicSettings.textureTransparency) {
                case core_common_1.TextureTransparency.Translucent:
                    return "translucent";
                case core_common_1.TextureTransparency.Opaque:
                    return opaquePass;
                case core_common_1.TextureTransparency.Mixed:
                    return `${opaquePass}-translucent`;
            }
        }
        // We have 3 sources of alpha: the material, the texture, and the color.
        // Base alpha comes from the material if it overrides it; otherwise from the color.
        // The texture's alpha is multiplied by the base alpha.
        // So we must draw in the translucent pass if the texture has transparency OR the base alpha is less than 1.
        let hasAlpha = false;
        const mat = wantMaterials(vf) ? this.mesh.materialInfo : undefined;
        if (undefined !== mat && mat.overridesAlpha)
            hasAlpha = mat.hasTranslucency;
        else
            hasAlpha = this.getColor(target).hasTranslucency;
        // Thematic gradient can optionally multiply gradient alpha with surface alpha.
        if (thematic && thematic.gradientSettings.transparencyMode === core_common_1.ThematicGradientTransparencyMode.MultiplySurfaceAndGradient) {
            switch (thematic.gradientSettings.textureTransparency) {
                case core_common_1.TextureTransparency.Opaque:
                    // This surface's alpha gets multiplied by 1 - gradient colors are all opaque.
                    return hasAlpha ? "translucent" : opaquePass;
                case core_common_1.TextureTransparency.Translucent:
                    // This surface's alpha gets multiplied by < 1 - gradient colors are all translucent.
                    return "translucent";
                case core_common_1.TextureTransparency.Mixed:
                    // The gradient contains a mix of translucent and opaque colors.
                    return hasAlpha ? "translucent" : `${opaquePass}-translucent`;
            }
        }
        if (!hasAlpha) {
            const tex = this.wantTextures(target, true) ? this.texture : undefined;
            switch (tex?.transparency) {
                case core_common_1.TextureTransparency.Translucent:
                    hasAlpha = true;
                    break;
                case core_common_1.TextureTransparency.Mixed:
                    opaquePass = `${opaquePass}-translucent`;
                    break;
            }
        }
        return hasAlpha ? "translucent" : opaquePass;
    }
    _wantWoWReversal(target) {
        const fillFlags = this.fillFlags;
        if (core_common_1.FillFlags.None !== (fillFlags & core_common_1.FillFlags.Background))
            return false; // fill color explicitly from background
        if (core_common_1.FillFlags.None !== (fillFlags & core_common_1.FillFlags.Always))
            return true; // fill displayed even in wireframe
        const vf = target.currentViewFlags;
        if (core_common_1.RenderMode.Wireframe === vf.renderMode || vf.visibleEdges)
            return false; // never invert surfaces when edges are displayed
        if (this.isLit && wantLighting(vf))
            return false;
        // Don't invert white pixels of textures...
        return !this.wantTextures(target, this.hasTexture);
    }
    get materialInfo() { return this.mesh.materialInfo; }
    useTexture(params) {
        return this.wantTextures(params.target, this.hasTexture);
    }
    useNormalMap(params) {
        return this.wantNormalMaps(params.target, this.hasNormalMap);
    }
    computeSurfaceFlags(params, flags) {
        const target = params.target;
        const vf = target.currentViewFlags;
        const useMaterial = wantMaterials(vf);
        flags[3 /* SurfaceBitIndex.IgnoreMaterial */] = useMaterial ? 0 : 1;
        flags[9 /* SurfaceBitIndex.HasMaterialAtlas */] = useMaterial && this.hasMaterialAtlas ? 1 : 0;
        flags[1 /* SurfaceBitIndex.ApplyLighting */] = 0;
        flags[6 /* SurfaceBitIndex.HasColorAndNormal */] = 0;
        if (this.isLit) {
            flags[2 /* SurfaceBitIndex.HasNormals */] = 1;
            if (wantLighting(vf))
                flags[1 /* SurfaceBitIndex.ApplyLighting */] = 1;
            // Textured meshes store normal in place of color index.
            // Untextured lit meshes store normal where textured meshes would store UV coords.
            // Tell shader where to find normal.
            if (!this.isTexturedType) {
                flags[6 /* SurfaceBitIndex.HasColorAndNormal */] = 1;
            }
        }
        else {
            flags[2 /* SurfaceBitIndex.HasNormals */] = 0;
        }
        flags[0 /* SurfaceBitIndex.HasTexture */] = this.useTexture(params) ? 1 : 0;
        flags[8 /* SurfaceBitIndex.HasNormalMap */] = this.useNormalMap(params) ? 1 : 0;
        flags[10 /* SurfaceBitIndex.UseConstantLodTextureMapping */] = this.mesh.textureUsesConstantLod ? 1 : 0;
        flags[11 /* SurfaceBitIndex.UseConstantLodNormalMapMapping */] = this.mesh.normalMapUsesConstantLod ? 1 : 0;
        // The transparency threshold controls how transparent a surface must be to allow light to pass through; more opaque surfaces cast shadows.
        flags[4 /* SurfaceBitIndex.TransparencyThreshold */] = params.target.isDrawingShadowMap ? 1 : 0;
        flags[5 /* SurfaceBitIndex.BackgroundFill */] = 0;
        switch (params.renderPass) {
            // NB: We need this for opaque pass due to SolidFill (must compute transparency, discard below threshold, render opaque at or above threshold)
            case 2 /* RenderPass.OpaqueLinear */:
            case 3 /* RenderPass.OpaquePlanar */:
            case 5 /* RenderPass.OpaqueGeneral */:
            case 8 /* RenderPass.Translucent */:
            case 12 /* RenderPass.WorldOverlay */:
            case 1 /* RenderPass.OpaqueLayers */:
            case 7 /* RenderPass.TranslucentLayers */:
            case 11 /* RenderPass.OverlayLayers */: {
                const mode = vf.renderMode;
                if (!this.isGlyph && (core_common_1.RenderMode.HiddenLine === mode || core_common_1.RenderMode.SolidFill === mode)) {
                    flags[4 /* SurfaceBitIndex.TransparencyThreshold */] = 1;
                    if (core_common_1.RenderMode.HiddenLine === mode && core_common_1.FillFlags.Always !== (this.fillFlags & core_common_1.FillFlags.Always)) {
                        // fill flags test for text - doesn't render with bg fill in hidden line mode.
                        flags[5 /* SurfaceBitIndex.BackgroundFill */] = 1;
                    }
                    break;
                }
            }
        }
    }
    constructor(indices, numIndices, mesh) {
        super(mesh, numIndices);
        this._buffers = AttributeBuffers_1.BuffersContainer.create();
        const attrPos = AttributeMap_1.AttributeMap.findAttribute("a_pos", 0 /* TechniqueId.Surface */, false);
        (0, core_bentley_1.assert)(undefined !== attrPos);
        this._buffers.addBuffer(indices, [AttributeBuffers_1.BufferParameters.create(attrPos.location, 3, GL_1.GL.DataType.UnsignedByte, false, 0, 0, false)]);
        this._indices = indices;
    }
    wantTextures(target, surfaceTextureExists) {
        if (this.hasScalarAnimation && undefined !== target.analysisTexture)
            return true;
        if (!surfaceTextureExists)
            return false;
        if (this.mesh.isTextureAlwaysDisplayed)
            return true;
        if (this.supportsThematicDisplay && target.wantThematicDisplay)
            return false;
        const fill = this.fillFlags;
        const flags = target.currentViewFlags;
        // ###TODO need to distinguish between gradient fill and actual textures...
        switch (flags.renderMode) {
            case core_common_1.RenderMode.SmoothShade:
                return flags.textures;
            case core_common_1.RenderMode.Wireframe:
                return core_common_1.FillFlags.Always === (fill & core_common_1.FillFlags.Always) || (flags.fill && core_common_1.FillFlags.ByView === (fill & core_common_1.FillFlags.ByView));
            default:
                return core_common_1.FillFlags.Always === (fill & core_common_1.FillFlags.Always);
        }
    }
    wantNormalMaps(target, normalMapExists) {
        if (!normalMapExists || !target.displayNormalMaps)
            return false;
        const flags = target.currentViewFlags;
        switch (flags.renderMode) {
            case core_common_1.RenderMode.SmoothShade:
                return flags.textures;
            default:
                return false;
        }
    }
}
exports.SurfaceGeometry = SurfaceGeometry;
//# sourceMappingURL=SurfaceGeometry.js.map