Source

VertexFormat.js

/**
 * Represent one of the element of a vertex.
 * It can be a position, a normal, a color, ….
 *
 * @category Geometry
 */
export class VertexElement {
    /**
     * Constructor
     *
     * @param {VertexElement.Usage} usage Element usage
     * @param {number} stream Stream index
     * @param {VertexElement.Type} type Type of element
     * @param {number?} count Value count, ex: A "vec2" will have "2" for this parameter
     * @param {boolean?} normalize True to ask rendering API to normalize values
     */
    constructor(usage, stream, type, count = 0, normalize = false) {
        /**
         * Value count for this element
         *
         * @type {number}
         * @public
         */
        this.count = count;

        /**
         * Indicates if the value need to be normalized by the graphic API
         *
         * @type {boolean}
         * @public
         */
        this.normalize = normalize;

        /**
         * Offset in the vertex data
         *
         * @type {number}
         * @public
         */
        this.offset = 0;

        /**
         * Stream index
         *
         * @type {number}
         * @public
         */
        this.stream = stream;

        /**
         * Stream stride.
         * @type {number}
         * @public
         */
        this.stride = 0;

        /**
         * Type of value
         *
         * @type {VertexElement.Type}
         * @public
         */
        this.type = type;

        /**
         * Type of element
         *
         * @type {VertexElement.Usage}
         * @public
         */
        this.usage = usage;
    }
}

/**
 * Type of stream available
 *
 * @enum {number}
 */
VertexElement.StreamType = {
    Static: 0,
    Dynamic: 1,
    Stream: 2,
};

/**
 * VertexElement's types
 *
 * @enum {number}
 */
VertexElement.Type = {
    Byte: 0,
    Float: 1,
    Int: 2,
    Short: 3,
};

/**
 * VertexElement's usage
 *
 * @enum {number}
 */
VertexElement.Usage = {
    Position: 0,
    Color: 1,
    UVS: 2,
    Normal: 3,
    Tangent: 4,
};

/**
 * Indicates the format of a vertex
 *
 * @category Geometry
 */
export class VertexFormat {
    /**
     * Constructor
     */
    constructor() {
        /**
         * Elements inside this format
         *
         * @type {Array.<VertexElement>}
         * @private
         */
        this.elements = [];

        /**
         * Stream's strides
         *
         * @type {Array.<VertexElement.StreamType>}
         * @private
         */
        this.streamType = [];

        /**
         * Stream's strides
         *
         * @type {Array.<number>}
         * @private
         */
        this.streamStride = [];

        /**
         * Stream's state
         *
         * @type {Array.<boolean>}
         * @private
         */
        this.streamNeedUpdate = [];

        /**
         * Indicates if the indices have changed
         *
         * @type {boolean}
         * @private
         */
        this.indicesNeedUpdate = true;
    }

    /**
     * Add an element to the format
     *
     * @param {VertexElement} element A VertexElement instance
     * @return {VertexFormat} A reference to the instance
     */
    add(element) {
        this.elements.push(element);
        this.compute();

        return this;
    }

    /**
     * Set elements
     *
     * @param {Array.<VertexElement>} elements An array of VertexElement instance
     * @return {VertexFormat} A reference to the instance
     */
    set(elements) {
        this.elements = elements;
        this.compute();

        return this;
    }

    /**
     * Get stream's type
     *
     * @param {number} index Stream index
     * @param {VertexElement.StreamType} type A type
     * @return {VertexFormat} A reference to the instance
     */
    setStreamType(index, type) {
        this.streamType[index] = type;

        return this;
    }

    /**
     * Compute offset and vertex format's data
     *
     * @private
     */
    compute() {
        let offset = 0;
        let size = 0;
        let previousStream = 0;

        // Sort elements by stream (ascending)
        this.elements.sort((a, b) => {
            const t = (a.stream > b.stream ? 1 : 0);
            return a.stream < b.stream ? -1 : t;
        });

        // Compute values
        for (let i = 0; i < this.elements.length; i += 1) {
            // Reset offset and stride when we change stream
            if (previousStream !== this.elements[i].stream) {
                this.streamStride[previousStream] = offset;
                offset = 0;
            }

            switch (this.elements[i].type) {
            case VertexElement.Type.Float:
            case VertexElement.Type.Int:
                size = 4;
                break;
            case VertexElement.Type.Short:
                size = 2;
                break;
            case VertexElement.Type.Byte:
                size = 1;
                break;
            default:
                size = 1;
                break;
            }

            this.elements[i].offset = offset;
            size *= this.elements[i].count;
            offset += size;
            previousStream = this.elements[i].stream;
        }

        this.streamStride[previousStream] = offset;
    }

    /**
     * Indicates if the indices need an update
     *
     * @param {boolean} state True to ask an update
     * @return {VertexFormat} A reference to the instance
     */
    setIndicesAsWaitingUpdate(state) {
        this.indicesNeedUpdate = state;

        return this;
    }

    /**
     * Indicates if the stream need an update
     *
     * @param {VertexElement.Usage } usage Stream usage
     * @param {boolean} state True to ask an update
     * @return {VertexFormat} A reference to the instance
     */
    setStreamAsWaitingUpdate(usage, state) {
        for (let i = 0; i < this.elements.length; i += 1) {
            if (this.elements[i].usage === usage) {
                this.streamNeedUpdate[this.elements[i].stream] = state;
            }
        }

        return this;
    }

    /**
     * Get elements
     *
     * @return {Array.<VertexElement>} An array of VertexElement
     */
    getElements() {
        return this.elements;
    }

    /**
     * Get stride of the asked stream
     *
     * @param {number} index Stream index
     * @return {number} A stride value, 0 if the stream don't exist
     */
    getStreamStride(index) {
        return this.streamStride[index] || 0;
    }

    /**
     * Get stream's type
     *
     * @param {number} index Stream index
     * @return {VertexElement.StreamType} A type, default: stream
     */
    getStreamType(index) {
        return this.streamType[index] || VertexElement.StreamType.Stream;
    }

    /**
     * Check if indices need an update
     *
     * @return {boolean} True if the indices need an update, otherwise false
     */
    isIndicesWaitingUpdate() {
        return this.indicesNeedUpdate;
    }

    /**
     * Check if the asked stream need an update
     *
     * @param {number} index Stream index
     * @return {boolean} True if the stream need an update, otherwise false
     */
    isStreamWaitingUpdate(index) {
        return this.streamNeedUpdate[index];
    }
}