UNPKG

@babylonjs/core

Version:

Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.

658 lines (657 loc) 22.9 kB
import { DataBuffer } from "./dataBuffer.js"; import { Logger } from "../Misc/logger.js"; import { EnumerateFloatValues, GetFloatData, GetTypeByteLength } from "./bufferUtils.js"; /** * Class used to store data that will be store in GPU memory */ export class Buffer { /** * Gets a boolean indicating if the Buffer is disposed */ get isDisposed() { return this._isDisposed; } /** * Constructor * @param engine the engine * @param data the data to use for this buffer * @param updatable whether the data is updatable * @param stride the stride (optional) * @param postponeInternalCreation whether to postpone creating the internal WebGL buffer (optional) * @param instanced whether the buffer is instanced (optional) * @param useBytes set to true if the stride in in bytes (optional) * @param divisor sets an optional divisor for instances (1 by default) * @param label defines the label of the buffer (for debug purpose) */ constructor(engine, data, updatable, stride = 0, postponeInternalCreation = false, instanced = false, useBytes = false, divisor, label) { this._isAlreadyOwned = false; this._isDisposed = false; if (engine && engine.getScene) { // old versions of VertexBuffer accepted 'mesh' instead of 'engine' this._engine = engine.getScene().getEngine(); } else { this._engine = engine; } this._updatable = updatable; this._instanced = instanced; this._divisor = divisor || 1; this._label = label; if (data instanceof DataBuffer) { this._data = null; this._buffer = data; } else { this._data = data; this._buffer = null; } this.byteStride = useBytes ? stride : stride * Float32Array.BYTES_PER_ELEMENT; if (!postponeInternalCreation) { // by default this.create(); } } /** * Create a new VertexBuffer based on the current buffer * @param kind defines the vertex buffer kind (position, normal, etc.) * @param offset defines offset in the buffer (0 by default) * @param size defines the size in floats of attributes (position is 3 for instance) * @param stride defines the stride size in floats in the buffer (the offset to apply to reach next value when data is interleaved) * @param instanced defines if the vertex buffer contains indexed data * @param useBytes defines if the offset and stride are in bytes * * @param divisor sets an optional divisor for instances (1 by default) * @returns the new vertex buffer */ createVertexBuffer(kind, offset, size, stride, instanced, useBytes = false, divisor) { const byteOffset = useBytes ? offset : offset * Float32Array.BYTES_PER_ELEMENT; const byteStride = stride ? (useBytes ? stride : stride * Float32Array.BYTES_PER_ELEMENT) : this.byteStride; // a lot of these parameters are ignored as they are overridden by the buffer return new VertexBuffer(this._engine, this, kind, this._updatable, true, byteStride, instanced === undefined ? this._instanced : instanced, byteOffset, size, undefined, undefined, true, this._divisor || divisor); } // Properties /** * Gets a boolean indicating if the Buffer is updatable? * @returns true if the buffer is updatable */ isUpdatable() { return this._updatable; } /** * Gets current buffer's data * @returns a DataArray or null */ getData() { return this._data; } /** * Gets underlying native buffer * @returns underlying native buffer */ getBuffer() { return this._buffer; } /** * Gets the stride in float32 units (i.e. byte stride / 4). * May not be an integer if the byte stride is not divisible by 4. * @returns the stride in float32 units * @deprecated Please use byteStride instead. */ getStrideSize() { return this.byteStride / Float32Array.BYTES_PER_ELEMENT; } // Methods /** * Store data into the buffer. Creates the buffer if not used already. * If the buffer was already used, it will be updated only if it is updatable, otherwise it will do nothing. * @param data defines the data to store */ create(data = null) { if (!data && this._buffer) { return; // nothing to do } data = data || this._data; if (!data) { return; } if (!this._buffer) { // create buffer if (this._updatable) { this._buffer = this._engine.createDynamicVertexBuffer(data, this._label); this._data = data; } else { this._buffer = this._engine.createVertexBuffer(data, undefined, this._label); } } else if (this._updatable) { // update buffer this._engine.updateDynamicVertexBuffer(this._buffer, data); this._data = data; } } /** @internal */ _rebuild() { if (!this._data) { if (!this._buffer) { // Buffer was not yet created, nothing to do return; } if (this._buffer.capacity > 0) { // We can at least recreate the buffer with the right size, even if we don't have the data if (this._updatable) { this._buffer = this._engine.createDynamicVertexBuffer(this._buffer.capacity, this._label); } else { this._buffer = this._engine.createVertexBuffer(this._buffer.capacity, undefined, this._label); } return; } Logger.Warn(`Missing data for buffer "${this._label}" ${this._buffer ? "(uniqueId: " + this._buffer.uniqueId + ")" : ""}. Buffer reconstruction failed.`); this._buffer = null; } else { this._buffer = null; this.create(this._data); } } /** * Update current buffer data * @param data defines the data to store */ update(data) { this.create(data); } /** * Updates the data directly. * @param data the new data * @param offset the new offset * @param vertexCount the vertex count (optional) * @param useBytes set to true if the offset is in bytes */ updateDirectly(data, offset, vertexCount, useBytes = false) { if (!this._buffer) { return; } if (this._updatable) { // update buffer this._engine.updateDynamicVertexBuffer(this._buffer, data, useBytes ? offset : offset * Float32Array.BYTES_PER_ELEMENT, vertexCount ? vertexCount * this.byteStride : undefined); if (offset === 0 && vertexCount === undefined) { // Keep the data if we easily can this._data = data; } else { this._data = null; } } } /** @internal */ _increaseReferences() { if (!this._buffer) { return; } if (!this._isAlreadyOwned) { this._isAlreadyOwned = true; return; } this._buffer.references++; } /** * Release all resources */ dispose() { if (!this._buffer) { return; } // The data buffer has an internal counter as this buffer can be used by several VertexBuffer objects // This means that we only flag it as disposed when all references are released (when _releaseBuffer will return true) if (this._engine._releaseBuffer(this._buffer)) { this._isDisposed = true; this._data = null; this._buffer = null; } } } /** * Specialized buffer used to store vertex data */ export class VertexBuffer { /** * Gets a boolean indicating if the Buffer is disposed */ get isDisposed() { return this._isDisposed; } /** * Gets or sets the instance divisor when in instanced mode */ get instanceDivisor() { return this._instanceDivisor; } set instanceDivisor(value) { const isInstanced = value != 0; this._instanceDivisor = value; if (isInstanced !== this._instanced) { this._instanced = isInstanced; this._computeHashCode(); } } /** * Gets the max possible amount of vertices stored within the current vertex buffer. * We do not have the end offset or count so this will be too big for concatenated vertex buffers. * @internal */ get _maxVerticesCount() { const data = this.getData(); if (!data) { return 0; } if (Array.isArray(data)) { // data is a regular number[] with float values return data.length / (this.byteStride / 4) - this.byteOffset / 4; } return (data.byteLength - this.byteOffset) / this.byteStride; } /** @internal */ constructor(engine, data, kind, updatableOrOptions, postponeInternalCreation, stride, instanced, offset, size, type, normalized = false, useBytes = false, divisor = 1, takeBufferOwnership = false) { /** @internal */ this._isDisposed = false; let updatable = false; this.engine = engine; if (typeof updatableOrOptions === "object" && updatableOrOptions !== null) { updatable = updatableOrOptions.updatable ?? false; postponeInternalCreation = updatableOrOptions.postponeInternalCreation; stride = updatableOrOptions.stride; instanced = updatableOrOptions.instanced; offset = updatableOrOptions.offset; size = updatableOrOptions.size; type = updatableOrOptions.type; normalized = updatableOrOptions.normalized ?? false; useBytes = updatableOrOptions.useBytes ?? false; divisor = updatableOrOptions.divisor ?? 1; takeBufferOwnership = updatableOrOptions.takeBufferOwnership ?? false; this._label = updatableOrOptions.label; } else { updatable = !!updatableOrOptions; } if (data instanceof Buffer) { this._buffer = data; this._ownsBuffer = takeBufferOwnership; } else { this._buffer = new Buffer(engine, data, updatable, stride, postponeInternalCreation, instanced, useBytes, divisor, this._label); this._ownsBuffer = true; } this.uniqueId = VertexBuffer._Counter++; this._kind = kind; if (type === undefined) { const vertexData = this.getData(); this.type = vertexData ? VertexBuffer.GetDataType(vertexData) : VertexBuffer.FLOAT; } else { this.type = type; } const typeByteLength = GetTypeByteLength(this.type); if (useBytes) { this._size = size || (stride ? stride / typeByteLength : VertexBuffer.DeduceStride(kind)); this.byteStride = stride || this._buffer.byteStride || this._size * typeByteLength; this.byteOffset = offset || 0; } else { this._size = size || stride || VertexBuffer.DeduceStride(kind); this.byteStride = stride ? stride * typeByteLength : this._buffer.byteStride || this._size * typeByteLength; this.byteOffset = (offset || 0) * typeByteLength; } this.normalized = normalized; this._instanced = instanced !== undefined ? instanced : false; this._instanceDivisor = instanced ? divisor : 0; this._alignBuffer(); this._computeHashCode(); } _computeHashCode() { // note: cast to any because the property is declared readonly this.hashCode = ((this.type - 5120) << 0) + ((this.normalized ? 1 : 0) << 3) + (this._size << 4) + ((this._instanced ? 1 : 0) << 6) + /* keep 5 bits free */ (this.byteStride << 12); } /** @internal */ _rebuild() { this._buffer?._rebuild(); } /** * Returns the kind of the VertexBuffer (string) * @returns a string */ getKind() { return this._kind; } // Properties /** * Gets a boolean indicating if the VertexBuffer is updatable? * @returns true if the buffer is updatable */ isUpdatable() { return this._buffer.isUpdatable(); } /** * Gets current buffer's data * @returns a DataArray or null */ getData() { return this._buffer.getData(); } /** * Gets current buffer's data as a float array. Float data is constructed if the vertex buffer data cannot be returned directly. * @param totalVertices number of vertices in the buffer to take into account * @param forceCopy defines a boolean indicating that the returned array must be cloned upon returning it * @returns a float array containing vertex data */ getFloatData(totalVertices, forceCopy) { const data = this.getData(); if (!data) { return null; } return GetFloatData(data, this._size, this.type, this.byteOffset, this.byteStride, this.normalized, totalVertices, forceCopy); } /** * Gets underlying native buffer * @returns underlying native buffer */ getBuffer() { return this._buffer.getBuffer(); } /** * Gets the Buffer instance that wraps the native GPU buffer * @returns the wrapper buffer */ getWrapperBuffer() { return this._buffer; } /** * Gets the stride in float32 units (i.e. byte stride / 4). * May not be an integer if the byte stride is not divisible by 4. * @returns the stride in float32 units * @deprecated Please use byteStride instead. */ getStrideSize() { return this.byteStride / GetTypeByteLength(this.type); } /** * Returns the offset as a multiple of the type byte length. * @returns the offset in bytes * @deprecated Please use byteOffset instead. */ getOffset() { return this.byteOffset / GetTypeByteLength(this.type); } /** * Returns the number of components or the byte size per vertex attribute * @param sizeInBytes If true, returns the size in bytes or else the size in number of components of the vertex attribute (default: false) * @returns the number of components */ getSize(sizeInBytes = false) { return sizeInBytes ? this._size * GetTypeByteLength(this.type) : this._size; } /** * Gets a boolean indicating is the internal buffer of the VertexBuffer is instanced * @returns true if this buffer is instanced */ getIsInstanced() { return this._instanced; } /** * Returns the instancing divisor, zero for non-instanced (integer). * @returns a number */ getInstanceDivisor() { return this._instanceDivisor; } // Methods /** * Store data into the buffer. If the buffer was already used it will be either recreated or updated depending on isUpdatable property * @param data defines the data to store */ create(data) { this._buffer.create(data); this._alignBuffer(); } /** * Updates the underlying buffer according to the passed numeric array or Float32Array. * This function will create a new buffer if the current one is not updatable * @param data defines the data to store */ update(data) { this._buffer.update(data); this._alignBuffer(); } /** * Updates directly the underlying WebGLBuffer according to the passed numeric array or Float32Array. * Returns the directly updated WebGLBuffer. * @param data the new data * @param offset the new offset * @param useBytes set to true if the offset is in bytes */ updateDirectly(data, offset, useBytes = false) { this._buffer.updateDirectly(data, offset, undefined, useBytes); this._alignBuffer(); } /** * Disposes the VertexBuffer and the underlying WebGLBuffer. */ dispose() { if (this._ownsBuffer) { this._buffer.dispose(); } this._isDisposed = true; } /** * Enumerates each value of this vertex buffer as numbers. * @param count the number of values to enumerate * @param callback the callback function called for each value */ forEach(count, callback) { EnumerateFloatValues(this._buffer.getData(), this.byteOffset, this.byteStride, this._size, this.type, count, this.normalized, (values, index) => { for (let i = 0; i < this._size; i++) { callback(values[i], index + i); } }); } /** @internal */ _alignBuffer() { } /** * Deduces the stride given a kind. * @param kind The kind string to deduce * @returns The deduced stride */ static DeduceStride(kind) { switch (kind) { case VertexBuffer.UVKind: case VertexBuffer.UV2Kind: case VertexBuffer.UV3Kind: case VertexBuffer.UV4Kind: case VertexBuffer.UV5Kind: case VertexBuffer.UV6Kind: return 2; case VertexBuffer.NormalKind: case VertexBuffer.PositionKind: return 3; case VertexBuffer.ColorKind: case VertexBuffer.ColorInstanceKind: case VertexBuffer.MatricesIndicesKind: case VertexBuffer.MatricesIndicesExtraKind: case VertexBuffer.MatricesWeightsKind: case VertexBuffer.MatricesWeightsExtraKind: case VertexBuffer.TangentKind: return 4; default: throw new Error("Invalid kind '" + kind + "'"); } } /** * Gets the vertex buffer type of the given data array. * @param data the data array * @returns the vertex buffer type */ static GetDataType(data) { if (data instanceof Int8Array) { return VertexBuffer.BYTE; } else if (data instanceof Uint8Array) { return VertexBuffer.UNSIGNED_BYTE; } else if (data instanceof Int16Array) { return VertexBuffer.SHORT; } else if (data instanceof Uint16Array) { return VertexBuffer.UNSIGNED_SHORT; } else if (data instanceof Int32Array) { return VertexBuffer.INT; } else if (data instanceof Uint32Array) { return VertexBuffer.UNSIGNED_INT; } else { return VertexBuffer.FLOAT; } } /** * Gets the byte length of the given type. * @param type the type * @returns the number of bytes * @deprecated Use `getTypeByteLength` from `bufferUtils` instead */ static GetTypeByteLength(type) { return GetTypeByteLength(type); } /** * Enumerates each value of the given parameters as numbers. * @param data the data to enumerate * @param byteOffset the byte offset of the data * @param byteStride the byte stride of the data * @param componentCount the number of components per element * @param componentType the type of the component * @param count the number of values to enumerate * @param normalized whether the data is normalized * @param callback the callback function called for each value * @deprecated Use `EnumerateFloatValues` from `bufferUtils` instead */ static ForEach(data, byteOffset, byteStride, componentCount, componentType, count, normalized, callback) { EnumerateFloatValues(data, byteOffset, byteStride, componentCount, componentType, count, normalized, (values, index) => { for (let componentIndex = 0; componentIndex < componentCount; componentIndex++) { callback(values[componentIndex], index + componentIndex); } }); } /** * Gets the given data array as a float array. Float data is constructed if the data array cannot be returned directly. * @param data the input data array * @param size the number of components * @param type the component type * @param byteOffset the byte offset of the data * @param byteStride the byte stride of the data * @param normalized whether the data is normalized * @param totalVertices number of vertices in the buffer to take into account * @param forceCopy defines a boolean indicating that the returned array must be cloned upon returning it * @returns a float array containing vertex data * @deprecated Use `GetFloatData` from `bufferUtils` instead */ static GetFloatData(data, size, type, byteOffset, byteStride, normalized, totalVertices, forceCopy) { return GetFloatData(data, size, type, byteOffset, byteStride, normalized, totalVertices, forceCopy); } } VertexBuffer._Counter = 0; /** * The byte type. */ VertexBuffer.BYTE = 5120; /** * The unsigned byte type. */ VertexBuffer.UNSIGNED_BYTE = 5121; /** * The short type. */ VertexBuffer.SHORT = 5122; /** * The unsigned short type. */ VertexBuffer.UNSIGNED_SHORT = 5123; /** * The integer type. */ VertexBuffer.INT = 5124; /** * The unsigned integer type. */ VertexBuffer.UNSIGNED_INT = 5125; /** * The float type. */ VertexBuffer.FLOAT = 5126; // Enums /** * Positions */ VertexBuffer.PositionKind = `position`; /** * Normals */ VertexBuffer.NormalKind = `normal`; /** * Tangents */ VertexBuffer.TangentKind = `tangent`; /** * Texture coordinates */ VertexBuffer.UVKind = `uv`; /** * Texture coordinates 2 */ VertexBuffer.UV2Kind = `uv2`; /** * Texture coordinates 3 */ VertexBuffer.UV3Kind = `uv3`; /** * Texture coordinates 4 */ VertexBuffer.UV4Kind = `uv4`; /** * Texture coordinates 5 */ VertexBuffer.UV5Kind = `uv5`; /** * Texture coordinates 6 */ VertexBuffer.UV6Kind = `uv6`; /** * Colors */ VertexBuffer.ColorKind = `color`; /** * Instance Colors */ VertexBuffer.ColorInstanceKind = `instanceColor`; /** * Matrix indices (for bones) */ VertexBuffer.MatricesIndicesKind = `matricesIndices`; /** * Matrix weights (for bones) */ VertexBuffer.MatricesWeightsKind = `matricesWeights`; /** * Additional matrix indices (for bones) */ VertexBuffer.MatricesIndicesExtraKind = `matricesIndicesExtra`; /** * Additional matrix weights (for bones) */ VertexBuffer.MatricesWeightsExtraKind = `matricesWeightsExtra`; //# sourceMappingURL=buffer.js.map