UNPKG

@kitware/vtk.js

Version:

Visualization Toolkit for the Web

425 lines (332 loc) 15.3 kB
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 };