@kitware/vtk.js
Version:
Visualization Toolkit for the Web
425 lines (332 loc) • 15.3 kB
JavaScript
import _asyncToGenerator from '@babel/runtime/helpers/asyncToGenerator';
import _regeneratorRuntime from '@babel/runtime/regenerator';
import macro from '../../macros.js';
import vtkHardwareSelector from '../Core/HardwareSelector.js';
import vtkWebGPUBuffer from './Buffer.js';
import vtkWebGPUHardwareSelectionPass from './HardwareSelectionPass.js';
import vtkSelectionNode from '../../Common/DataModel/SelectionNode.js';
import vtkDataSet from '../../Common/DataModel/DataSet.js';
var SelectionContent = vtkSelectionNode.SelectionContent,
SelectionField = vtkSelectionNode.SelectionField;
var FieldAssociations = vtkDataSet.FieldAssociations;
var vtkErrorMacro = macro.vtkErrorMacro;
function getInfoHash(info) {
return "".concat(info.propID, " ").concat(info.compositeID);
}
function convert(xx, yy, buffdata, channel) {
var offset = ((buffdata.height - yy - 1) * buffdata.colorBufferWidth + xx) * 4 + channel;
return buffdata.colorValues[offset];
}
function getPixelInformationWithData(buffdata, inDisplayPosition, maxDistance, outSelectedPosition) {
// Base case
var maxDist = maxDistance < 0 ? 0 : maxDistance;
if (maxDist === 0) {
outSelectedPosition[0] = inDisplayPosition[0];
outSelectedPosition[1] = inDisplayPosition[1];
if (inDisplayPosition[0] < 0 || inDisplayPosition[0] >= buffdata.width || inDisplayPosition[1] < 0 || inDisplayPosition[1] >= buffdata.height) {
return null;
}
var actorid = convert(inDisplayPosition[0], inDisplayPosition[1], buffdata, 0);
if (actorid <= 0) {
// the pixel did not hit any actor.
return null;
}
var _info = {};
_info.propID = actorid;
var compositeID = convert(inDisplayPosition[0], inDisplayPosition[1], buffdata, 1);
if (compositeID < 0 || compositeID > 0xffffff) {
compositeID = 0;
}
_info.compositeID = compositeID;
if (buffdata.captureZValues) {
var offset = (buffdata.height - inDisplayPosition[1] - 1) * buffdata.zbufferBufferWidth + inDisplayPosition[0];
_info.zValue = buffdata.depthValues[offset];
_info.zValue = buffdata.webGPURenderer.convertToOpenGLDepth(_info.zValue);
_info.displayPosition = inDisplayPosition;
}
return _info;
} // Iterate over successively growing boxes.
// They recursively call the base case to handle single pixels.
var dispPos = [inDisplayPosition[0], inDisplayPosition[1]];
var curPos = [0, 0];
var info = getPixelInformationWithData(buffdata, inDisplayPosition, 0, outSelectedPosition);
if (info) {
return info;
}
for (var dist = 1; dist < maxDist; ++dist) {
// Vertical sides of box.
for (var y = dispPos[1] > dist ? dispPos[1] - dist : 0; y <= dispPos[1] + dist; ++y) {
curPos[1] = y;
if (dispPos[0] >= dist) {
curPos[0] = dispPos[0] - dist;
info = getPixelInformationWithData(buffdata, curPos, 0, outSelectedPosition);
if (info) {
return info;
}
}
curPos[0] = dispPos[0] + dist;
info = getPixelInformationWithData(buffdata, curPos, 0, outSelectedPosition);
if (info) {
return info;
}
} // Horizontal sides of box.
for (var x = dispPos[0] >= dist ? dispPos[0] - (dist - 1) : 0; x <= dispPos[0] + (dist - 1); ++x) {
curPos[0] = x;
if (dispPos[1] >= dist) {
curPos[1] = dispPos[1] - dist;
info = getPixelInformationWithData(buffdata, curPos, 0, outSelectedPosition);
if (info) {
return info;
}
}
curPos[1] = dispPos[1] + dist;
info = getPixelInformationWithData(buffdata, curPos, 0, outSelectedPosition);
if (info) {
return info;
}
}
} // nothing hit.
outSelectedPosition[0] = inDisplayPosition[0];
outSelectedPosition[1] = inDisplayPosition[1];
return null;
} //-----------------------------------------------------------------------------
function convertSelection(fieldassociation, dataMap, buffdata) {
var sel = [];
var count = 0;
dataMap.forEach(function (value, key) {
var child = vtkSelectionNode.newInstance();
child.setContentType(SelectionContent.INDICES);
switch (fieldassociation) {
case FieldAssociations.FIELD_ASSOCIATION_CELLS:
child.setFieldType(SelectionField.CELL);
break;
case FieldAssociations.FIELD_ASSOCIATION_POINTS:
child.setFieldType(SelectionField.POINT);
break;
default:
vtkErrorMacro('Unknown field association');
}
child.getProperties().propID = value.info.propID;
var wprop = buffdata.webGPURenderer.getPropFromID(value.info.propID);
child.getProperties().prop = wprop.getRenderable();
child.getProperties().compositeID = value.info.compositeID;
child.getProperties().pixelCount = value.pixelCount;
if (buffdata.captureZValues) {
child.getProperties().displayPosition = [value.info.displayPosition[0], value.info.displayPosition[1], value.info.zValue];
child.getProperties().worldPosition = buffdata.webGPURenderWindow.displayToWorld(value.info.displayPosition[0], value.info.displayPosition[1], value.info.zValue, buffdata.renderer);
}
child.setSelectionList(value.attributeIDs);
sel[count] = child;
count++;
});
return sel;
} //----------------------------------------------------------------------------
function generateSelectionWithData(buffdata, fx1, fy1, fx2, fy2) {
var x1 = Math.floor(fx1);
var y1 = Math.floor(fy1);
var x2 = Math.floor(fx2);
var y2 = Math.floor(fy2);
var dataMap = new Map();
var outSelectedPosition = [0, 0];
for (var yy = y1; yy <= y2; yy++) {
for (var xx = x1; xx <= x2; xx++) {
var pos = [xx, yy];
var info = getPixelInformationWithData(buffdata, pos, 0, outSelectedPosition);
if (info) {
var hash = getInfoHash(info);
if (!dataMap.has(hash)) {
dataMap.set(hash, {
info: info,
pixelCount: 1,
attributeIDs: [info.attributeID]
});
} else {
var dmv = dataMap.get(hash);
dmv.pixelCount++;
if (buffdata.captureZValues) {
if (info.zValue < dmv.info.zValue) {
dmv.info = info;
}
}
if (dmv.attributeIDs.indexOf(info.attributeID) === -1) {
dmv.attributeIDs.push(info.attributeID);
}
}
}
}
}
return convertSelection(buffdata.fieldAssociation, dataMap, buffdata);
} // ----------------------------------------------------------------------------
// vtkWebGPUHardwareSelector methods
// ----------------------------------------------------------------------------
function vtkWebGPUHardwareSelector(publicAPI, model) {
// Set our className
model.classHierarchy.push('vtkWebGPUHardwareSelector'); //----------------------------------------------------------------------------
publicAPI.endSelection = function () {
model.WebGPURenderer.setSelector(null);
}; //----------------------------------------------------------------------------
// note we ignore the x,y arguments as WebGPU has to do buffer copies
// of the entire depth bufer. We could realloc hardware selection textures
// based on the passed in size etc but it gets messy so for now we always
// render the full size window and copy it to the buffers.
publicAPI.getSourceDataAsync = /*#__PURE__*/function () {
var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(renderer) {
var webGPURenderer, originalSuppress, device, texture, depthTexture, result, colorBuffer, cmdEnc, zbuffer, cLoad, zLoad;
return _regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
if (!(!renderer || !model._WebGPURenderWindow)) {
_context.next = 3;
break;
}
vtkErrorMacro('Renderer and view must be set before calling Select.');
return _context.abrupt("return", false);
case 3:
// todo revisit making selection part of core
// then we can do this in core
model._WebGPURenderWindow.getRenderable().preRender();
if (model._WebGPURenderWindow.getInitialized()) {
_context.next = 8;
break;
}
model._WebGPURenderWindow.initialize();
_context.next = 8;
return new Promise(function (resolve) {
model._WebGPURenderWindow.onInitialized(resolve);
});
case 8:
webGPURenderer = model._WebGPURenderWindow.getViewNodeFor(renderer);
if (webGPURenderer) {
_context.next = 11;
break;
}
return _context.abrupt("return", false);
case 11:
// Initialize renderer for selection.
// change the renderer's background to black, which will indicate a miss
originalSuppress = webGPURenderer.getSuppressClear();
webGPURenderer.setSuppressClear(true);
model._selectionPass.traverse(model._WebGPURenderWindow, webGPURenderer); // restore original background
webGPURenderer.setSuppressClear(originalSuppress);
device = model._WebGPURenderWindow.getDevice();
texture = model._selectionPass.getColorTexture();
depthTexture = model._selectionPass.getDepthTexture(); // as this is async we really don't want to store things in
// the class as multiple calls may start before resolving
// so anything specific to this request gets put into the
// result object (by value in most cases)
result = {
area: [0, 0, texture.getWidth() - 1, texture.getHeight() - 1],
captureZValues: model.captureZValues,
fieldAssociation: model.fieldAssociation,
renderer: renderer,
webGPURenderer: webGPURenderer,
webGPURenderWindow: model._WebGPURenderWindow,
width: texture.getWidth(),
height: texture.getHeight()
}; // must be a multiple of 256 bytes, so 16 texels with rgba32uint
result.colorBufferWidth = 16 * Math.floor((result.width + 15) / 16);
result.colorBufferSizeInBytes = result.colorBufferWidth * result.height * 4 * 4;
colorBuffer = vtkWebGPUBuffer.newInstance({
label: 'hardwareSelectColorBuffer'
});
colorBuffer.setDevice(device);
/* eslint-disable no-bitwise */
/* eslint-disable no-undef */
colorBuffer.create(result.colorBufferSizeInBytes, GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST);
/* eslint-enable no-bitwise */
/* eslint-enable no-undef */
cmdEnc = model._WebGPURenderWindow.getCommandEncoder();
cmdEnc.copyTextureToBuffer({
texture: texture.getHandle()
}, {
buffer: colorBuffer.getHandle(),
bytesPerRow: 16 * result.colorBufferWidth,
rowsPerImage: result.height
}, {
width: result.width,
height: result.height,
depthOrArrayLayers: 1
});
if (model.captureZValues) {
result.zbufferBufferWidth = 64 * Math.floor((result.width + 63) / 64);
zbuffer = vtkWebGPUBuffer.newInstance({
label: 'hardwareSelectDepthBuffer'
});
zbuffer.setDevice(device);
result.zbufferSizeInBytes = result.height * result.zbufferBufferWidth * 4;
/* eslint-disable no-bitwise */
/* eslint-disable no-undef */
zbuffer.create(result.zbufferSizeInBytes, GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST);
/* eslint-enable no-bitwise */
/* eslint-enable no-undef */
cmdEnc.copyTextureToBuffer({
texture: depthTexture.getHandle(),
aspect: 'depth-only'
}, {
buffer: zbuffer.getHandle(),
bytesPerRow: 4 * result.zbufferBufferWidth,
rowsPerImage: result.height
}, {
width: result.width,
height: result.height,
depthOrArrayLayers: 1
});
}
device.submitCommandEncoder(cmdEnc);
/* eslint-disable no-undef */
cLoad = colorBuffer.mapAsync(GPUMapMode.READ);
if (!model.captureZValues) {
_context.next = 37;
break;
}
zLoad = zbuffer.mapAsync(GPUMapMode.READ);
_context.next = 33;
return Promise.all([cLoad, zLoad]);
case 33:
result.depthValues = new Float32Array(zbuffer.getMappedRange().slice());
zbuffer.unmap();
_context.next = 39;
break;
case 37:
_context.next = 39;
return cLoad;
case 39:
/* eslint-enable no-undef */
result.colorValues = new Uint32Array(colorBuffer.getMappedRange().slice());
colorBuffer.unmap();
result.generateSelection = function (fx1, fy1, fx2, fy2) {
return generateSelectionWithData(result, fx1, fy1, fx2, fy2);
};
return _context.abrupt("return", result);
case 43:
case "end":
return _context.stop();
}
}
}, _callee);
}));
return function (_x) {
return _ref.apply(this, arguments);
};
}();
} // ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------
var DEFAULT_VALUES = {// WebGPURenderWindow: null,
}; // ----------------------------------------------------------------------------
function extend(publicAPI, model) {
var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
Object.assign(model, DEFAULT_VALUES, initialValues); // Build VTK API
vtkHardwareSelector.extend(publicAPI, model, initialValues);
model._selectionPass = vtkWebGPUHardwareSelectionPass.newInstance();
macro.setGet(publicAPI, model, ['_WebGPURenderWindow']);
macro.moveToProtected(publicAPI, model, ['WebGPURenderWindow']); // Object methods
vtkWebGPUHardwareSelector(publicAPI, model);
} // ----------------------------------------------------------------------------
var newInstance = macro.newInstance(extend, 'vtkWebGPUHardwareSelector'); // ----------------------------------------------------------------------------
var vtkWebGPUHardwareSelector$1 = {
newInstance: newInstance,
extend: extend
};
export { vtkWebGPUHardwareSelector$1 as default, extend, newInstance };