"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
 */
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.InstancedGeometry = exports.PatternBuffers = exports.InstanceBuffers = exports.RenderInstancesImpl = exports.InstanceBuffersData = void 0;
exports.isInstancedGraphicParams = isInstancedGraphicParams;
const core_bentley_1 = require("@itwin/core-bentley");
const core_geometry_1 = require("@itwin/core-geometry");
const AttributeMap_1 = require("./AttributeMap");
const CachedGeometry_1 = require("./CachedGeometry");
const GL_1 = require("./GL");
const AttributeBuffers_1 = require("./AttributeBuffers");
const Matrix_1 = require("./Matrix");
const Symbols_1 = require("../../common/internal/Symbols");
const core_common_1 = require("@itwin/core-common");
/** @internal */
function isInstancedGraphicParams(params) {
    return typeof params === "object" && typeof params.count === "number" && params.transforms instanceof Float32Array && params.transformCenter instanceof core_geometry_1.Point3d;
}
class InstanceData {
    constructor(numInstances, rtcCenter) {
        // The model matrix from which _rtcModelTransform was previously computed. If it changes, _rtcModelTransform must be recomputed.
        this._modelMatrix = core_geometry_1.Transform.createIdentity();
        this.numInstances = numInstances;
        this._rtcOnlyTransform = core_geometry_1.Transform.createTranslation(rtcCenter);
        this._rtcModelTransform = this._rtcOnlyTransform.clone();
    }
    getRtcModelTransform(modelMatrix) {
        if (!this._modelMatrix.isAlmostEqual(modelMatrix)) {
            modelMatrix.clone(this._modelMatrix);
            modelMatrix.multiplyTransformTransform(this._rtcOnlyTransform, this._rtcModelTransform);
        }
        return this._rtcModelTransform;
    }
    getRtcOnlyTransform() {
        return this._rtcOnlyTransform;
    }
    get patternFeatureId() {
        return InstanceData._noFeatureId;
    }
}
InstanceData._noFeatureId = new Float32Array([0, 0, 0]);
/** @internal */
class InstanceBuffersData extends InstanceData {
    constructor(count, transforms, rtcCenter, symbology, featureIds, disableDisposal = false) {
        super(count, rtcCenter);
        this._noDispose = false;
        this.transforms = transforms;
        this.featureIds = featureIds;
        this.symbology = symbology;
        this._noDispose = disableDisposal;
    }
    static create(params, disableDisposal = false) {
        const { count, featureIds, symbologyOverrides, transforms } = params;
        (0, core_bentley_1.assert)(count > 0 && Math.floor(count) === count);
        (0, core_bentley_1.assert)(count === transforms.length / 12);
        (0, core_bentley_1.assert)(undefined === featureIds || count === featureIds.length / 3);
        (0, core_bentley_1.assert)(undefined === symbologyOverrides || count * 8 === symbologyOverrides.length);
        let idBuf;
        if (undefined !== featureIds && undefined === (idBuf = AttributeBuffers_1.BufferHandle.createArrayBuffer(featureIds)))
            return undefined;
        let symBuf;
        if (undefined !== symbologyOverrides && undefined === (symBuf = AttributeBuffers_1.BufferHandle.createArrayBuffer(symbologyOverrides)))
            return undefined;
        const tfBuf = AttributeBuffers_1.BufferHandle.createArrayBuffer(transforms);
        const transformCenter = params.transformCenter instanceof core_geometry_1.Point3d ? params.transformCenter : core_geometry_1.Point3d.fromJSON(params.transformCenter);
        if (!tfBuf) {
            return undefined;
        }
        return new InstanceBuffersData(count, tfBuf, transformCenter, symBuf, idBuf, disableDisposal);
    }
    dispose() {
        if (!this._noDispose) {
            (0, core_bentley_1.dispose)(this.transforms);
            (0, core_bentley_1.dispose)(this.featureIds);
            (0, core_bentley_1.dispose)(this.symbology);
        }
    }
    get isDisposed() {
        return this.transforms.isDisposed && (!this.featureIds || this.featureIds.isDisposed) && (!this.symbology || this.symbology.isDisposed);
    }
}
exports.InstanceBuffersData = InstanceBuffersData;
/** @internal */
var RenderInstancesImpl;
(function (RenderInstancesImpl) {
    function create(params) {
        const buffers = InstanceBuffersData.create(params.instances, true);
        if (!buffers) {
            return undefined;
        }
        let featureTable;
        if (params.features) {
            // ###TODO permit user to specify batch type and other batch options...
            featureTable = new core_common_1.PackedFeatureTable(params.features.data, params.features.modelId, params.features.count, core_common_1.BatchType.Primary);
        }
        return {
            [Symbols_1._implementationProhibited]: "renderInstances",
            buffers,
            [Symbols_1._transforms]: params.instances.transforms,
            [Symbols_1._transformCenter]: params.instances.transformCenter,
            [Symbols_1._featureTable]: featureTable,
        };
    }
    RenderInstancesImpl.create = create;
})(RenderInstancesImpl || (exports.RenderInstancesImpl = RenderInstancesImpl = {}));
/** @internal */
class InstanceBuffers {
    get numInstances() { return this._data.numInstances; }
    get transforms() { return this._data.transforms; }
    get featureIds() { return this._data.featureIds; }
    get symbology() { return this._data.symbology; }
    get hasFeatures() { return undefined !== this.featureIds; }
    get patternFeatureId() { return this._data.patternFeatureId; }
    getRtcModelTransform(modelMatrix) { return this._data.getRtcModelTransform(modelMatrix); }
    getRtcOnlyTransform() { return this._data.getRtcOnlyTransform(); }
    constructor(data, range) {
        this.patternParams = InstanceBuffers._patternParams;
        this.patternTransforms = undefined;
        this.viewIndependentOrigin = undefined;
        this._data = data;
        this.range = range;
    }
    static createTransformBufferParameters(techniqueId) {
        const params = [];
        const numRows = 3;
        let row = 0;
        while (row < numRows) {
            // 3 rows per instance; 4 floats per row; 4 bytes per float.
            const floatsPerRow = 4;
            const bytesPerVertex = floatsPerRow * 4;
            const offset = row * bytesPerVertex;
            const stride = 3 * bytesPerVertex;
            const name = `a_instanceMatrixRow${row}`;
            const details = AttributeMap_1.AttributeMap.findAttribute(name, techniqueId, true);
            (0, core_bentley_1.assert)(details !== undefined);
            const bParams = {
                glAttribLoc: details.location,
                glSize: floatsPerRow,
                glType: GL_1.GL.DataType.Float,
                glNormalized: false,
                glStride: stride,
                glOffset: offset,
                glInstanced: true,
            };
            params.push(bParams);
            row++;
        }
        return params;
    }
    static fromParams(params, computeReprRange) {
        const data = InstanceBuffersData.create(params);
        if (!data) {
            return undefined;
        }
        const range = params.range ?? this.computeRange(computeReprRange(), params.transforms, params.transformCenter);
        return new InstanceBuffers(data, range);
    }
    static fromRenderInstances(arg, reprRange) {
        const instances = arg;
        const range = this.computeRange(reprRange, instances[Symbols_1._transforms], instances[Symbols_1._transformCenter]);
        return new InstanceBuffers(instances.buffers, range);
    }
    dispose() {
        (0, core_bentley_1.dispose)(this._data);
    }
    get isDisposed() {
        return this._data.isDisposed;
    }
    collectStatistics(stats) {
        const featureBytes = undefined !== this.featureIds ? this.featureIds.bytesUsed : 0;
        const symBytes = undefined !== this.symbology ? this.symbology.bytesUsed : 0;
        const bytesUsed = this.transforms.bytesUsed + symBytes + featureBytes;
        stats.addInstances(bytesUsed);
    }
    static extendTransformedRange(tfs, i, range, x, y, z) {
        range.extendXYZ(tfs[i + 3] + tfs[i + 0] * x + tfs[i + 1] * y + tfs[i + 2] * z, tfs[i + 7] + tfs[i + 4] * x + tfs[i + 5] * y + tfs[i + 6] * z, tfs[i + 11] + tfs[i + 8] * x + tfs[i + 9] * y + tfs[i + 10] * z);
    }
    static computeRange(reprRange, tfs, rtcCenter, out) {
        const range = out ?? new core_geometry_1.Range3d();
        const numFloatsPerTransform = 3 * 4;
        (0, core_bentley_1.assert)(0 === tfs.length % (3 * 4));
        for (let i = 0; i < tfs.length; i += numFloatsPerTransform) {
            this.extendTransformedRange(tfs, i, range, reprRange.low.x, reprRange.low.y, reprRange.low.z);
            this.extendTransformedRange(tfs, i, range, reprRange.low.x, reprRange.low.y, reprRange.high.z);
            this.extendTransformedRange(tfs, i, range, reprRange.low.x, reprRange.high.y, reprRange.low.z);
            this.extendTransformedRange(tfs, i, range, reprRange.low.x, reprRange.high.y, reprRange.high.z);
            this.extendTransformedRange(tfs, i, range, reprRange.high.x, reprRange.low.y, reprRange.low.z);
            this.extendTransformedRange(tfs, i, range, reprRange.high.x, reprRange.low.y, reprRange.high.z);
            this.extendTransformedRange(tfs, i, range, reprRange.high.x, reprRange.high.y, reprRange.low.z);
            this.extendTransformedRange(tfs, i, range, reprRange.high.x, reprRange.high.y, reprRange.high.z);
        }
        range.low.addInPlace(rtcCenter);
        range.high.addInPlace(rtcCenter);
        return range.clone(out);
    }
}
exports.InstanceBuffers = InstanceBuffers;
InstanceBuffers._patternParams = new Float32Array([0, 0, 0, 0]);
/** @internal */
class PatternBuffers extends InstanceData {
    constructor(count, rtcCenter, range, patternParams, // [ isAreaPattern, spacingX, spacingY, scale ]
    origin, // [ x, y ]
    orgTransform, localToModel, symbolToLocal, offsets, featureId, viewIndependentOrigin) {
        super(count, rtcCenter);
        this.range = range;
        this.patternParams = patternParams;
        this.origin = origin;
        this.orgTransform = orgTransform;
        this.localToModel = localToModel;
        this.symbolToLocal = symbolToLocal;
        this.offsets = offsets;
        this.viewIndependentOrigin = viewIndependentOrigin;
        this[_a] = "renderAreaPattern";
        this.patternTransforms = this;
        if (undefined !== featureId) {
            this._featureId = new Float32Array([
                (featureId & 0x0000ff) >>> 0,
                (featureId & 0x00ff00) >>> 8,
                (featureId & 0xff0000) >>> 16,
            ]);
        }
    }
    static create(params) {
        const count = params.xyOffsets.byteLength / 2;
        (0, core_bentley_1.assert)(Math.floor(count) === count);
        const offsets = AttributeBuffers_1.BufferHandle.createArrayBuffer(params.xyOffsets);
        if (!offsets)
            return undefined;
        return new PatternBuffers(count, new core_geometry_1.Point3d(), params.range, new Float32Array([1, params.spacing.x, params.spacing.y, params.scale]), new Float32Array([params.origin.x, params.origin.y]), Matrix_1.Matrix4.fromTransform(params.orgTransform), Matrix_1.Matrix4.fromTransform(params.patternToModel), Matrix_1.Matrix4.fromTransform(core_geometry_1.Transform.createTranslation(params.symbolTranslation)), offsets, params.featureId, params.viewIndependentOrigin);
    }
    get hasFeatures() {
        return undefined !== this._featureId;
    }
    get patternFeatureId() {
        return this._featureId ?? super.patternFeatureId;
    }
    get isDisposed() {
        return this.offsets.isDisposed;
    }
    dispose() {
        (0, core_bentley_1.dispose)(this.offsets);
    }
    collectStatistics(stats) {
        stats.addInstances(this.offsets.bytesUsed);
    }
}
exports.PatternBuffers = PatternBuffers;
_a = Symbols_1._implementationProhibited;
/** @internal */
class InstancedGeometry extends CachedGeometry_1.CachedGeometry {
    getRtcModelTransform(modelMatrix) { return this._buffers.getRtcModelTransform(modelMatrix); }
    getRtcOnlyTransform() { return this._buffers.getRtcOnlyTransform(); }
    get viewIndependentOrigin() { return this._buffers.viewIndependentOrigin; }
    get asInstanced() { return this; }
    get asLUT() { return this._repr.asLUT; }
    get asMesh() { return this._repr.asMesh; }
    get asSurface() { return this._repr.asSurface; }
    get asEdge() { return this._repr.asEdge; }
    get asSilhouette() { return this._repr.asSilhouette; }
    get asIndexedEdge() { return this._repr.asIndexedEdge; }
    get renderOrder() { return this._repr.renderOrder; }
    get isLitSurface() { return this._repr.isLitSurface; }
    get hasBakedLighting() { return this._repr.hasBakedLighting; }
    get hasAnimation() { return this._repr.hasAnimation; }
    get usesQuantizedPositions() { return this._repr.usesQuantizedPositions; }
    get qOrigin() { return this._repr.qOrigin; }
    get qScale() { return this._repr.qScale; }
    get materialInfo() { return this._repr.materialInfo; }
    get polylineBuffers() { return this._repr.polylineBuffers; }
    get isEdge() { return this._repr.isEdge; }
    get hasFeatures() { return this._buffers.hasFeatures; }
    get techniqueId() { return this._repr.techniqueId; }
    get supportsThematicDisplay() { return this._repr.supportsThematicDisplay; }
    getPass(target) { return this._repr.getPass(target); }
    wantWoWReversal(params) { return this._repr.wantWoWReversal(params); }
    getLineCode(params) { return this._repr.getLineCode(params); }
    getLineWeight(params) { return this._repr.getLineWeight(params); }
    wantMonochrome(target) { return this._repr.wantMonochrome(target); }
    wantMixMonochromeColor(target) { return this._repr.wantMixMonochromeColor(target); }
    static create(repr, ownsBuffers, buffers) {
        const techId = repr.techniqueId;
        const container = AttributeBuffers_1.BuffersContainer.create();
        container.appendLinkages(repr.lutBuffers.linkages);
        container.addBuffer(buffers.transforms, InstanceBuffers.createTransformBufferParameters(repr.techniqueId));
        if (buffers.symbology) {
            const attrInstanceOverrides = AttributeMap_1.AttributeMap.findAttribute("a_instanceOverrides", techId, true);
            const attrInstanceRgba = AttributeMap_1.AttributeMap.findAttribute("a_instanceRgba", techId, true);
            (0, core_bentley_1.assert)(attrInstanceOverrides !== undefined);
            (0, core_bentley_1.assert)(attrInstanceRgba !== undefined);
            container.addBuffer(buffers.symbology, [
                AttributeBuffers_1.BufferParameters.create(attrInstanceOverrides.location, 4, GL_1.GL.DataType.UnsignedByte, false, 8, 0, true),
                AttributeBuffers_1.BufferParameters.create(attrInstanceRgba.location, 4, GL_1.GL.DataType.UnsignedByte, false, 8, 4, true),
            ]);
        }
        if (buffers.featureIds) {
            const attrFeatureId = AttributeMap_1.AttributeMap.findAttribute("a_featureId", techId, true);
            (0, core_bentley_1.assert)(attrFeatureId !== undefined);
            container.addBuffer(buffers.featureIds, [AttributeBuffers_1.BufferParameters.create(attrFeatureId.location, 3, GL_1.GL.DataType.UnsignedByte, false, 0, 0, true)]);
        }
        return new this(repr, ownsBuffers, buffers, container);
    }
    static createPattern(repr, ownsBuffers, buffers) {
        const techId = repr.techniqueId;
        const container = AttributeBuffers_1.BuffersContainer.create();
        container.appendLinkages(repr.lutBuffers.linkages);
        const attrX = AttributeMap_1.AttributeMap.findAttribute("a_patternX", techId, true);
        const attrY = AttributeMap_1.AttributeMap.findAttribute("a_patternY", techId, true);
        (0, core_bentley_1.assert)(undefined !== attrX && undefined !== attrY);
        container.addBuffer(buffers.offsets, [
            AttributeBuffers_1.BufferParameters.create(attrX.location, 1, GL_1.GL.DataType.Float, false, 8, 0, true),
            AttributeBuffers_1.BufferParameters.create(attrY.location, 1, GL_1.GL.DataType.Float, false, 8, 4, true),
        ]);
        return new this(repr, ownsBuffers, buffers, container);
    }
    constructor(repr, ownsBuffers, buffers, container) {
        super();
        this._repr = repr;
        this._ownsBuffers = ownsBuffers;
        this._buffers = buffers;
        this._buffersContainer = container;
    }
    get isDisposed() {
        if (!this._repr.isDisposed)
            return false;
        return !this._ownsBuffers || this._buffers.isDisposed;
    }
    dispose() {
        this._repr.dispose();
        if (this._ownsBuffers)
            (0, core_bentley_1.dispose)(this._buffers);
    }
    _wantWoWReversal(_target) {
        (0, core_bentley_1.assert)(false, "Should never be called");
        return false;
    }
    draw() {
        this._repr.drawInstanced(this._buffers.numInstances, this._buffersContainer);
    }
    computeRange(output) {
        return this._buffers.range.clone(output);
    }
    collectStatistics(stats) {
        this._repr.collectStatistics(stats);
        if (this._ownsBuffers)
            this._buffers.collectStatistics(stats);
    }
    get patternParams() { return this._buffers.patternParams; }
    get patternTransforms() { return this._buffers.patternTransforms; }
    get patternFeatureId() { return this._buffers.patternFeatureId; }
}
exports.InstancedGeometry = InstancedGeometry;
//# sourceMappingURL=InstancedGeometry.js.map