three
Version:
JavaScript 3D library
1,045 lines (754 loc) • 24.6 kB
JavaScript
import { Vector3 } from '../math/Vector3.js';
import { Vector2 } from '../math/Vector2.js';
import { denormalize, normalize } from '../math/MathUtils.js';
import { StaticDrawUsage, FloatType } from '../constants.js';
import { fromHalfFloat, toHalfFloat } from '../extras/DataUtils.js';
const _vector = /*@__PURE__*/ new Vector3();
const _vector2 = /*@__PURE__*/ new Vector2();
let _id = 0;
/**
* This class stores data for an attribute (such as vertex positions, face
* indices, normals, colors, UVs, and any custom attributes ) associated with
* a geometry, which allows for more efficient passing of data to the GPU.
*
* When working with vector-like data, the `fromBufferAttribute( attribute, index )`
* helper methods on vector and color class might be helpful. E.g. {@link Vector3#fromBufferAttribute}.
*/
class BufferAttribute {
/**
* Constructs a new buffer attribute.
*
* @param {TypedArray} array - The array holding the attribute data.
* @param {number} itemSize - The item size.
* @param {boolean} [normalized=false] - Whether the data are normalized or not.
*/
constructor( array, itemSize, normalized = false ) {
if ( Array.isArray( array ) ) {
throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' );
}
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isBufferAttribute = true;
/**
* The ID of the buffer attribute.
*
* @name BufferAttribute#id
* @type {number}
* @readonly
*/
Object.defineProperty( this, 'id', { value: _id ++ } );
/**
* The name of the buffer attribute.
*
* @type {string}
*/
this.name = '';
/**
* The array holding the attribute data. It should have `itemSize * numVertices`
* elements, where `numVertices` is the number of vertices in the associated geometry.
*
* @type {TypedArray}
*/
this.array = array;
/**
* The number of values of the array that should be associated with a particular vertex.
* For instance, if this attribute is storing a 3-component vector (such as a position,
* normal, or color), then the value should be `3`.
*
* @type {number}
*/
this.itemSize = itemSize;
/**
* Represents the number of items this buffer attribute stores. It is internally computed
* by dividing the `array` length by the `itemSize`.
*
* @type {number}
* @readonly
*/
this.count = array !== undefined ? array.length / itemSize : 0;
/**
* Applies to integer data only. Indicates how the underlying data in the buffer maps to
* the values in the GLSL code. For instance, if `array` is an instance of `UInt16Array`,
* and `normalized` is `true`, the values `0 -+65535` in the array data will be mapped to
* `0.0f - +1.0f` in the GLSL attribute. If `normalized` is `false`, the values will be converted
* to floats unmodified, i.e. `65535` becomes `65535.0f`.
*
* @type {boolean}
*/
this.normalized = normalized;
/**
* 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 = [];
/**
* Configures the bound GPU type for use in shaders.
*
* Note: this only has an effect for integer arrays and is not configurable for float arrays.
* For lower precision float types, use `Float16BufferAttribute`.
*
* @type {(FloatType|IntType)}
* @default FloatType
*/
this.gpuType = FloatType;
/**
* A version number, incremented every time the `needsUpdate` is set to `true`.
*
* @type {number}
*/
this.version = 0;
}
/**
* 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 buffer attribute.
*
* @param {(StaticDrawUsage|DynamicDrawUsage|StreamDrawUsage|StaticReadUsage|DynamicReadUsage|StreamReadUsage|StaticCopyUsage|DynamicCopyUsage|StreamCopyUsage)} value - The usage to set.
* @return {BufferAttribute} A reference to this buffer attribute.
*/
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 buffer attribute to this instance.
*
* @param {BufferAttribute} source - The buffer attribute to copy.
* @return {BufferAttribute} A reference to this instance.
*/
copy( source ) {
this.name = source.name;
this.array = new source.array.constructor( source.array );
this.itemSize = source.itemSize;
this.count = source.count;
this.normalized = source.normalized;
this.usage = source.usage;
this.gpuType = source.gpuType;
return this;
}
/**
* Copies a vector from the given buffer attribute 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 buffer attribute.
* @param {BufferAttribute} attribute - The buffer attribute to copy from.
* @param {number} index2 - The source index into the given buffer attribute.
* @return {BufferAttribute} A reference to this instance.
*/
copyAt( index1, attribute, index2 ) {
index1 *= this.itemSize;
index2 *= attribute.itemSize;
for ( let i = 0, l = this.itemSize; i < l; i ++ ) {
this.array[ index1 + i ] = attribute.array[ index2 + i ];
}
return this;
}
/**
* Copies the given array data into this buffer attribute.
*
* @param {(TypedArray|Array)} array - The array to copy.
* @return {BufferAttribute} A reference to this instance.
*/
copyArray( array ) {
this.array.set( array );
return this;
}
/**
* Applies the given 3x3 matrix to the given attribute. Works with
* item size `2` and `3`.
*
* @param {Matrix3} m - The matrix to apply.
* @return {BufferAttribute} A reference to this instance.
*/
applyMatrix3( m ) {
if ( this.itemSize === 2 ) {
for ( let i = 0, l = this.count; i < l; i ++ ) {
_vector2.fromBufferAttribute( this, i );
_vector2.applyMatrix3( m );
this.setXY( i, _vector2.x, _vector2.y );
}
} else if ( this.itemSize === 3 ) {
for ( let i = 0, l = this.count; i < l; i ++ ) {
_vector.fromBufferAttribute( this, i );
_vector.applyMatrix3( m );
this.setXYZ( i, _vector.x, _vector.y, _vector.z );
}
}
return this;
}
/**
* Applies the given 4x4 matrix to the given attribute. Only works with
* item size `3`.
*
* @param {Matrix4} m - The matrix to apply.
* @return {BufferAttribute} A reference to this instance.
*/
applyMatrix4( m ) {
for ( let i = 0, l = this.count; i < l; i ++ ) {
_vector.fromBufferAttribute( this, i );
_vector.applyMatrix4( m );
this.setXYZ( i, _vector.x, _vector.y, _vector.z );
}
return this;
}
/**
* Applies the given 3x3 normal matrix to the given attribute. Only works with
* item size `3`.
*
* @param {Matrix3} m - The normal matrix to apply.
* @return {BufferAttribute} A reference to this instance.
*/
applyNormalMatrix( m ) {
for ( let i = 0, l = this.count; i < l; i ++ ) {
_vector.fromBufferAttribute( this, i );
_vector.applyNormalMatrix( m );
this.setXYZ( i, _vector.x, _vector.y, _vector.z );
}
return this;
}
/**
* Applies the given 4x4 matrix to the given attribute. Only works with
* item size `3` and with direction vectors.
*
* @param {Matrix4} m - The matrix to apply.
* @return {BufferAttribute} A reference to this instance.
*/
transformDirection( m ) {
for ( let i = 0, l = this.count; i < l; i ++ ) {
_vector.fromBufferAttribute( this, i );
_vector.transformDirection( m );
this.setXYZ( i, _vector.x, _vector.y, _vector.z );
}
return this;
}
/**
* Sets the given array data in the buffer attribute.
*
* @param {(TypedArray|Array)} value - The array data to set.
* @param {number} [offset=0] - The offset in this buffer attribute's array.
* @return {BufferAttribute} A reference to this instance.
*/
set( value, offset = 0 ) {
// Matching BufferAttribute constructor, do not normalize the array.
this.array.set( value, offset );
return this;
}
/**
* Returns the given component of the vector at the given index.
*
* @param {number} index - The index into the buffer attribute.
* @param {number} component - The component index.
* @return {number} The returned value.
*/
getComponent( index, component ) {
let value = this.array[ index * this.itemSize + component ];
if ( this.normalized ) value = denormalize( value, this.array );
return value;
}
/**
* Sets the given value to the given component of the vector at the given index.
*
* @param {number} index - The index into the buffer attribute.
* @param {number} component - The component index.
* @param {number} value - The value to set.
* @return {BufferAttribute} A reference to this instance.
*/
setComponent( index, component, value ) {
if ( this.normalized ) value = normalize( value, this.array );
this.array[ index * this.itemSize + component ] = value;
return this;
}
/**
* Returns the x component of the vector at the given index.
*
* @param {number} index - The index into the buffer attribute.
* @return {number} The x component.
*/
getX( index ) {
let x = this.array[ index * this.itemSize ];
if ( this.normalized ) x = denormalize( x, this.array );
return x;
}
/**
* Sets the x component of the vector at the given index.
*
* @param {number} index - The index into the buffer attribute.
* @param {number} x - The value to set.
* @return {BufferAttribute} A reference to this instance.
*/
setX( index, x ) {
if ( this.normalized ) x = normalize( x, this.array );
this.array[ index * this.itemSize ] = x;
return this;
}
/**
* Returns the y component of the vector at the given index.
*
* @param {number} index - The index into the buffer attribute.
* @return {number} The y component.
*/
getY( index ) {
let y = this.array[ index * this.itemSize + 1 ];
if ( this.normalized ) y = denormalize( y, this.array );
return y;
}
/**
* Sets the y component of the vector at the given index.
*
* @param {number} index - The index into the buffer attribute.
* @param {number} y - The value to set.
* @return {BufferAttribute} A reference to this instance.
*/
setY( index, y ) {
if ( this.normalized ) y = normalize( y, this.array );
this.array[ index * this.itemSize + 1 ] = y;
return this;
}
/**
* Returns the z component of the vector at the given index.
*
* @param {number} index - The index into the buffer attribute.
* @return {number} The z component.
*/
getZ( index ) {
let z = this.array[ index * this.itemSize + 2 ];
if ( this.normalized ) z = denormalize( z, this.array );
return z;
}
/**
* Sets the z component of the vector at the given index.
*
* @param {number} index - The index into the buffer attribute.
* @param {number} z - The value to set.
* @return {BufferAttribute} A reference to this instance.
*/
setZ( index, z ) {
if ( this.normalized ) z = normalize( z, this.array );
this.array[ index * this.itemSize + 2 ] = z;
return this;
}
/**
* Returns the w component of the vector at the given index.
*
* @param {number} index - The index into the buffer attribute.
* @return {number} The w component.
*/
getW( index ) {
let w = this.array[ index * this.itemSize + 3 ];
if ( this.normalized ) w = denormalize( w, this.array );
return w;
}
/**
* Sets the w component of the vector at the given index.
*
* @param {number} index - The index into the buffer attribute.
* @param {number} w - The value to set.
* @return {BufferAttribute} A reference to this instance.
*/
setW( index, w ) {
if ( this.normalized ) w = normalize( w, this.array );
this.array[ index * this.itemSize + 3 ] = w;
return this;
}
/**
* Sets the x and y component of the vector at the given index.
*
* @param {number} index - The index into the buffer attribute.
* @param {number} x - The value for the x component to set.
* @param {number} y - The value for the y component to set.
* @return {BufferAttribute} A reference to this instance.
*/
setXY( index, x, y ) {
index *= this.itemSize;
if ( this.normalized ) {
x = normalize( x, this.array );
y = normalize( y, this.array );
}
this.array[ index + 0 ] = x;
this.array[ index + 1 ] = y;
return this;
}
/**
* Sets the x, y and z component of the vector at the given index.
*
* @param {number} index - The index into the buffer attribute.
* @param {number} x - The value for the x component to set.
* @param {number} y - The value for the y component to set.
* @param {number} z - The value for the z component to set.
* @return {BufferAttribute} A reference to this instance.
*/
setXYZ( index, x, y, z ) {
index *= this.itemSize;
if ( this.normalized ) {
x = normalize( x, this.array );
y = normalize( y, this.array );
z = normalize( z, this.array );
}
this.array[ index + 0 ] = x;
this.array[ index + 1 ] = y;
this.array[ index + 2 ] = z;
return this;
}
/**
* Sets the x, y, z and w component of the vector at the given index.
*
* @param {number} index - The index into the buffer attribute.
* @param {number} x - The value for the x component to set.
* @param {number} y - The value for the y component to set.
* @param {number} z - The value for the z component to set.
* @param {number} w - The value for the w component to set.
* @return {BufferAttribute} A reference to this instance.
*/
setXYZW( index, x, y, z, w ) {
index *= this.itemSize;
if ( this.normalized ) {
x = normalize( x, this.array );
y = normalize( y, this.array );
z = normalize( z, this.array );
w = normalize( w, this.array );
}
this.array[ index + 0 ] = x;
this.array[ index + 1 ] = y;
this.array[ index + 2 ] = z;
this.array[ index + 3 ] = w;
return this;
}
/**
* Sets the given callback function that is executed after the Renderer has transferred
* the attribute array data to the GPU. Can be used to perform clean-up operations after
* the upload when attribute data are not needed anymore on the CPU side.
*
* @param {Function} callback - The `onUpload()` callback.
* @return {BufferAttribute} A reference to this instance.
*/
onUpload( callback ) {
this.onUploadCallback = callback;
return this;
}
/**
* Returns a new buffer attribute with copied values from this instance.
*
* @return {BufferAttribute} A clone of this instance.
*/
clone() {
return new this.constructor( this.array, this.itemSize ).copy( this );
}
/**
* Serializes the buffer attribute into JSON.
*
* @return {Object} A JSON object representing the serialized buffer attribute.
*/
toJSON() {
const data = {
itemSize: this.itemSize,
type: this.array.constructor.name,
array: Array.from( this.array ),
normalized: this.normalized
};
if ( this.name !== '' ) data.name = this.name;
if ( this.usage !== StaticDrawUsage ) data.usage = this.usage;
return data;
}
}
/**
* Convenient class that can be used when creating a `Int8` buffer attribute with
* a plain `Array` instance.
*
* @augments BufferAttribute
*/
class Int8BufferAttribute extends BufferAttribute {
/**
* Constructs a new buffer attribute.
*
* @param {(Array<number>|Int8Array)} array - The array holding the attribute data.
* @param {number} itemSize - The item size.
* @param {boolean} [normalized=false] - Whether the data are normalized or not.
*/
constructor( array, itemSize, normalized ) {
super( new Int8Array( array ), itemSize, normalized );
}
}
/**
* Convenient class that can be used when creating a `UInt8` buffer attribute with
* a plain `Array` instance.
*
* @augments BufferAttribute
*/
class Uint8BufferAttribute extends BufferAttribute {
/**
* Constructs a new buffer attribute.
*
* @param {(Array<number>|Uint8Array)} array - The array holding the attribute data.
* @param {number} itemSize - The item size.
* @param {boolean} [normalized=false] - Whether the data are normalized or not.
*/
constructor( array, itemSize, normalized ) {
super( new Uint8Array( array ), itemSize, normalized );
}
}
/**
* Convenient class that can be used when creating a `UInt8Clamped` buffer attribute with
* a plain `Array` instance.
*
* @augments BufferAttribute
*/
class Uint8ClampedBufferAttribute extends BufferAttribute {
/**
* Constructs a new buffer attribute.
*
* @param {(Array<number>|Uint8ClampedArray)} array - The array holding the attribute data.
* @param {number} itemSize - The item size.
* @param {boolean} [normalized=false] - Whether the data are normalized or not.
*/
constructor( array, itemSize, normalized ) {
super( new Uint8ClampedArray( array ), itemSize, normalized );
}
}
/**
* Convenient class that can be used when creating a `Int16` buffer attribute with
* a plain `Array` instance.
*
* @augments BufferAttribute
*/
class Int16BufferAttribute extends BufferAttribute {
/**
* Constructs a new buffer attribute.
*
* @param {(Array<number>|Int16Array)} array - The array holding the attribute data.
* @param {number} itemSize - The item size.
* @param {boolean} [normalized=false] - Whether the data are normalized or not.
*/
constructor( array, itemSize, normalized ) {
super( new Int16Array( array ), itemSize, normalized );
}
}
/**
* Convenient class that can be used when creating a `UInt16` buffer attribute with
* a plain `Array` instance.
*
* @augments BufferAttribute
*/
class Uint16BufferAttribute extends BufferAttribute {
/**
* Constructs a new buffer attribute.
*
* @param {(Array<number>|Uint16Array)} array - The array holding the attribute data.
* @param {number} itemSize - The item size.
* @param {boolean} [normalized=false] - Whether the data are normalized or not.
*/
constructor( array, itemSize, normalized ) {
super( new Uint16Array( array ), itemSize, normalized );
}
}
/**
* Convenient class that can be used when creating a `Int32` buffer attribute with
* a plain `Array` instance.
*
* @augments BufferAttribute
*/
class Int32BufferAttribute extends BufferAttribute {
/**
* Constructs a new buffer attribute.
*
* @param {(Array<number>|Int32Array)} array - The array holding the attribute data.
* @param {number} itemSize - The item size.
* @param {boolean} [normalized=false] - Whether the data are normalized or not.
*/
constructor( array, itemSize, normalized ) {
super( new Int32Array( array ), itemSize, normalized );
}
}
/**
* Convenient class that can be used when creating a `UInt32` buffer attribute with
* a plain `Array` instance.
*
* @augments BufferAttribute
*/
class Uint32BufferAttribute extends BufferAttribute {
/**
* Constructs a new buffer attribute.
*
* @param {(Array<number>|Uint32Array)} array - The array holding the attribute data.
* @param {number} itemSize - The item size.
* @param {boolean} [normalized=false] - Whether the data are normalized or not.
*/
constructor( array, itemSize, normalized ) {
super( new Uint32Array( array ), itemSize, normalized );
}
}
/**
* Convenient class that can be used when creating a `Float16` buffer attribute with
* a plain `Array` instance.
*
* This class automatically converts to to and from FP16 since `Float16Array` is not
* natively supported in JavaScript.
*
* @augments BufferAttribute
*/
class Float16BufferAttribute extends BufferAttribute {
/**
* Constructs a new buffer attribute.
*
* @param {(Array<number>|Uint16Array)} array - The array holding the attribute data.
* @param {number} itemSize - The item size.
* @param {boolean} [normalized=false] - Whether the data are normalized or not.
*/
constructor( array, itemSize, normalized ) {
super( new Uint16Array( array ), itemSize, normalized );
this.isFloat16BufferAttribute = true;
}
getX( index ) {
let x = fromHalfFloat( this.array[ index * this.itemSize ] );
if ( this.normalized ) x = denormalize( x, this.array );
return x;
}
setX( index, x ) {
if ( this.normalized ) x = normalize( x, this.array );
this.array[ index * this.itemSize ] = toHalfFloat( x );
return this;
}
getY( index ) {
let y = fromHalfFloat( this.array[ index * this.itemSize + 1 ] );
if ( this.normalized ) y = denormalize( y, this.array );
return y;
}
setY( index, y ) {
if ( this.normalized ) y = normalize( y, this.array );
this.array[ index * this.itemSize + 1 ] = toHalfFloat( y );
return this;
}
getZ( index ) {
let z = fromHalfFloat( this.array[ index * this.itemSize + 2 ] );
if ( this.normalized ) z = denormalize( z, this.array );
return z;
}
setZ( index, z ) {
if ( this.normalized ) z = normalize( z, this.array );
this.array[ index * this.itemSize + 2 ] = toHalfFloat( z );
return this;
}
getW( index ) {
let w = fromHalfFloat( this.array[ index * this.itemSize + 3 ] );
if ( this.normalized ) w = denormalize( w, this.array );
return w;
}
setW( index, w ) {
if ( this.normalized ) w = normalize( w, this.array );
this.array[ index * this.itemSize + 3 ] = toHalfFloat( w );
return this;
}
setXY( index, x, y ) {
index *= this.itemSize;
if ( this.normalized ) {
x = normalize( x, this.array );
y = normalize( y, this.array );
}
this.array[ index + 0 ] = toHalfFloat( x );
this.array[ index + 1 ] = toHalfFloat( y );
return this;
}
setXYZ( index, x, y, z ) {
index *= this.itemSize;
if ( this.normalized ) {
x = normalize( x, this.array );
y = normalize( y, this.array );
z = normalize( z, this.array );
}
this.array[ index + 0 ] = toHalfFloat( x );
this.array[ index + 1 ] = toHalfFloat( y );
this.array[ index + 2 ] = toHalfFloat( z );
return this;
}
setXYZW( index, x, y, z, w ) {
index *= this.itemSize;
if ( this.normalized ) {
x = normalize( x, this.array );
y = normalize( y, this.array );
z = normalize( z, this.array );
w = normalize( w, this.array );
}
this.array[ index + 0 ] = toHalfFloat( x );
this.array[ index + 1 ] = toHalfFloat( y );
this.array[ index + 2 ] = toHalfFloat( z );
this.array[ index + 3 ] = toHalfFloat( w );
return this;
}
}
/**
* Convenient class that can be used when creating a `Float32` buffer attribute with
* a plain `Array` instance.
*
* @augments BufferAttribute
*/
class Float32BufferAttribute extends BufferAttribute {
/**
* Constructs a new buffer attribute.
*
* @param {(Array<number>|Float32Array)} array - The array holding the attribute data.
* @param {number} itemSize - The item size.
* @param {boolean} [normalized=false] - Whether the data are normalized or not.
*/
constructor( array, itemSize, normalized ) {
super( new Float32Array( array ), itemSize, normalized );
}
}
//
export {
Float32BufferAttribute,
Float16BufferAttribute,
Uint32BufferAttribute,
Int32BufferAttribute,
Uint16BufferAttribute,
Int16BufferAttribute,
Uint8ClampedBufferAttribute,
Uint8BufferAttribute,
Int8BufferAttribute,
BufferAttribute
};