@kitware/vtk.js
Version:
Visualization Toolkit for the Web
366 lines (335 loc) • 12.1 kB
JavaScript
import { n as newInstance$1, o as obj, e as setGet, c as macro, a as newTypedArray } from '../../macros2.js';
import { j as cross, l as normalize } from '../../Common/Core/Math/index.js';
import vtkDataArray from '../../Common/Core/DataArray.js';
import vtkWebGPUBuffer from './Buffer.js';
import vtkWebGPUIndexBuffer from './IndexBuffer.js';
import vtkWebGPUTypes from './Types.js';
import Constants from './BufferManager/Constants.js';
const {
BufferUsage
} = Constants;
const {
vtkErrorMacro
} = macro;
const {
VtkDataTypes
} = vtkDataArray;
// the webgpu constants all show up as undefined
/* eslint-disable no-undef */
// ----------------------------------------------------------------------------
// Static API
// ----------------------------------------------------------------------------
const STATIC = {};
function _getFormatForDataArray(dataArray) {
let format;
switch (dataArray.getDataType()) {
case VtkDataTypes.UNSIGNED_CHAR:
format = 'uint8';
break;
case VtkDataTypes.FLOAT:
format = 'float32';
break;
case VtkDataTypes.UNSIGNED_INT:
format = 'uint32';
break;
case VtkDataTypes.INT:
format = 'sint32';
break;
case VtkDataTypes.DOUBLE:
format = 'float32';
break;
case VtkDataTypes.UNSIGNED_SHORT:
format = 'uint16';
break;
case VtkDataTypes.SHORT:
format = 'sin16';
break;
default:
format = 'float32';
break;
}
switch (dataArray.getNumberOfComponents()) {
case 2:
format += 'x2';
break;
case 3:
// only 32bit types support x3
if (!format.includes('32')) {
vtkErrorMacro(`unsupported x3 type for ${format}`);
}
format += 'x3';
break;
case 4:
format += 'x4';
break;
}
return format;
}
function packArray(indexBuffer, inArrayData, numComp, outputType, options) {
const result = {};
const flatSize = indexBuffer.getFlatSize();
if (!flatSize) {
return result;
}
// setup shift and scale
let shift = [0.0, 0.0, 0.0, 0.0];
if (options.shift) {
if (options.shift.length) {
shift = options.shift;
} else {
shift.fill(options.shift);
}
}
let scale = [1.0, 1.0, 1.0, 1.0];
if (options.scale) {
if (options.scale.length) {
scale = options.scale;
} else {
scale.fill(options.scale);
}
}
const packExtra = Object.prototype.hasOwnProperty.call(options, 'packExtra') ? options.packExtra : false;
let addAPoint;
let vboidx = 0;
const stride = numComp + (packExtra ? 1 : 0);
const packedVBO = newTypedArray(outputType, flatSize * stride);
// pick the right function based on point versus cell data
let flatIdMap = indexBuffer.getFlatIdToPointId();
if (options.cellData) {
flatIdMap = indexBuffer.getFlatIdToCellId();
}
// add data based on number of components
if (numComp === 1) {
addAPoint = function addAPointFunc(i) {
packedVBO[vboidx++] = scale[0] * inArrayData[i] + shift[0];
};
} else if (numComp === 2) {
addAPoint = function addAPointFunc(i) {
packedVBO[vboidx++] = scale[0] * inArrayData[i] + shift[0];
packedVBO[vboidx++] = scale[1] * inArrayData[i + 1] + shift[1];
};
} else if (numComp === 3 && !packExtra) {
addAPoint = function addAPointFunc(i) {
packedVBO[vboidx++] = scale[0] * inArrayData[i] + shift[0];
packedVBO[vboidx++] = scale[1] * inArrayData[i + 1] + shift[1];
packedVBO[vboidx++] = scale[2] * inArrayData[i + 2] + shift[2];
};
} else if (numComp === 3 && packExtra) {
addAPoint = function addAPointFunc(i) {
packedVBO[vboidx++] = scale[0] * inArrayData[i] + shift[0];
packedVBO[vboidx++] = scale[1] * inArrayData[i + 1] + shift[1];
packedVBO[vboidx++] = scale[2] * inArrayData[i + 2] + shift[2];
packedVBO[vboidx++] = scale[3] * 1.0 + shift[3];
};
} else if (numComp === 4) {
addAPoint = function addAPointFunc(i) {
packedVBO[vboidx++] = scale[0] * inArrayData[i] + shift[0];
packedVBO[vboidx++] = scale[1] * inArrayData[i + 1] + shift[1];
packedVBO[vboidx++] = scale[2] * inArrayData[i + 2] + shift[2];
packedVBO[vboidx++] = scale[3] * inArrayData[i + 3] + shift[3];
};
}
// for each entry in the flat array process it
for (let index = 0; index < flatSize; index++) {
const inArrayId = numComp * flatIdMap[index];
addAPoint(inArrayId);
}
result.nativeArray = packedVBO;
return result;
}
function getNormal(pointData, i0, i1, i2) {
const v1 = [pointData[i2 * 3] - pointData[i1 * 3], pointData[i2 * 3 + 1] - pointData[i1 * 3 + 1], pointData[i2 * 3 + 2] - pointData[i1 * 3 + 2]];
const v2 = [pointData[i0 * 3] - pointData[i1 * 3], pointData[i0 * 3 + 1] - pointData[i1 * 3 + 1], pointData[i0 * 3 + 2] - pointData[i1 * 3 + 2]];
const result = [];
cross(v1, v2, result);
normalize(result);
return result;
}
function generateNormals(cellArray, pointArray) {
const pointData = pointArray.getData();
const cellArrayData = cellArray.getData();
if (!cellArrayData || !pointData) {
return null;
}
// return a cellArray of normals
const packedVBO = new Int8Array(cellArray.getNumberOfCells() * 4);
const size = cellArrayData.length;
let vboidx = 0;
for (let index = 0; index < size;) {
const normal = getNormal(pointData, cellArrayData[index + 1], cellArrayData[index + 2], cellArrayData[index + 3]);
packedVBO[vboidx++] = 127 * normal[0];
packedVBO[vboidx++] = 127 * normal[1];
packedVBO[vboidx++] = 127 * normal[2];
packedVBO[vboidx++] = 127;
index += cellArrayData[index] + 1;
}
return packedVBO;
}
// ----------------------------------------------------------------------------
// vtkWebGPUBufferManager methods
// ----------------------------------------------------------------------------
function vtkWebGPUBufferManager(publicAPI, model) {
// Set our className
model.classHierarchy.push('vtkWebGPUBufferManager');
function _createBuffer(req) {
// if a dataArray is provided set the nativeArray
if (req.dataArray && !req.nativeArray) {
req.nativeArray = req.dataArray.getData();
}
let buffer;
let gpuUsage;
// handle index buffers
if (req.usage === BufferUsage.Index) {
// todo change to FlattenedIndex to be more clear
buffer = vtkWebGPUIndexBuffer.newInstance({
label: req.label
});
buffer.setDevice(model.device);
/* eslint-disable no-bitwise */
gpuUsage = GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST;
/* eslint-enable no-bitwise */
buffer.buildIndexBuffer(req);
buffer.createAndWrite(req.nativeArray, gpuUsage);
buffer.setArrayInformation([{
format: req.format
}]);
}
// create one if not done already
if (!buffer) {
buffer = vtkWebGPUBuffer.newInstance({
label: req.label
});
buffer.setDevice(model.device);
}
// handle uniform buffers
if (req.usage === BufferUsage.UniformArray) {
/* eslint-disable no-bitwise */
gpuUsage = GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST;
/* eslint-enable no-bitwise */
buffer.createAndWrite(req.nativeArray, gpuUsage);
}
// handle storage buffers
if (req.usage === BufferUsage.Storage) {
/* eslint-disable no-bitwise */
gpuUsage = GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST;
/* eslint-enable no-bitwise */
buffer.createAndWrite(req.nativeArray, gpuUsage);
}
// handle textures
if (req.usage === BufferUsage.Texture) {
/* eslint-disable no-bitwise */
gpuUsage = GPUBufferUsage.COPY_SRC;
/* eslint-enable no-bitwise */
buffer.createAndWrite(req.nativeArray, gpuUsage);
}
// all of the below types that have gpuUsage = VERTEX require format
// to be provided.
// handle point data
if (req.usage === BufferUsage.PointArray) {
gpuUsage = GPUBufferUsage.VERTEX;
const arrayType = vtkWebGPUTypes.getNativeTypeFromBufferFormat(req.format);
const result = packArray(req.indexBuffer, req.dataArray.getData(), req.dataArray.getNumberOfComponents(), arrayType, {
packExtra: req.packExtra,
shift: req.shift,
scale: req.scale,
cellData: req.cellData,
cellOffset: req.cellOffset
});
buffer.createAndWrite(result.nativeArray, gpuUsage);
buffer.setStrideInBytes(vtkWebGPUTypes.getByteStrideFromBufferFormat(req.format));
buffer.setArrayInformation([{
offset: 0,
format: req.format,
interpolation: req.cellData ? 'flat' : 'perspective'
}]);
}
// handle normals from points, snorm8x4
if (req.usage === BufferUsage.NormalsFromPoints) {
gpuUsage = GPUBufferUsage.VERTEX;
const arrayType = vtkWebGPUTypes.getNativeTypeFromBufferFormat(req.format);
const normals = generateNormals(req.cells, req.dataArray);
const result = packArray(req.indexBuffer, normals, 4, arrayType, {
cellData: true
});
buffer.createAndWrite(result.nativeArray, gpuUsage);
buffer.setStrideInBytes(vtkWebGPUTypes.getByteStrideFromBufferFormat(req.format));
buffer.setArrayInformation([{
offset: 0,
format: req.format,
interpolation: 'flat'
}]);
}
if (req.usage === BufferUsage.RawVertex) {
gpuUsage = GPUBufferUsage.VERTEX;
buffer.createAndWrite(req.nativeArray, gpuUsage);
buffer.setStrideInBytes(vtkWebGPUTypes.getByteStrideFromBufferFormat(req.format));
buffer.setArrayInformation([{
offset: 0,
format: req.format
}]);
}
buffer.setSourceTime(req.time);
return buffer;
}
// is the buffer already present?
publicAPI.hasBuffer = hash => model.device.hasCachedObject(hash);
publicAPI.getBuffer = req => {
// if we have a source the get/create/cache the buffer
if (req.hash) {
return model.device.getCachedObject(req.hash, _createBuffer, req);
}
return _createBuffer(req);
};
publicAPI.getBufferForPointArray = (dataArray, indexBuffer) => {
const format = _getFormatForDataArray(dataArray);
const buffRequest = {
hash: `${dataArray.getMTime()}I${indexBuffer.getMTime()}${format}`,
usage: BufferUsage.PointArray,
format,
dataArray,
indexBuffer
};
return publicAPI.getBuffer(buffRequest);
};
publicAPI.getFullScreenQuadBuffer = () => {
if (model.fullScreenQuadBuffer) {
return model.fullScreenQuadBuffer;
}
model.fullScreenQuadBuffer = vtkWebGPUBuffer.newInstance();
model.fullScreenQuadBuffer.setDevice(model.device);
// prettier-ignore
const array = new Float32Array([-1.0, -1.0, 0.0, 1.0, -1.0, 0.0, 1.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, 1.0, 0.0, -1.0, 1.0, 0.0]);
model.fullScreenQuadBuffer.createAndWrite(array, GPUBufferUsage.VERTEX);
model.fullScreenQuadBuffer.setStrideInBytes(12);
model.fullScreenQuadBuffer.setArrayInformation([{
offset: 0,
format: 'float32x3'
}]);
return model.fullScreenQuadBuffer;
};
}
// ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------
const DEFAULT_VALUES = {
device: null,
fullScreenQuadBuffer: null
};
// ----------------------------------------------------------------------------
function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);
// Object methods
obj(publicAPI, model);
setGet(publicAPI, model, ['device']);
vtkWebGPUBufferManager(publicAPI, model);
}
// ----------------------------------------------------------------------------
const newInstance = newInstance$1(extend);
// ----------------------------------------------------------------------------
var vtkWebGPUBufferManager$1 = {
newInstance,
extend,
...STATIC,
...Constants
};
export { STATIC, vtkWebGPUBufferManager$1 as default, extend, newInstance };