"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 Rendering
 */
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.collectGraphicDescriptionTransferables = exports.isGraphicDescription = exports.GraphicDescriptionBuilderImpl = void 0;
const core_geometry_1 = require("@itwin/core-geometry");
const ImdlModel_1 = require("../../imdl/ImdlModel");
const GraphicType_1 = require("../../render/GraphicType");
const GraphicAssembler_1 = require("../../render/GraphicAssembler");
const core_common_1 = require("@itwin/core-common");
const core_bentley_1 = require("@itwin/core-bentley");
const PointStringParams_1 = require("./PointStringParams");
const PolylineParams_1 = require("./PolylineParams");
const VertexTableBuilder_1 = require("./VertexTableBuilder");
const ParseImdlDocument_1 = require("../../imdl/ParseImdlDocument");
const Symbols_1 = require("../Symbols");
const GraphicDescriptionContextImpl_1 = require("./GraphicDescriptionContextImpl");
class GraphicDescriptionBuilderImpl extends GraphicAssembler_1.GraphicAssembler {
    constructor(options) {
        const type = options.type;
        const placement = options.placement ?? core_geometry_1.Transform.createIdentity();
        const wantEdges = options.generateEdges ?? type === GraphicType_1.GraphicType.Scene;
        const wantNormals = wantEdges || type === GraphicType_1.GraphicType.Scene;
        const preserveOrder = type === GraphicType_1.GraphicType.ViewOverlay || type === GraphicType_1.GraphicType.WorldOverlay || type === GraphicType_1.GraphicType.ViewBackground;
        super({
            ...options,
            [Symbols_1._implementationProhibited]: undefined,
            type,
            placement,
            wantEdges,
            wantNormals,
            preserveOrder,
        });
        this[_a] = undefined;
        this._computeChordTolerance = options.computeChordTolerance;
        this._viewIndependentOrigin = options.viewIndependentOrigin?.clone();
        this._context = options.context;
        if (!(this._context.transientIds instanceof core_bentley_1.TransientIdSequence)) {
            throw new Error("Invalid WorkerGraphicDescriptionContext");
        }
    }
    finish() {
        const description = {
            [Symbols_1._implementationProhibited]: undefined,
            type: this.type,
            primitives: [],
        };
        if (this[Symbols_1._accumulator].isEmpty) {
            return description;
        }
        const tolerance = this._computeChordTolerance({ builder: this, computeRange: () => this[Symbols_1._accumulator].geometries.computeRange() });
        const meshes = this[Symbols_1._accumulator].toMeshes(this, tolerance, this.pickable);
        if (meshes.length === 0) {
            return description;
        }
        // If the meshes contain quantized positions, they are all quantized to the same range. If that range is small relative to the distance
        // from the origin, quantization errors can produce display artifacts. Remove the translation from the quantization parameters and apply
        // it in the transform instead.
        // If the positions are not quantized, they have already been transformed to be relative to the center of the meshes' range.
        // Apply the inverse translation to put them back into model space.
        let transformOrigin;
        let meshesRangeOffset = false;
        for (const mesh of meshes) {
            const verts = mesh.points;
            if (!transformOrigin) {
                // This is the first mesh we've processed.
                if (verts instanceof core_common_1.QPoint3dList) {
                    transformOrigin = verts.params.origin.clone();
                    verts.params.origin.setZero();
                }
                else {
                    // In this case we need to modify the qOrigin of the graphic that will get created later since we have translated the origin.
                    // We can't modify it directly, but if we temporarily modify the range of the mesh used to create it the qOrigin will get created properly.
                    // Range is shared (not cloned) by all meshes and the mesh list itself, so modifying the range of the meshlist will modify it for all meshes.
                    // This will become the range in local coordinates of the batch, if we are creating a batch.
                    transformOrigin = verts.range.center;
                    if (!meshesRangeOffset) {
                        meshes.range?.low.subtractInPlace(transformOrigin);
                        meshes.range?.high.subtractInPlace(transformOrigin);
                        meshesRangeOffset = true;
                    }
                }
            }
            else {
                if (verts instanceof core_common_1.QPoint3dList) {
                    (0, core_bentley_1.assert)(transformOrigin.isAlmostEqual(verts.params.origin));
                    verts.params.origin.setZero();
                }
                else {
                    (0, core_bentley_1.assert)(verts.range.center.isAlmostZero);
                }
            }
            const primitive = this.createPrimitive(mesh);
            if (primitive) {
                const origin = this._viewIndependentOrigin;
                if (origin) {
                    primitive.modifier = {
                        type: "viewIndependentOrigin",
                        origin: { x: origin.x, y: origin.y, z: origin.z },
                    };
                }
                description.primitives.push(primitive);
            }
        }
        this[Symbols_1._accumulator].clear();
        if (transformOrigin) {
            description.translation = { x: transformOrigin.x, y: transformOrigin.y, z: transformOrigin.z };
        }
        const featureTable = this.pickable && meshes.features?.anyDefined ? meshes.features : undefined;
        if (featureTable) {
            (0, core_bentley_1.assert)(undefined !== this.pickable);
            const features = core_common_1.PackedFeatureTable.pack(featureTable);
            const range = meshes.range ?? new core_geometry_1.Range3d();
            description.batch = {
                ...this.pickable,
                range: range.toJSON(),
                modelId: this.pickable.modelId ?? this.pickable.id,
                featureTable: {
                    multiModel: false,
                    data: features.data,
                    numFeatures: features.numFeatures,
                    animationNodeIds: features.animationNodeIds,
                },
            };
        }
        return description;
    }
    createPrimitive(mesh) {
        const meshArgs = mesh.toMeshArgs();
        if (meshArgs) {
            return this.createMeshPrimitive(meshArgs);
        }
        const polylineArgs = mesh.toPolylineArgs();
        if (!polylineArgs) {
            return undefined;
        }
        return polylineArgs.flags.isDisjoint ? this.createPointStringPrimitive(polylineArgs) : this.createPolylinePrimitive(polylineArgs);
    }
    createMeshPrimitive(args) {
        const params = (0, VertexTableBuilder_1.createMeshParams)(args, this._context.constraints.maxTextureSize, true);
        let material;
        const mat = params.surface.material;
        if (mat) {
            (0, core_bentley_1.assert)(mat.isAtlas === false && mat.material instanceof GraphicDescriptionContextImpl_1.WorkerMaterial);
            material = mat.material.toImdl();
        }
        let textureMapping;
        const tex = params.surface.textureMapping;
        if (tex) {
            (0, core_bentley_1.assert)(tex.texture instanceof GraphicDescriptionContextImpl_1.WorkerTexture);
            textureMapping = { alwaysDisplayed: false, texture: tex.texture.index.toString(10) };
        }
        return {
            type: "mesh",
            params: {
                ...params,
                vertices: convertVertexTable(params.vertices),
                auxChannels: params.auxChannels?.toJSON(),
                edges: params.edges ? (0, ParseImdlDocument_1.edgeParamsToImdl)(params.edges) : undefined,
                surface: {
                    ...params.surface,
                    indices: params.surface.indices.data,
                    material,
                    textureMapping,
                },
            },
        };
    }
    createPolylinePrimitive(args) {
        const params = (0, PolylineParams_1.createPolylineParams)(args, this._context.constraints.maxTextureSize);
        if (!params) {
            return undefined;
        }
        return {
            type: "polyline",
            params: {
                ...params,
                vertices: convertVertexTable(params.vertices),
                polyline: {
                    indices: params.polyline.indices.data,
                    prevIndices: params.polyline.prevIndices.data,
                    nextIndicesAndParams: params.polyline.nextIndicesAndParams,
                },
            },
        };
    }
    createPointStringPrimitive(args) {
        const params = (0, PointStringParams_1.createPointStringParams)(args, this._context.constraints.maxTextureSize);
        if (!params) {
            return undefined;
        }
        return {
            type: "point",
            params: {
                indices: params.indices.data,
                vertices: convertVertexTable(params.vertices),
                weight: params.weight,
            },
        };
    }
    resolveGradient(gradient) {
        return this._context.createGradientTexture(gradient);
    }
}
exports.GraphicDescriptionBuilderImpl = GraphicDescriptionBuilderImpl;
_a = Symbols_1._implementationProhibited;
function convertVertexTable(src) {
    return {
        ...src,
        qparams: src.qparams.toJSON(),
        uvParams: src.uvParams?.toJSON(),
        uniformColor: src.uniformColor?.toJSON(),
    };
}
function isGraphicDescription(description) {
    const descr = description;
    return "object" === typeof descr && Array.isArray(descr.primitives) && "number" === typeof descr.type;
}
exports.isGraphicDescription = isGraphicDescription;
function collectGraphicDescriptionTransferables(xfers, description) {
    if (!isGraphicDescription(description)) {
        throw new Error("Invalid GraphicDescription");
    }
    for (const primitive of description.primitives) {
        (0, ImdlModel_1.addPrimitiveTransferables)(xfers, primitive);
    }
}
exports.collectGraphicDescriptionTransferables = collectGraphicDescriptionTransferables;
//# sourceMappingURL=GraphicDescriptionBuilderImpl.js.map