"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.ClipVolume = void 0;
const core_bentley_1 = require("@itwin/core-bentley");
const core_geometry_1 = require("@itwin/core-geometry");
const RenderClipVolume_1 = require("../RenderClipVolume");
const scratch = {
    normal: new core_geometry_1.Vector3d(),
    dir: new core_geometry_1.Vector3d(),
    pos: new core_geometry_1.Point3d(),
    v0: new core_geometry_1.Vector3d(),
};
/** Maintains an ArrayBuffer to serve as texture data for a ClipVector.
 * The clip planes are in view coordinates, so the data must be updated whenever the view
 * matrix changes.
 */
class ClipPlanesBuffer {
    getData(viewMatrix) {
        if (!viewMatrix.isAlmostEqual(this._viewMatrix))
            this.updateData(viewMatrix);
        return this._data;
    }
    get byteLength() {
        return this._view.buffer.byteLength;
    }
    static create(clips, numRows) {
        (0, core_bentley_1.assert)(numRows > 1); // at least one plane, plus a union boundary.
        return new ClipPlanesBuffer(clips, numRows);
    }
    constructor(clips, numRows) {
        /** Most recently-applied view matrix. */
        this._viewMatrix = core_geometry_1.Transform.createZero();
        /** The current write position. */
        this._curPos = 0;
        this._data = new Uint8Array(numRows * 4 * 4);
        this._view = new DataView(this._data.buffer);
        this._clips = clips;
        this.numRows = numRows;
        this._append = (value) => this.appendFloat(value);
    }
    appendFloat(value) {
        this._view.setFloat32(this._curPos, value, true);
        this.advance(4);
    }
    appendUint8(value) {
        this._view.setUint8(this._curPos, value);
        this.advance(1);
    }
    appendValues(a, b, c, d) {
        this._append(a);
        this._append(b);
        this._append(c);
        this._append(d);
    }
    appendPlane(normal, distance) {
        this.appendValues(normal.x, normal.y, normal.z, distance);
    }
    appendSetBoundary() {
        this.appendValues(0, 0, 0, 0);
    }
    appendUnionBoundary() {
        this.appendValues(2, 2, 2, 0);
    }
    appendEncodedFloat(value) {
        const sign = value < 0 ? 1 : 0;
        value = Math.abs(value);
        const exponent = Math.floor(Math.log10(value)) + 1;
        value = value / Math.pow(10, exponent);
        const bias = 38;
        let temp = value * 256;
        const b0 = Math.floor(temp);
        temp = (temp - b0) * 256;
        const b1 = Math.floor(temp);
        temp = (temp - b1) * 256;
        const b2 = Math.floor(temp);
        const b3 = (exponent + bias) * 2 + sign;
        this.appendUint8(b0);
        this.appendUint8(b1);
        this.appendUint8(b2);
        this.appendUint8(b3);
    }
    advance(numBytes) {
        this._curPos += numBytes;
    }
    reset() {
        this._curPos = 0;
    }
    updateData(transform) {
        this.reset();
        transform.clone(this._viewMatrix);
        const { normal, dir, pos, v0 } = { ...scratch };
        for (const clip of this._clips) {
            for (let j = 0; j < clip.convexSets.length; j++) {
                const set = clip.convexSets[j];
                if (0 === set.planes.length)
                    continue;
                if (j > 0)
                    this.appendSetBoundary();
                for (const plane of set.planes) {
                    plane.inwardNormalRef.clone(normal);
                    let distance = plane.distance;
                    const norm = normal;
                    transform.matrix.multiplyVector(norm, dir);
                    dir.normalizeInPlace();
                    transform.multiplyPoint3d(norm.scale(distance, v0), pos);
                    v0.setFromPoint3d(pos);
                    normal.set(dir.x, dir.y, dir.z);
                    distance = -v0.dotProduct(dir);
                    this.appendPlane(normal, distance);
                }
            }
            this.appendUnionBoundary();
        }
    }
}
/** A ClipVector encoded for transmission to the GPU as a texture.
 * @internal
 */
class ClipVolume extends RenderClipVolume_1.RenderClipVolume {
    get numRows() {
        return this._buffer.numRows;
    }
    get byteLength() {
        return this._buffer.byteLength;
    }
    getData(viewMatrix) {
        return this._buffer.getData(viewMatrix);
    }
    static create(clip) {
        if (!clip.isValid)
            return undefined;
        // Compute how many rows of data we need.
        const unions = [];
        let numRows = 0;
        for (const primitive of clip.clips) {
            const union = primitive.fetchClipPlanesRef();
            if (!union)
                continue;
            let numSets = 0;
            for (const set of union.convexSets) {
                const setLength = set.planes.length;
                if (setLength > 0) {
                    ++numSets;
                    numRows += setLength;
                }
            }
            if (numSets > 0) {
                unions.push(union);
                numRows += numSets - 1; // Additional boundary rows between sets.
            }
        }
        if (unions.length === 0)
            return undefined;
        numRows += unions.length; // Additional boundary row after each union - *including* the last union.
        const buffer = ClipPlanesBuffer.create(unions, numRows);
        return new ClipVolume(clip, buffer);
    }
    constructor(clip, buffer) {
        super(clip);
        this._buffer = buffer;
    }
}
exports.ClipVolume = ClipVolume;
//# sourceMappingURL=ClipVolume.js.map