three
Version:
JavaScript 3D library
292 lines (217 loc) • 7.17 kB
JavaScript
import { generateUUID } from '../math/MathUtils.js';
import { StaticDrawUsage } from '../constants.js';
/**
* "Interleaved" means that multiple attributes, possibly of different types,
* (e.g., position, normal, uv, color) are packed into a single array buffer.
*
* An introduction into interleaved arrays can be found here: [Interleaved array basics]{@link https://blog.tojicode.com/2011/05/interleaved-array-basics.html}
*/
class InterleavedBuffer {
/**
* Constructs a new interleaved buffer.
*
* @param {TypedArray} array - A typed array with a shared buffer storing attribute data.
* @param {number} stride - The number of typed-array elements per vertex.
*/
constructor( array, stride ) {
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isInterleavedBuffer = true;
/**
* A typed array with a shared buffer storing attribute data.
*
* @type {TypedArray}
*/
this.array = array;
/**
* The number of typed-array elements per vertex.
*
* @type {number}
*/
this.stride = stride;
/**
* The total number of elements in the array
*
* @type {number}
* @readonly
*/
this.count = array !== undefined ? array.length / stride : 0;
/**
* Defines the intended usage pattern of the data store for optimization purposes.
*
* Note: After the initial use of a buffer, its usage cannot be changed. Instead,
* instantiate a new one and set the desired usage before the next render.
*
* @type {(StaticDrawUsage|DynamicDrawUsage|StreamDrawUsage|StaticReadUsage|DynamicReadUsage|StreamReadUsage|StaticCopyUsage|DynamicCopyUsage|StreamCopyUsage)}
* @default StaticDrawUsage
*/
this.usage = StaticDrawUsage;
/**
* This can be used to only update some components of stored vectors (for example, just the
* component related to color). Use the `addUpdateRange()` function to add ranges to this array.
*
* @type {Array<Object>}
*/
this.updateRanges = [];
/**
* A version number, incremented every time the `needsUpdate` is set to `true`.
*
* @type {number}
*/
this.version = 0;
/**
* The UUID of the interleaved buffer.
*
* @type {string}
* @readonly
*/
this.uuid = generateUUID();
}
/**
* A callback function that is executed after the renderer has transferred the attribute array
* data to the GPU.
*/
onUploadCallback() {}
/**
* Flag to indicate that this attribute has changed and should be re-sent to
* the GPU. Set this to `true` when you modify the value of the array.
*
* @type {number}
* @default false
* @param {boolean} value
*/
set needsUpdate( value ) {
if ( value === true ) this.version ++;
}
/**
* Sets the usage of this interleaved buffer.
*
* @param {(StaticDrawUsage|DynamicDrawUsage|StreamDrawUsage|StaticReadUsage|DynamicReadUsage|StreamReadUsage|StaticCopyUsage|DynamicCopyUsage|StreamCopyUsage)} value - The usage to set.
* @return {InterleavedBuffer} A reference to this interleaved buffer.
*/
setUsage( value ) {
this.usage = value;
return this;
}
/**
* Adds a range of data in the data array to be updated on the GPU.
*
* @param {number} start - Position at which to start update.
* @param {number} count - The number of components to update.
*/
addUpdateRange( start, count ) {
this.updateRanges.push( { start, count } );
}
/**
* Clears the update ranges.
*/
clearUpdateRanges() {
this.updateRanges.length = 0;
}
/**
* Copies the values of the given interleaved buffer to this instance.
*
* @param {InterleavedBuffer} source - The interleaved buffer to copy.
* @return {InterleavedBuffer} A reference to this instance.
*/
copy( source ) {
this.array = new source.array.constructor( source.array );
this.count = source.count;
this.stride = source.stride;
this.usage = source.usage;
return this;
}
/**
* Copies a vector from the given interleaved buffer to this one. The start
* and destination position in the attribute buffers are represented by the
* given indices.
*
* @param {number} index1 - The destination index into this interleaved buffer.
* @param {InterleavedBuffer} interleavedBuffer - The interleaved buffer to copy from.
* @param {number} index2 - The source index into the given interleaved buffer.
* @return {InterleavedBuffer} A reference to this instance.
*/
copyAt( index1, interleavedBuffer, index2 ) {
index1 *= this.stride;
index2 *= interleavedBuffer.stride;
for ( let i = 0, l = this.stride; i < l; i ++ ) {
this.array[ index1 + i ] = interleavedBuffer.array[ index2 + i ];
}
return this;
}
/**
* Sets the given array data in the interleaved buffer.
*
* @param {(TypedArray|Array)} value - The array data to set.
* @param {number} [offset=0] - The offset in this interleaved buffer's array.
* @return {InterleavedBuffer} A reference to this instance.
*/
set( value, offset = 0 ) {
this.array.set( value, offset );
return this;
}
/**
* Returns a new interleaved buffer with copied values from this instance.
*
* @param {Object} [data] - An object with shared array buffers that allows to retain shared structures.
* @return {InterleavedBuffer} A clone of this instance.
*/
clone( data ) {
if ( data.arrayBuffers === undefined ) {
data.arrayBuffers = {};
}
if ( this.array.buffer._uuid === undefined ) {
this.array.buffer._uuid = generateUUID();
}
if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) {
data.arrayBuffers[ this.array.buffer._uuid ] = this.array.slice( 0 ).buffer;
}
const array = new this.array.constructor( data.arrayBuffers[ this.array.buffer._uuid ] );
const ib = new this.constructor( array, this.stride );
ib.setUsage( this.usage );
return ib;
}
/**
* Sets the given callback function that is executed after the Renderer has transferred
* the array data to the GPU. Can be used to perform clean-up operations after
* the upload when data are not needed anymore on the CPU side.
*
* @param {Function} callback - The `onUpload()` callback.
* @return {InterleavedBuffer} A reference to this instance.
*/
onUpload( callback ) {
this.onUploadCallback = callback;
return this;
}
/**
* Serializes the interleaved buffer into JSON.
*
* @param {Object} [data] - An optional value holding meta information about the serialization.
* @return {Object} A JSON object representing the serialized interleaved buffer.
*/
toJSON( data ) {
if ( data.arrayBuffers === undefined ) {
data.arrayBuffers = {};
}
// generate UUID for array buffer if necessary
if ( this.array.buffer._uuid === undefined ) {
this.array.buffer._uuid = generateUUID();
}
if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) {
data.arrayBuffers[ this.array.buffer._uuid ] = Array.from( new Uint32Array( this.array.buffer ) );
}
//
return {
uuid: this.uuid,
buffer: this.array.buffer._uuid,
type: this.array.constructor.name,
stride: this.stride
};
}
}
export { InterleavedBuffer };