@openhps/core
Version:
Open Hybrid Positioning System - Core component
287 lines (268 loc) • 8.44 kB
JavaScript
import { nodeObject } from '../tsl/TSLBase.js';
import { NodeUpdateType } from '../core/constants.js';
import { getValueType } from '../core/NodeUtils.js';
import ArrayElementNode from '../utils/ArrayElementNode.js';
import BufferNode from './BufferNode.js';
/**
* Represents the element access on uniform array nodes.
*
* @augments ArrayElementNode
*/
class UniformArrayElementNode extends ArrayElementNode {
static get type() {
return 'UniformArrayElementNode';
}
/**
* Constructs a new buffer node.
*
* @param {UniformArrayNode} uniformArrayNode - The uniform array node to access.
* @param {IndexNode} indexNode - The index data that define the position of the accessed element in the array.
*/
constructor(uniformArrayNode, indexNode) {
super(uniformArrayNode, indexNode);
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isArrayBufferElementNode = true;
}
generate(builder) {
const snippet = super.generate(builder);
const type = this.getNodeType();
const paddedType = this.node.getPaddedType();
return builder.format(snippet, paddedType, type);
}
}
/**
* Similar to {@link BufferNode} this module represents array-like data as
* uniform buffers. Unlike {@link BufferNode}, it can handle more common
* data types in the array (e.g `three.js` primitives) and automatically
* manage buffer padding. It should be the first choice when working with
* uniforms buffers.
* ```js
* const tintColors = uniformArray( [
* new Color( 1, 0, 0 ),
* new Color( 0, 1, 0 ),
* new Color( 0, 0, 1 )
* ], 'color' );
*
* const redColor = tintColors.element( 0 );
*
* @augments BufferNode
*/
class UniformArrayNode extends BufferNode {
static get type() {
return 'UniformArrayNode';
}
/**
* Constructs a new uniform array node.
*
* @param {Array<any>} value - Array holding the buffer data.
* @param {?string} [elementType=null] - The data type of a buffer element.
*/
constructor(value, elementType = null) {
super(null);
/**
* Array holding the buffer data. Unlike {@link BufferNode}, the array can
* hold number primitives as well as three.js objects like vectors, matrices
* or colors.
*
* @type {Array<any>}
*/
this.array = value;
/**
* The data type of an array element.
*
* @type {string}
*/
this.elementType = elementType === null ? getValueType(value[0]) : elementType;
/**
* The padded type. Uniform buffers must conform to a certain buffer layout
* so a separate type is computed to ensure correct buffer size.
*
* @type {string}
*/
this.paddedType = this.getPaddedType();
/**
* Overwritten since uniform array nodes are updated per render.
*
* @type {string}
* @default 'render'
*/
this.updateType = NodeUpdateType.RENDER;
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isArrayBufferNode = true;
}
/**
* This method is overwritten since the node type is inferred from the
* {@link UniformArrayNode#paddedType}.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {string} The node type.
*/
getNodeType( /*builder*/
) {
return this.paddedType;
}
/**
* The data type of the array elements.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {string} The element type.
*/
getElementType() {
return this.elementType;
}
/**
* Returns the padded type based on the element type.
*
* @return {string} The padded type.
*/
getPaddedType() {
const elementType = this.elementType;
let paddedType = 'vec4';
if (elementType === 'mat2') {
paddedType = 'mat2';
} else if (/mat/.test(elementType) === true) {
paddedType = 'mat4';
} else if (elementType.charAt(0) === 'i') {
paddedType = 'ivec4';
} else if (elementType.charAt(0) === 'u') {
paddedType = 'uvec4';
}
return paddedType;
}
/**
* The update makes sure to correctly transfer the data from the (complex) objects
* in the array to the internal, correctly padded value buffer.
*
* @param {NodeFrame} frame - A reference to the current node frame.
*/
update( /*frame*/
) {
const {
array,
value
} = this;
const elementType = this.elementType;
if (elementType === 'float' || elementType === 'int' || elementType === 'uint') {
for (let i = 0; i < array.length; i++) {
const index = i * 4;
value[index] = array[i];
}
} else if (elementType === 'color') {
for (let i = 0; i < array.length; i++) {
const index = i * 4;
const vector = array[i];
value[index] = vector.r;
value[index + 1] = vector.g;
value[index + 2] = vector.b || 0;
//value[ index + 3 ] = vector.a || 0;
}
} else if (elementType === 'mat2') {
for (let i = 0; i < array.length; i++) {
const index = i * 4;
const matrix = array[i];
value[index] = matrix.elements[0];
value[index + 1] = matrix.elements[1];
value[index + 2] = matrix.elements[2];
value[index + 3] = matrix.elements[3];
}
} else if (elementType === 'mat3') {
for (let i = 0; i < array.length; i++) {
const index = i * 16;
const matrix = array[i];
value[index] = matrix.elements[0];
value[index + 1] = matrix.elements[1];
value[index + 2] = matrix.elements[2];
value[index + 4] = matrix.elements[3];
value[index + 5] = matrix.elements[4];
value[index + 6] = matrix.elements[5];
value[index + 8] = matrix.elements[6];
value[index + 9] = matrix.elements[7];
value[index + 10] = matrix.elements[8];
value[index + 15] = 1;
}
} else if (elementType === 'mat4') {
for (let i = 0; i < array.length; i++) {
const index = i * 16;
const matrix = array[i];
for (let i = 0; i < matrix.elements.length; i++) {
value[index + i] = matrix.elements[i];
}
}
} else {
for (let i = 0; i < array.length; i++) {
const index = i * 4;
const vector = array[i];
value[index] = vector.x;
value[index + 1] = vector.y;
value[index + 2] = vector.z || 0;
value[index + 3] = vector.w || 0;
}
}
}
/**
* Implement the value buffer creation based on the array data.
*
* @param {NodeBuilder} builder - A reference to the current node builder.
* @return {null}
*/
setup(builder) {
const length = this.array.length;
const elementType = this.elementType;
let arrayType = Float32Array;
const paddedType = this.paddedType;
const paddedElementLength = builder.getTypeLength(paddedType);
if (elementType.charAt(0) === 'i') arrayType = Int32Array;
if (elementType.charAt(0) === 'u') arrayType = Uint32Array;
this.value = new arrayType(length * paddedElementLength);
this.bufferCount = length;
this.bufferType = paddedType;
return super.setup(builder);
}
/**
* Overwrites the default `element()` method to provide element access
* based on {@link UniformArrayNode}.
*
* @param {IndexNode} indexNode - The index node.
* @return {UniformArrayElementNode}
*/
element(indexNode) {
return nodeObject(new UniformArrayElementNode(this, nodeObject(indexNode)));
}
}
export default UniformArrayNode;
/**
* TSL function for creating an uniform array node.
*
* @tsl
* @function
* @param {Array<any>} values - Array-like data.
* @param {?string} [nodeType] - The data type of the array elements.
* @returns {UniformArrayNode}
*/
export const uniformArray = (values, nodeType) => nodeObject(new UniformArrayNode(values, nodeType));
/**
* @tsl
* @function
* @deprecated since r168. Use {@link uniformArray} instead.
*
* @param {Array<any>} values - Array-like data.
* @param {string} nodeType - The data type of the array elements.
* @returns {UniformArrayNode}
*/
export const uniforms = (values, nodeType) => {
// @deprecated, r168
console.warn('THREE.TSL: uniforms() has been renamed to uniformArray().');
return nodeObject(new UniformArrayNode(values, nodeType));
};