@cesium/engine
Version:
CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.
811 lines (768 loc) • 30.4 kB
JavaScript
import Check from "../../Core/Check.js";
import defined from "../../Core/defined.js";
import Cartesian2 from "../../Core/Cartesian2.js";
import Cartesian3 from "../../Core/Cartesian3.js";
import Cartesian4 from "../../Core/Cartesian4.js";
import DeveloperError from "../../Core/DeveloperError.js";
import ComponentDatatype from "../../Core/ComponentDatatype.js";
import AttributeCompression from "../../Core/AttributeCompression.js";
import IndexDatatype from "../../Core/IndexDatatype.js";
import PrimitiveType from "../../Core/PrimitiveType.js";
import Matrix4 from "../../Core/Matrix4.js";
import AttributeType from "../AttributeType.js";
/**
* A class for reading the data from a <code>ModelComponents.Attribute</code>.
*
* NOTE: Much of the functionality here already exists, scattered in many places.
* In most cases, the functionality is tailored for "one case" (like only handling
* positions, or only normals, or not considering quantization, or not handling
* interleaved buffers, ...). In many cases, the functionality is tailored for an
* 'accessor' (and often, the functions also expect the 'gltf' to be given).
* Most of what is done here (and in the existing functions) is pretty low-level
* and generic, though: The functions could often be fed with some (count, type,
* componentType), and there could be convenience functions that EITHER take these
* values from an 'accessor' OR from an 'attribute'. The tl;dr: Large parts of
* this could be "nicer", or "more generic", and "better" along all dimensions
* of this term. Just give me time...
*
* NOTE: The fact that all this has to operate on TypedArray is unfortunate.
* Most of the subsequent processing could operate on some abstraction of
* that. The fact that that TypedArrays can be read/written as "bulk", and
* then offer access that is "as efficient as it can be" could be a
* justification, as part of the performance-genericity trade-off
*
* NOTE: All this does not properly handle MATn types. There should be SOME
* abstraction for element- and component-wise access of the data. See
* https://github.com/javagl/JglTF/blob/84ce6d019fec3b75b6af1649bbe834005b2c620f/jgltf-model/src/main/java/de/javagl/jgltf/model/AbstractAccessorData.java#L149
*
* @private
*/
class ModelReader {
/**
* Reads the data of the given atttribute into a typed array.
*
* This will read the data into a compact, flat array with the data
* type corresponding to the data type of the attribute.
*
* If the attribute is contained in an interleaved buffer, marked as
* 'normalized', quantized, or oct-encoded, then it will be deinterleaved,
* normalization will be applied, it will be dequantized and oct-decoded
* as necessary.
*
* The result will be THE actual attribute data.
*
* @param {ModelComponents.Attribute} attribute The attribute
* @returns {TypedArray} The attribute data
*/
static readAttributeAsTypedArray(attribute) {
//>>includeStart('debug', pragmas.debug);
Check.defined("attribute", attribute);
//>>includeEnd('debug');
// Obtain a compact (non-interleaved) typed array that contains
// the components.
const compactTypedArray =
ModelReader.readAttributeAsRawCompactTypedArray(attribute);
// If the attribute is not normalized and the data is not quantized
// and not normalized, then this can be returned directly
const normalized = attribute.normalized;
const quantization = attribute.quantization;
if (!defined(quantization) && !normalized) {
return compactTypedArray;
}
const elementType = attribute.type;
const elementCount = attribute.count;
// If the attribute is normalized, normalize the data from
// the typed array
let normalizedTypedArray = compactTypedArray;
if (normalized) {
// Note that although this is called "dequantize", it does
// not really "dequantize" based on the quantization. It only
// performs the conversion from the (normalized) integer
// component types into floating point.
normalizedTypedArray = AttributeCompression.dequantize(
compactTypedArray,
attribute.componentDatatype,
elementType,
elementCount,
);
}
if (!defined(quantization)) {
return normalizedTypedArray;
}
// Now, this one actually DOES dequantize...
const dequantizedTypedArray = ModelReader.dequantize(
normalizedTypedArray,
elementCount,
elementType,
quantization,
);
return dequantizedTypedArray;
}
/**
* Read the data of the given attribute into a compact typed array.
*
* If the attribute is stored as interleaved data, then the result
* will be the deinterleaved data. If the data is quantized or
* normalized, then the resulting data will be the "raw" data,
* without applying normalization or dequantization.
*
* @param {ModelComponents.Attribute} attribute The attribute
* @returns {TypedArray} The raw attribute data
*/
static readAttributeAsRawCompactTypedArray(attribute) {
//>>includeStart('debug', pragmas.debug);
Check.defined("attribute", attribute);
//>>includeEnd('debug');
const elementType = attribute.type;
const elementCount = attribute.count;
const componentsPerElement =
AttributeType.getNumberOfComponents(elementType);
const totalComponentCount = elementCount * componentsPerElement;
// If the data is quantized, use the quantized component type
let componentType = attribute.componentDatatype;
const quantization = attribute.quantization;
if (defined(quantization)) {
componentType = quantization.componentDatatype;
}
const buffer = attribute.buffer;
// If the byte stride is the default (i.e. the total element size),
// then just fetch the whole buffer data into a typed array of the
// desired target type, and return it
const byteOffset = attribute.byteOffset;
const byteStride = attribute.byteStride;
const bytesPerComponent = ComponentDatatype.getSizeInBytes(componentType);
const defaultByteStride = componentsPerElement * bytesPerComponent;
if (!defined(byteStride) || byteStride === defaultByteStride) {
const typedArray = ComponentDatatype.createTypedArray(
componentType,
totalComponentCount,
);
buffer.getBufferData(typedArray, byteOffset);
return typedArray;
}
// Fetch the whole buffer in its raw form, to pick out the
// interleaved values.
// Note: When ALL attributes have to be fetched from an
// interleaved buffer, then this getBufferData call will
// be performed multiple times. It would be preferable to
// have ONE "TypedArray[] getThemFrom(buffer)" call that
// returns all of the (interleaved) attributes at once,
// but this requires abstractions that we don't have.
const fullTypedArray = new Uint8Array(buffer.sizeInBytes);
buffer.getBufferData(fullTypedArray);
// Read the components of each element, and write them into
// a typed array in a compact form
const compactTypedArray = ComponentDatatype.createTypedArray(
componentType,
totalComponentCount,
);
const elementByteStride = byteStride ?? defaultByteStride;
const dataView = new DataView(
fullTypedArray.buffer,
fullTypedArray.byteOffset,
fullTypedArray.byteLength,
);
const components = new Array(componentsPerElement);
const componentsReader = ModelReader.createComponentsReader(componentType);
for (let i = 0; i < elementCount; ++i) {
const elementByteOffset = byteOffset + i * elementByteStride;
componentsReader(
dataView,
elementByteOffset,
componentsPerElement,
components,
);
for (let j = 0; j < componentsPerElement; ++j) {
compactTypedArray[i * componentsPerElement + j] = components[j];
}
}
return compactTypedArray;
}
/**
* Dequantize the data from the given input array, based on the given
* quantization information, and return the result.
*
* This assumes that normalization has already been applied. This means that
* when the <code>quantization.normalized</code> flag is <code>true</code>,
* then the input is assumed to contain floating point values in the range
* [-1, 1].
*
* @param {TypedArray} quantizedTypedArray The quantized typed array
* @param {number} elementCount The number of elements
* @param {AttributeType} elementType The element type
* @param {ModelComponents.Quantization} quantization The quantization
* @returns {TypedArray} The result
* @throws DeveloperError When the element type is not SCALAR, VEC2,
* VEC3, or VEC4
*/
static dequantize(
quantizedTypedArray,
elementCount,
elementType,
quantization,
) {
//>>includeStart('debug', pragmas.debug);
Check.defined("quantizedTypedArray", quantizedTypedArray);
Check.typeOf.number.greaterThanOrEquals("elementCount", elementCount, 0);
Check.defined("elementType", elementType);
Check.defined("quantization", quantization);
//>>includeEnd('debug');
if (quantization.octEncoded) {
const dequantizedTypedArray = ModelReader.octDecode(
quantizedTypedArray,
elementCount,
quantization.normalizationRange,
undefined,
);
if (quantization.octEncodedZXY) {
ModelReader.convertZxyToXyz(
dequantizedTypedArray,
dequantizedTypedArray,
);
}
return dequantizedTypedArray;
}
// These could be generalized, if the offset/stepSize were not
// CartesianX objects, but arrays...
const stepSize = quantization.quantizedVolumeStepSize;
const offset = quantization.quantizedVolumeOffset;
if (elementType === AttributeType.SCALAR) {
return ModelReader.dequantize1D(
quantizedTypedArray,
elementCount,
stepSize,
offset,
undefined,
);
}
if (elementType === AttributeType.VEC2) {
return ModelReader.dequantize2D(
quantizedTypedArray,
elementCount,
stepSize,
offset,
undefined,
);
}
if (elementType === AttributeType.VEC3) {
return ModelReader.dequantize3D(
quantizedTypedArray,
elementCount,
stepSize,
offset,
undefined,
);
}
if (elementType === AttributeType.VEC4) {
return ModelReader.dequantize4D(
quantizedTypedArray,
elementCount,
stepSize,
offset,
undefined,
);
}
throw new DeveloperError(
`Element type for dequantization must be SCALAR, VEC2, VEC3, or VEC4, but is ${elementType}`,
);
}
/**
* Decode oct-encoded normals from the given input, and write the
* result into the given output, allocating and returning a new
* array if the result was undefined.
*
* This will apply the <code>AttributeCompression.octDecodeInRange</code>
* function to each three components of the input.
*
* @param {TypedArray} quantizedTypedArray The input
* @param {number} elementCount The number of elements
* @param {number} normalizationRange The normalization range
* @param {TypedArray} [dequantizedTypedArray] The result
* @returns {TypedArray} The result
*/
static octDecode(
quantizedTypedArray,
elementCount,
normalizationRange,
dequantizedTypedArray,
) {
//>>includeStart('debug', pragmas.debug);
Check.defined("quantizedTypedArray", quantizedTypedArray);
Check.typeOf.number.greaterThanOrEquals("elementCount", elementCount, 0);
Check.typeOf.number.greaterThan(
"normalizationRange",
normalizationRange,
0,
);
//>>includeEnd('debug');
if (!defined(dequantizedTypedArray)) {
dequantizedTypedArray = new Float32Array(quantizedTypedArray.length);
}
const c = new Cartesian3();
for (let i = 0; i < elementCount; i++) {
Cartesian3.unpack(quantizedTypedArray, i * 3, c);
AttributeCompression.octDecodeInRange(c, normalizationRange, c);
Cartesian3.pack(dequantizedTypedArray, c, i * 3);
}
return dequantizedTypedArray;
}
/**
* Swizzle all three consecutive elements in the given input array
* from (z, x, y) to (x, y, z), and write the result into the
* given output array, creating a new array if the given output
* array was undefined.
*
* @param {TypedArray} input The input
* @param {number} elementCount The number of elements
* @param {TypedArray} [output] The result
* @returns {TypedArray} The result
*/
static convertZxyToXyz(input, elementCount, output) {
//>>includeStart('debug', pragmas.debug);
Check.defined("input", input);
Check.typeOf.number.greaterThanOrEquals("elementCount", elementCount, 0);
//>>includeEnd('debug');
if (!defined(output)) {
output = new Float32Array(input.length);
}
let offset = 0;
for (let i = 0; i < elementCount; i++, offset += 3) {
const z = input[offset + 0];
const x = input[offset + 1];
const y = input[offset + 2];
output[offset + 0] = x;
output[offset + 1] = y;
output[offset + 2] = z;
}
return output;
}
/**
* Dequantize the given quantized array, based on the given quantization
* information, and write the result into the given output array, creating
* the output array if it was undefined.
*
* This will simply fill the output array with
* <code>output[i] = input[i] * stepSize + offset</code>
*
* @param {TypedArray} quantizedTypedArray The quantized array
* @param {number} elementCount The number of elements
* @param {number} stepSize The quantization step size
* @param {number} offset The quantization offset
* @param {TypedArray} [dequantizedTypedArray] The result
* @returns {TypedArray} The result
*/
static dequantize1D(
quantizedTypedArray,
elementCount,
stepSize,
offset,
dequantizedTypedArray,
) {
//>>includeStart('debug', pragmas.debug);
Check.defined("quantizedTypedArray", quantizedTypedArray);
Check.typeOf.number.greaterThanOrEquals("elementCount", elementCount, 0);
Check.defined("stepSize", stepSize);
Check.defined("offset", offset);
//>>includeEnd('debug');
if (!defined(dequantizedTypedArray)) {
dequantizedTypedArray = new Float32Array(quantizedTypedArray.length);
}
for (let i = 0; i < elementCount; i++) {
const q = quantizedTypedArray[i];
const d = q * stepSize + offset;
dequantizedTypedArray[i] = d;
}
return dequantizedTypedArray;
}
/**
* Dequantize the given quantized array, based on the given quantization
* information, and write the result into the given output array, creating
* the output array if it was undefined.
*
* This will simply fill the output array with
* <code>output[i] = input[i] * stepSize + offset</code>
* when interpreting the input and output as arrays of Cartesian2.
*
* @param {TypedArray} quantizedTypedArray The quantized array
* @param {number} elementCount The number of elements
* @param {Cartesian2} stepSize The quantization step size
* @param {Cartesian2} offset The quantization offset
* @param {TypedArray} [dequantizedTypedArray] The result
* @returns {TypedArray} The result
*/
static dequantize2D(
quantizedTypedArray,
elementCount,
stepSize,
offset,
dequantizedTypedArray,
) {
//>>includeStart('debug', pragmas.debug);
Check.defined("quantizedTypedArray", quantizedTypedArray);
Check.typeOf.number.greaterThanOrEquals("elementCount", elementCount, 0);
Check.defined("stepSize", stepSize);
Check.defined("offset", offset);
//>>includeEnd('debug');
if (!defined(dequantizedTypedArray)) {
dequantizedTypedArray = new Float32Array(quantizedTypedArray.length);
}
const c = new Cartesian2();
for (let i = 0; i < elementCount; i++) {
Cartesian2.unpack(quantizedTypedArray, i * 2, c);
Cartesian2.multiplyComponents(c, stepSize, c);
Cartesian2.add(c, offset, c);
Cartesian2.pack(c, dequantizedTypedArray, i * 2);
}
return dequantizedTypedArray;
}
/**
* Dequantize the given quantized array, based on the given quantization
* information, and write the result into the given output array, creating
* the output array if it was undefined.
*
* This will simply fill the output array with
* <code>output[i] = input[i] * stepSize + offset</code>
* when interpreting the input and output as arrays of Cartesian3.
*
* @param {TypedArray} quantizedTypedArray The quantized array
* @param {number} elementCount The number of elements
* @param {Cartesian3} stepSize The quantization step size
* @param {Cartesian3} offset The quantization offset
* @param {TypedArray} [dequantizedTypedArray] The result
* @returns {TypedArray} The result
*/
static dequantize3D(
quantizedTypedArray,
elementCount,
stepSize,
offset,
dequantizedTypedArray,
) {
//>>includeStart('debug', pragmas.debug);
Check.defined("quantizedTypedArray", quantizedTypedArray);
Check.typeOf.number.greaterThanOrEquals("elementCount", elementCount, 0);
Check.defined("stepSize", stepSize);
Check.defined("offset", offset);
//>>includeEnd('debug');
if (!defined(dequantizedTypedArray)) {
dequantizedTypedArray = new Float32Array(quantizedTypedArray.length);
}
const c = new Cartesian3();
for (let i = 0; i < elementCount; i++) {
Cartesian3.unpack(quantizedTypedArray, i * 3, c);
Cartesian3.multiplyComponents(c, stepSize, c);
Cartesian3.add(c, offset, c);
Cartesian3.pack(c, dequantizedTypedArray, i * 3);
}
return dequantizedTypedArray;
}
/**
* Dequantize the given quantized array, based on the given quantization
* information, and write the result into the given output array, creating
* the output array if it was undefined.
*
* This will simply fill the output array with
* <code>output[i] = input[i] * stepSize + offset</code>
* when interpreting the input and output as arrays of Cartesian4.
*
* @param {TypedArray} quantizedTypedArray The quantized array
* @param {number} elementCount The number of elements
* @param {Cartesian4} stepSize The quantization step size
* @param {Cartesian4} offset The quantization offset
* @param {TypedArray} [dequantizedTypedArray] The result
* @returns {TypedArray} The result
*/
static dequantize4D(
quantizedTypedArray,
elementCount,
stepSize,
offset,
dequantizedTypedArray,
) {
//>>includeStart('debug', pragmas.debug);
Check.defined("quantizedTypedArray", quantizedTypedArray);
Check.typeOf.number.greaterThanOrEquals("elementCount", elementCount, 0);
Check.defined("stepSize", stepSize);
Check.defined("offset", offset);
//>>includeEnd('debug');
if (!defined(dequantizedTypedArray)) {
dequantizedTypedArray = new Float32Array(quantizedTypedArray.length);
}
const c = new Cartesian4();
for (let i = 0; i < elementCount; i++) {
Cartesian4.unpack(quantizedTypedArray, i * 4, c);
Cartesian4.multiplyComponents(c, stepSize, c);
Cartesian4.add(c, offset, c);
Cartesian4.pack(c, dequantizedTypedArray, i * 4);
}
return dequantizedTypedArray;
}
/**
* Reads and returns a value with the given type
* at the given byte offset from the data view, in little-endian
* order
* @callback ComponentsReaderCallback
* @param {DataView} dataView Typed data view into a binary buffer
* @param {number} byteOffset The offset, in bytes, from the start of the view to read the data from
* @param {number} numberOfComponents The number of components to read
* @param {number[]} result The array in which to read the result
*/
/**
* Creates a function that reads the specified number of components with
* the given type from the given data view, in little-endian
* order, and writes them into a given result array.
*
* @param {ComponentDatatype} componentType The component type
* @returns {ComponentsReaderCallback} The reader
*/
static createComponentsReader(componentType) {
const componentReader = ModelReader.createComponentReader(componentType);
const sizeInBytes = ComponentDatatype.getSizeInBytes(componentType);
return function (dataView, byteOffset, numberOfComponents, result) {
let offset = byteOffset;
for (let i = 0; i < numberOfComponents; ++i) {
result[i] = componentReader(dataView, offset);
offset += sizeInBytes;
}
};
}
/**
* Reads and returns a value with the given type
* at the given byte offset from the data view, in little-endian
* order
* @callback ComponentReaderCallback
* @param {DataView} dataView Typed data view into a binary buffer
* @param {number} byteOffset The offset, in bytes, from the start of the view to read the data from
* @returns {number|BigInt} The value read from the dataView
*/
/**
* Creates a function that reads and returns a value with the given type
* at the given byte offset from the data view, in little-endian
* order
* @param {ComponentDatatype} componentType The component type
* @returns {ComponentReaderCallback} The reader
*/
static createComponentReader(componentType) {
switch (componentType) {
case ComponentDatatype.BYTE:
return function (dataView, byteOffset) {
return dataView.getInt8(byteOffset);
};
case ComponentDatatype.UNSIGNED_BYTE:
return function (dataView, byteOffset) {
return dataView.getUint8(byteOffset);
};
case ComponentDatatype.SHORT:
return function (dataView, byteOffset) {
return dataView.getInt16(byteOffset, true);
};
case ComponentDatatype.UNSIGNED_SHORT:
return function (dataView, byteOffset) {
return dataView.getUint16(byteOffset, true);
};
case ComponentDatatype.INT:
return function (dataView, byteOffset) {
return dataView.getInt32(byteOffset, true);
};
case ComponentDatatype.UNSIGNED_INT:
return function (dataView, byteOffset) {
return dataView.getUint32(byteOffset, true);
};
case ComponentDatatype.FLOAT:
return function (dataView, byteOffset) {
return dataView.getFloat32(byteOffset, true);
};
case ComponentDatatype.DOUBLE:
return function (dataView, byteOffset) {
return dataView.getFloat64(byteOffset, true);
};
}
throw new DeveloperError(
`The componentType must be a valid ComponentDatatype, but is ${componentType}`,
);
}
/**
* Transform the elements of the given array with the given 4x4 matrix,
* interpreting each 3 consecutive elements as a 3D point, and write
* the result into the given result array, creating the result array
* if it was undefined.
*
* @param {TypedArray} input The input array
* @param {Matrix4} matrix The matrix
* @param {TypedArray} [result] The result
* @returns {TypedArray} The result
*/
static transform3D(input, matrix, result) {
//>>includeStart('debug', pragmas.debug);
Check.defined("input", input);
Check.defined("matrix", matrix);
//>>includeEnd('debug');
if (!defined(result)) {
result = new Float32Array(input.length);
}
const c = new Cartesian3();
const elementCount = input.length / 3;
for (let i = 0; i < elementCount; i++) {
Cartesian3.unpack(input, i * 3, c);
Matrix4.multiplyByPoint(matrix, c, c);
Cartesian3.pack(c, result, i * 3);
}
return result;
}
/**
* Read the indices values from the given primitive indices, and
* return them as a typed array.
*
* If the given object already has a <code>typedArray/code> property, then it
* is assumed that this contains the proper indices, and they are returned.
*
* Otherwise, this reads the data from the <code>buffer</code> of the given
* primitive indices object, into a typed array with a type that matches the
* <code>indexDataType</code>, and returns it.
*
* Clients may not modify the returned typed array.
*
* @param {ModelComponents.Indices} primitiveIndices The primitive indices
* @returns {TypedArray} The indices values
* @throws {DeveloperError} If the <code>indexDataType</code> of the given
* object is neither <code>UNSIGNED_BYTE</code>, nor <code>UNSIGNED_SHORT</code>,
* nor <code>UNSIGNED_INT</code>
*/
static readIndicesAsTypedArray(primitiveIndices) {
const existingIndices = primitiveIndices.typedArray;
if (defined(existingIndices)) {
return existingIndices;
}
const indicesBuffer = primitiveIndices.buffer;
const indicesCount = primitiveIndices.count;
const indexDatatype = primitiveIndices.indexDatatype;
const indices = ModelReader.createIndexTypedArray(
indexDatatype,
indicesCount,
);
indicesBuffer.getBufferData(indices);
return indices;
}
/**
* Read the indices values from the given primitive indices object, and return
* them as a typed array of triangle vertex indices.
*
* If the given primitive type is <code>TRIANGLES</code>, then the indices
* values will be read from the given object, and returned.
*
* If the primitive type is <code>TRIANGLE_STRIP</code> or <code>TRIANGLE_FAN</code>,
* then the original indices values, will be read, converted into triangle indices
* (i.e. their equivalent <code>TRIANGLES</code> representation), and the result
* will be returned.
*
* The type of the returned array will match the <code>indexDataType</code>
* of the given object.
*
* Clients may not modify the returned typed array.
*
* @param {ModelComponents.Indices} primitiveIndices The primitive indices
* @returns {TypedArray} The indices, converted to triangle indices if necessary
* @throws {DeveloperError} If the <code>indexDataType</code> of the given
* object is neither <code>UNSIGNED_BYTE</code>, nor <code>UNSIGNED_SHORT</code>,
* nor <code>UNSIGNED_INT</code>, or the given <code>primitiveType</code>
* is neither <code>TRIANGLES</code>, nor <code>TRIANGLE_STRIP</code>,
* nor <code>TRIANGLE_FAN</code>
*/
static readIndicesAsTriangleIndicesTypedArray(
primitiveIndices,
primitiveType,
) {
const originalIndices =
ModelReader.readIndicesAsTypedArray(primitiveIndices);
if (primitiveType === PrimitiveType.TRIANGLES) {
return originalIndices;
}
if (primitiveType === PrimitiveType.TRIANGLE_STRIP) {
const triangleIndices =
ModelReader.convertTriangleStripToTriangleIndices(originalIndices);
return triangleIndices;
}
if (primitiveType === PrimitiveType.TRIANGLE_FAN) {
const triangleIndices =
ModelReader.convertTriangleFanToTriangleIndices(originalIndices);
return triangleIndices;
}
throw new DeveloperError(
`The primitiveType must be TRIANGLES (${PrimitiveType.TRIANGLES}, ` +
`TRIANGLE_STRIP (${PrimitiveType.TRIANGLE_STRIP}, or ` +
`TRIANGLE_FAN (${PrimitiveType.TRIANGLE_FAN}, but is ${primitiveType}`,
);
}
/**
* Converts the given indices from a <code>TRIANGLE_STRIP</code> representation
* into a <code>TRIANGLES</code> representation, and returns the result.
*
* The type of the result will be the same as the type of the input array.
*
* @param {TypedArray} indices The input indices
* @returns {TypedArray} The resulting triangle indices
*/
static convertTriangleStripToTriangleIndices(indices) {
const triangleIndices = indices.constructor((indices.length - 2) * 3);
for (let i = 0; i < indices.length - 2; i++) {
if (i % 2 === 1) {
triangleIndices[i * 3 + 0] = indices[i + 0];
triangleIndices[i * 3 + 1] = indices[i + 2];
triangleIndices[i * 3 + 2] = indices[i + 1];
} else {
triangleIndices[i * 3 + 0] = indices[i + 0];
triangleIndices[i * 3 + 1] = indices[i + 1];
triangleIndices[i * 3 + 2] = indices[i + 2];
}
}
return triangleIndices;
}
/**
* Converts the given indices from a <code>TRIANGLE_FAN</code> representation
* into a <code>TRIANGLES</code> representation, and returns the result.
*
* The type of the result will be the same as the type of the input array.
*
* @param {TypedArray} indices The input indices
* @returns {TypedArray} The resulting triangle indices
*/
static convertTriangleFanToTriangleIndices(indices) {
const triangleIndices = indices.constructor((indices.length - 2) * 3);
for (let i = 0; i < indices.length - 2; i++) {
triangleIndices[i * 3 + 0] = indices[i + 0];
triangleIndices[i * 3 + 1] = indices[i + 1];
triangleIndices[i * 3 + 2] = indices[i + 2];
}
return triangleIndices;
}
/**
* Create a typed array with a type that matches the given index data type,
* and the given size.
*
* @param {number} indexDatatype The <code>IndexDataType</code>
* @param {number} size The size of the array that will be created
* @returns {TypedArray} The typed array
* @throws {DeveloperError} If the <code>indexDataType</code> is neither
* <code>UNSIGNED_BYTE</code>, nor <code>UNSIGNED_SHORT</code>,
* nor <code>UNSIGNED_INT</code>, or the size is negative.
*/
static createIndexTypedArray(indexDatatype, size) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.number.greaterThanOrEquals("size", size, 0);
//>>includeEnd('debug');
switch (indexDatatype) {
case IndexDatatype.UNSIGNED_BYTE:
return new Uint8Array(size);
case IndexDatatype.UNSIGNED_SHORT:
return new Uint16Array(size);
case IndexDatatype.UNSIGNED_INT:
return new Uint32Array(size);
}
throw new DeveloperError(
`The indexDatatype must be UNSIGNED_BYTE (${IndexDatatype.UNSIGNED_BYTE}, ` +
`UNSIGNED_SHORT (${IndexDatatype.UNSIGNED_SHORT}, or ` +
`UNSIGNED_INT (${IndexDatatype.UNSIGNED_INT}, but is ${indexDatatype}`,
);
}
}
export default ModelReader;