playcanvas
Version:
PlayCanvas WebGL game engine
355 lines (352 loc) • 15.2 kB
JavaScript
import { Debug } from '../../core/debug.js';
import { typedArrayTypes } from './constants.js';
/**
* @import { ScopeId } from './scope-id.js'
* @import { VertexBuffer } from './vertex-buffer.js'
* @import { VertexFormat } from './vertex-format.js'
*/ function set1(a) {
this.array[this.index] = a;
}
function set2(a, b) {
this.array[this.index] = a;
this.array[this.index + 1] = b;
}
function set3(a, b, c) {
this.array[this.index] = a;
this.array[this.index + 1] = b;
this.array[this.index + 2] = c;
}
function set4(a, b, c, d) {
this.array[this.index] = a;
this.array[this.index + 1] = b;
this.array[this.index + 2] = c;
this.array[this.index + 3] = d;
}
function arraySet1(index, inputArray, inputIndex) {
this.array[index] = inputArray[inputIndex];
}
function arraySet2(index, inputArray, inputIndex) {
this.array[index] = inputArray[inputIndex];
this.array[index + 1] = inputArray[inputIndex + 1];
}
function arraySet3(index, inputArray, inputIndex) {
this.array[index] = inputArray[inputIndex];
this.array[index + 1] = inputArray[inputIndex + 1];
this.array[index + 2] = inputArray[inputIndex + 2];
}
function arraySet4(index, inputArray, inputIndex) {
this.array[index] = inputArray[inputIndex];
this.array[index + 1] = inputArray[inputIndex + 1];
this.array[index + 2] = inputArray[inputIndex + 2];
this.array[index + 3] = inputArray[inputIndex + 3];
}
function arrayGet1(offset, outputArray, outputIndex) {
outputArray[outputIndex] = this.array[offset];
}
function arrayGet2(offset, outputArray, outputIndex) {
outputArray[outputIndex] = this.array[offset];
outputArray[outputIndex + 1] = this.array[offset + 1];
}
function arrayGet3(offset, outputArray, outputIndex) {
outputArray[outputIndex] = this.array[offset];
outputArray[outputIndex + 1] = this.array[offset + 1];
outputArray[outputIndex + 2] = this.array[offset + 2];
}
function arrayGet4(offset, outputArray, outputIndex) {
outputArray[outputIndex] = this.array[offset];
outputArray[outputIndex + 1] = this.array[offset + 1];
outputArray[outputIndex + 2] = this.array[offset + 2];
outputArray[outputIndex + 3] = this.array[offset + 3];
}
/**
* Helps with accessing a specific vertex attribute.
*
* @category Graphics
* @ignore
*/ class VertexIteratorAccessor {
/**
* Create a new VertexIteratorAccessor instance.
*
* @param {ArrayBuffer} buffer - The vertex buffer containing the attribute to be accessed.
* @param {object} vertexElement - The vertex attribute to be accessed.
* @param {string} vertexElement.name - The meaning of the vertex element. This is used to link
* the vertex data to a shader input. Can be:
*
* - {@link SEMANTIC_POSITION}
* - {@link SEMANTIC_NORMAL}
* - {@link SEMANTIC_TANGENT}
* - {@link SEMANTIC_BLENDWEIGHT}
* - {@link SEMANTIC_BLENDINDICES}
* - {@link SEMANTIC_COLOR}
* - {@link SEMANTIC_TEXCOORD0}
* - {@link SEMANTIC_TEXCOORD1}
* - {@link SEMANTIC_TEXCOORD2}
* - {@link SEMANTIC_TEXCOORD3}
* - {@link SEMANTIC_TEXCOORD4}
* - {@link SEMANTIC_TEXCOORD5}
* - {@link SEMANTIC_TEXCOORD6}
* - {@link SEMANTIC_TEXCOORD7}
*
* If vertex data has a meaning other that one of those listed above, use the user-defined
* semantics: {@link SEMANTIC_ATTR0} to {@link SEMANTIC_ATTR15}.
* @param {number} vertexElement.numComponents - The number of components of the vertex
* attribute. Can be 1, 2, 3 or 4.
* @param {number} vertexElement.dataType - The data type of the attribute. Can be:
*
* - {@link TYPE_INT8}
* - {@link TYPE_UINT8}
* - {@link TYPE_INT16}
* - {@link TYPE_UINT16}
* - {@link TYPE_INT32}
* - {@link TYPE_UINT32}
* - {@link TYPE_FLOAT32}
* @param {boolean} vertexElement.normalize - If true, vertex attribute data will be mapped
* from a 0 to 255 range down to 0 to 1 when fed to a shader. If false, vertex attribute data
* is left unchanged. If this property is unspecified, false is assumed.
* @param {number} vertexElement.offset - The number of initial bytes at the start of a vertex
* that are not relevant to this attribute.
* @param {number} vertexElement.stride - The number of total bytes that are between the start
* of one vertex, and the start of the next.
* @param {ScopeId} vertexElement.scopeId - The shader input variable corresponding to the
* attribute.
* @param {number} vertexElement.size - The size of the attribute in bytes.
* @param {VertexFormat} vertexFormat - A vertex format that defines the layout of vertex data
* inside the buffer.
*/ constructor(buffer, vertexElement, vertexFormat){
this.index = 0;
this.numComponents = vertexElement.numComponents;
// create the typed array based on the element data type
if (vertexFormat.interleaved) {
this.array = new typedArrayTypes[vertexElement.dataType](buffer, vertexElement.offset);
} else {
this.array = new typedArrayTypes[vertexElement.dataType](buffer, vertexElement.offset, vertexFormat.vertexCount * vertexElement.numComponents);
}
// BYTES_PER_ELEMENT is on the instance and constructor for Chrome, Safari and Firefox, but just the constructor for Opera
this.stride = vertexElement.stride / this.array.constructor.BYTES_PER_ELEMENT;
// Methods
switch(vertexElement.numComponents){
case 1:
this.set = set1;
this.getToArray = arrayGet1;
this.setFromArray = arraySet1;
break;
case 2:
this.set = set2;
this.getToArray = arrayGet2;
this.setFromArray = arraySet2;
break;
case 3:
this.set = set3;
this.getToArray = arrayGet3;
this.setFromArray = arraySet3;
break;
case 4:
this.set = set4;
this.getToArray = arrayGet4;
this.setFromArray = arraySet4;
break;
}
}
/**
* Get a attribute component at the iterator's current index.
*
* @param {number} offset - The component offset. Should be either 0, 1, 2, or 3.
* @returns {number} The value of a attribute component.
*/ get(offset) {
return this.array[this.index + offset];
}
/**
* Set all the attribute components at the iterator's current index.
*
* @param {number} a - The first component value.
* @param {number} [b] - The second component value (if applicable).
* @param {number} [c] - The third component value (if applicable).
* @param {number} [d] - The fourth component value (if applicable).
*/ set(a, b, c, d) {
// Will be replaced with specialized implementation based on number of components
}
/**
* Read attribute components to an output array.
*
* @param {number} offset - The component offset at which to read data from the buffer. Will be
* used instead of the iterator's current index.
* @param {number[]|ArrayBufferView} outputArray - The output array to write data into.
* @param {number} outputIndex - The output index at which to write into the output array.
*/ getToArray(offset, outputArray, outputIndex) {
// Will be replaced with specialized implementation based on number of components
}
/**
* Write attribute components from an input array.
*
* @param {number} index - The starting index at which to write data into the buffer. Will be
* used instead of the iterator's current index.
* @param {number[]|ArrayBufferView} inputArray - The input array to read data from.
* @param {number} inputIndex - The input index at which to read from the input array.
*/ setFromArray(index, inputArray, inputIndex) {
// Will be replaced with specialized implementation based on number of components
}
}
/**
* A vertex iterator simplifies the process of writing vertex data to a vertex buffer.
*
* @category Graphics
*/ class VertexIterator {
/**
* Create a new VertexIterator instance.
*
* @param {VertexBuffer} vertexBuffer - The vertex buffer to be iterated.
*/ constructor(vertexBuffer){
// Store the vertex buffer
this.vertexBuffer = vertexBuffer;
this.vertexFormatSize = vertexBuffer.getFormat().size;
// Lock the vertex buffer
this.buffer = this.vertexBuffer.lock();
// Create an empty list
this.accessors = [];
/**
* The vertex buffer elements.
*
* @type {Object<string, VertexIteratorAccessor>}
*/ this.element = {};
// Add a new 'setter' function for each element
const vertexFormat = this.vertexBuffer.getFormat();
for(let i = 0; i < vertexFormat.elements.length; i++){
const vertexElement = vertexFormat.elements[i];
this.accessors[i] = new VertexIteratorAccessor(this.buffer, vertexElement, vertexFormat);
this.element[vertexElement.name] = this.accessors[i];
}
}
/**
* Moves the vertex iterator on to the next vertex.
*
* @param {number} [count] - Number of steps to move on when calling next. Defaults to 1.
* @example
* const iterator = new pc.VertexIterator(vertexBuffer);
* iterator.element[pc.SEMANTIC_POSITION].set(-0.9, -0.9, 0.0);
* iterator.element[pc.SEMANTIC_COLOR].set(255, 0, 0, 255);
* iterator.next();
* iterator.element[pc.SEMANTIC_POSITION].set(0.9, -0.9, 0.0);
* iterator.element[pc.SEMANTIC_COLOR].set(0, 255, 0, 255);
* iterator.next();
* iterator.element[pc.SEMANTIC_POSITION].set(0.0, 0.9, 0.0);
* iterator.element[pc.SEMANTIC_COLOR].set(0, 0, 255, 255);
* iterator.end();
*/ next(count = 1) {
let i = 0;
const accessors = this.accessors;
const numAccessors = this.accessors.length;
while(i < numAccessors){
const accessor = accessors[i++];
accessor.index += count * accessor.stride;
}
}
/**
* Notifies the vertex buffer being iterated that writes are complete. Internally the vertex
* buffer is unlocked and vertex data is uploaded to video memory.
*
* @example
* const iterator = new pc.VertexIterator(vertexBuffer);
* iterator.element[pc.SEMANTIC_POSITION].set(-0.9, -0.9, 0.0);
* iterator.element[pc.SEMANTIC_COLOR].set(255, 0, 0, 255);
* iterator.next();
* iterator.element[pc.SEMANTIC_POSITION].set(0.9, -0.9, 0.0);
* iterator.element[pc.SEMANTIC_COLOR].set(0, 255, 0, 255);
* iterator.next();
* iterator.element[pc.SEMANTIC_POSITION].set(0.0, 0.9, 0.0);
* iterator.element[pc.SEMANTIC_COLOR].set(0, 0, 255, 255);
* iterator.end();
*/ end() {
// Unlock the vertex buffer
this.vertexBuffer.unlock();
}
/**
* Copies data for specified semantic into vertex buffer. Works with both interleaved (slower)
* and non-interleaved (fast) vertex buffers.
*
* @param {string} semantic - The semantic of the vertex element to set.
* @param {number[]|ArrayBufferView} data - The data to set.
* @param {number} numVertices - The number of vertices to write.
* @ignore
*/ writeData(semantic, data, numVertices) {
const element = this.element[semantic];
if (element) {
if (numVertices > this.vertexBuffer.numVertices) {
Debug.error(`NumVertices provided to setData: ${numVertices} is larger than space in VertexBuffer: ${this.vertexBuffer.numVertices}`);
// avoid overwrite
numVertices = this.vertexBuffer.numVertices;
}
const numComponents = element.numComponents;
// copy data to interleaved buffer by looping over vertices and copying them manually
if (this.vertexBuffer.getFormat().interleaved) {
let index = 0;
for(let i = 0; i < numVertices; i++){
element.setFromArray(index, data, i * numComponents);
index += element.stride;
}
} else {
// if data contains more data than needed, copy from its subarray
if (data.length > numVertices * numComponents) {
const copyCount = numVertices * numComponents;
// if data is typed array
if (ArrayBuffer.isView(data)) {
data = data.subarray(0, copyCount);
element.array.set(data);
} else {
// data is array, copy right amount manually
for(let i = 0; i < copyCount; i++){
element.array[i] = data[i];
}
}
} else {
// copy whole data
element.array.set(data);
}
}
}
}
/**
* Function to extract elements of a specified semantic from vertex buffer into flat array
* (data). Works with both interleaved (slower) and non-interleaved (fast) vertex buffers.
* Returns number of vertices. Note: when data is a typed array and is smaller than needed,
* only part of the data gets copied out (typed arrays ignore read/write out of range).
*
* @param {string} semantic - The semantic of the vertex element to read.
* @param {number[]|ArrayBufferView} data - The array to receive the data.
* @returns {number} The number of vertices read.
* @ignore
*/ readData(semantic, data) {
const element = this.element[semantic];
let count = 0;
if (element) {
count = this.vertexBuffer.numVertices;
let i;
const numComponents = element.numComponents;
if (this.vertexBuffer.getFormat().interleaved) {
// extract data from interleaved buffer by looping over vertices and copying them manually
if (Array.isArray(data)) {
data.length = 0;
}
element.index = 0;
let offset = 0;
for(i = 0; i < count; i++){
element.getToArray(offset, data, i * numComponents);
offset += element.stride;
}
} else {
if (ArrayBuffer.isView(data)) {
// destination data is typed array
data.set(element.array);
} else {
// destination data is array
data.length = 0;
const copyCount = count * numComponents;
for(i = 0; i < copyCount; i++){
data[i] = element.array[i];
}
}
}
}
return count;
}
}
export { VertexIterator };