UNPKG

@thewtex/vtk.js-esm

Version:

Visualization Toolkit for the Web

499 lines (389 loc) 23.4 kB
import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray'; import macro from '../../macro.js'; import vtkWebGPUFullScreenQuad from './FullScreenQuad.js'; import vtkWebGPUUniformBuffer from './UniformBuffer.js'; import vtkWebGPUShaderCache from './ShaderCache.js'; import vtkWebGPUStorageBuffer from './StorageBuffer.js'; import vtkWebGPUSampler from './Sampler.js'; import { BlendMode } from '../Core/VolumeMapper/Constants.js'; import { i as identity, t as translate, j as transpose, g as invert, m as multiply, s as scale } from '../../vendor/gl-matrix/esm/mat4.js'; var volFragTemplate = "\n//VTK::Renderer::Dec\n\n//VTK::Mapper::Dec\n\n//VTK::TCoord::Dec\n\n//VTK::RenderEncoder::Dec\n\n//VTK::IOStructs::Dec\n\n// dummy for now till they support 3d textures\nfn getTextureValue(vTex: texture_3d<f32>, tpos: vec4<f32>, vNum: i32) -> f32\n{\n return textureSampleLevel(vTex, clampSampler, tpos.xyz, 0.0).a;\n // 512.0 * f32(vNum) + 20.0 * trunc(10.0 * tpos.z);\n}\n\nfn getGradient(vTex: texture_3d<f32>, tpos: vec4<f32>, vNum: i32, scalar: f32) -> vec4<f32>\n{\n var result: vec4<f32>;\n\n var tstep: vec4<f32> = volumeSSBO.values[vNum].tstep;\n result.x = getTextureValue(vTex, tpos + vec4<f32>(tstep.x, 0.0, 0.0, 1.0), vNum) - scalar;\n result.y = getTextureValue(vTex, tpos + vec4<f32>(0.0, tstep.y, 0.0, 1.0), vNum) - scalar;\n result.z = getTextureValue(vTex, tpos + vec4<f32>(0.0, 0.0, tstep.z, 1.0), vNum) - scalar;\n\n // divide by spacing\n result = result / volumeSSBO.values[vNum].spacing;\n\n var grad: f32 = length(result.xyz);\n\n // // rotate to View Coords\n // result.xyz =\n // result.x * vPlaneNormal0 +\n // result.y * vPlaneNormal2 +\n // result.z * vPlaneNormal4;\n\n if (grad > 0.0)\n {\n result = result * (1.0 / grad);\n }\n\n result.w = grad;\n\n return result;\n}\n\nfn processVolume(vTex: texture_3d<f32>, vNum: i32, cNum: i32, posSC: vec4<f32>, tfunRows: f32) -> vec4<f32>\n{\n var outColor: vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 0.0);\n\n // convert to tcoords and reject if outside the volume\n var tpos: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*posSC;\n // var tpos: vec4<f32> = posSC*0.003;\n if (tpos.x < 0.0 || tpos.y < 0.0 || tpos.z < 0.0 ||\n tpos.x > 1.0 || tpos.y > 1.0 || tpos.z > 1.0) { return outColor; }\n\n var scalar: f32 = getTextureValue(vTex, tpos, vNum);\n\n var coord: vec2<f32> =\n vec2<f32>(scalar * componentSSBO.values[cNum].cScale + componentSSBO.values[cNum].cShift,\n (0.5 + 2.0 * f32(vNum)) / tfunRows);\n var color: vec4<f32> = textureSampleLevel(tfunTexture, clampSampler, coord, 0.0);\n coord.x = scalar * componentSSBO.values[cNum].oScale + componentSSBO.values[cNum].oShift;\n\n var gofactor: f32 = 1.0;\n if (componentSSBO.values[cNum].gomin < 1.0)\n {\n var normal: vec4<f32> = getGradient(vTex, tpos, vNum, scalar);\n gofactor = clamp(normal.a*componentSSBO.values[cNum].goScale + componentSSBO.values[cNum].goShift,\n componentSSBO.values[cNum].gomin, componentSSBO.values[cNum].gomax);\n }\n var opacity: f32 = gofactor*textureSampleLevel(ofunTexture, clampSampler, coord, 0.0).r;\n outColor = vec4<f32>(color.rgb, opacity);\n\n //VTK::Volume::Process\n\n return outColor;\n}\n\nfn composite(rayLengthSC: f32, minPosSC: vec4<f32>, rayStepSC: vec4<f32>) -> vec4<f32>\n{\n // initial ray position is at the beginning\n var rayPosSC: vec4<f32> = minPosSC;\n\n // how many rows (tfuns) do we have in our tfunTexture\n var tfunRows: f32 = f32(textureDimensions(tfunTexture).y);\n\n var curDist: f32 = 0.0;\n var computedColor: vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 0.0);\n var sampleColor: vec4<f32>;\n loop\n {\n // for each volume, sample and accumulate color\n\n//VTK::Volume::Calls\n\n // increment position\n curDist = curDist + mapperUBO.SampleDistance;\n rayPosSC = rayPosSC + rayStepSC;\n\n // check if we have reached a terminating condition\n if (curDist > rayLengthSC) { break; }\n if (computedColor.a > 0.98) { break; }\n }\n return computedColor;\n}\n\n[[stage(fragment)]]\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n var output: fragmentOutput;\n\n var rayMax: f32 = textureSampleLevel(maxTexture, clampSampler, input.tcoordVS, 0.0).r;\n var rayMin: f32 = textureSampleLevel(minTexture, clampSampler, input.tcoordVS, 0.0).r;\n\n // discard empty rays\n if (rayMax <= rayMin) { discard; }\n else\n {\n var winDimsI32: vec2<i32> = textureDimensions(minTexture);\n var winDims: vec2<f32> = vec2<f32>(f32(winDimsI32.x), f32(winDimsI32.y));\n\n // compute start and end ray positions in view coordinates\n var minPosSC: vec4<f32> = rendererUBO.PCSCMatrix*vec4<f32>(2.0*input.fragPos.x/winDims.x - 1.0, 1.0 - 2.0 * input.fragPos.y/winDims.y, rayMin, 1.0);\n minPosSC = minPosSC * (1.0 / minPosSC.w);\n var maxPosSC: vec4<f32> = rendererUBO.PCSCMatrix*vec4<f32>(2.0*input.fragPos.x/winDims.x - 1.0, 1.0 - 2.0 * input.fragPos.y/winDims.y, rayMax, 1.0);\n maxPosSC = maxPosSC * (1.0 / maxPosSC.w);\n\n var rayLengthSC: f32 = distance(minPosSC.xyz, maxPosSC.xyz);\n var rayStepSC: vec4<f32> = (maxPosSC - minPosSC)*(mapperUBO.SampleDistance/rayLengthSC);\n rayStepSC.w = 0.0;\n\n //VTK::Volume::Loop\n\n // var computedColor: vec4<f32> = vec4<f32>(rayMin, rayMax, 0.0, min(100.0*(rayMax - rayMin), 1.0));\n // computedColor = vec4<f32>(rayLengthSC / 500.0, 1.0, 0.0, 1.0);\n // computedColor = vec4<f32>(maxPosSC.xyz*0.01, 1.0);\n\n //VTK::RenderEncoder::Impl\n }\n\n return output;\n}\n"; var tmpMat4 = new Float64Array(16); var tmp2Mat4 = new Float64Array(16); // ---------------------------------------------------------------------------- // vtkWebGPUVolumePassFSQ methods // ---------------------------------------------------------------------------- function vtkWebGPUVolumePassFSQ(publicAPI, model) { // Set our className model.classHierarchy.push('vtkWebGPUVolumePassFSQ'); publicAPI.replaceShaderPosition = function (hash, pipeline, vertexInput) { var vDesc = pipeline.getShaderDescription('vertex'); vDesc.addBuiltinOutput('vec4<f32>', '[[builtin(position)]] Position'); var code = vDesc.getCode(); code = vtkWebGPUShaderCache.substitute(code, '//VTK::Position::Impl', ['output.tcoordVS = vec2<f32>(vertexBC.x * 0.5 + 0.5, 1.0 - vertexBC.y * 0.5 - 0.5);', 'output.Position = vec4<f32>(vertexBC, 1.0);']).result; vDesc.setCode(code); var fDesc = pipeline.getShaderDescription('fragment'); fDesc.addBuiltinInput('vec4<f32>', '[[builtin(position)]] fragPos'); }; model.shaderReplacements.set('replaceShaderPosition', publicAPI.replaceShaderPosition); publicAPI.replaceShaderVolume = function (hash, pipeline, vertexInput) { var fDesc = pipeline.getShaderDescription('fragment'); var code = fDesc.getCode(); var calls = []; for (var i = 0; i < model.volumes.length; i++) { // todo pass rowPos calls.push(" sampleColor = processVolume(volTexture".concat(i, ", ").concat(i, ", ").concat(model.rowStarts[i], ", rayPosSC, tfunRows);")); calls.push(" computedColor = vec4<f32>(\n sampleColor.a * sampleColor.rgb * (1.0 - computedColor.a) + computedColor.rgb,\n (1.0 - computedColor.a)*sampleColor.a + computedColor.a);"); } code = vtkWebGPUShaderCache.substitute(code, '//VTK::Volume::Calls', calls).result; if (model.blendMode === BlendMode.COMPOSITE_BLEND) { code = vtkWebGPUShaderCache.substitute(code, '//VTK::Volume::Loop', ['var computedColor: vec4<f32> = composite(rayLengthSC, minPosSC, rayStepSC);']).result; } fDesc.setCode(code); }; model.shaderReplacements.set('replaceShaderVolume', publicAPI.replaceShaderVolume); publicAPI.updateLUTImage = function (device) { // depends on // - volumes array (length and values) - mtime // - tfun arrays - renderable/property mtime var mtime = publicAPI.getMTime(); for (var i = 0; i < model.volumes.length; i++) { var vol = model.volumes[i].getRenderable(); var image = vol.getMapper().getInputData(); mtime = Math.max(mtime, vol.getMTime(), image.getMTime()); } if (mtime < model.lutBuildTime.getMTime()) { return; } // first determine how large the image should be model.numRows = 0; model.rowStarts = []; for (var vidx = 0; vidx < model.volumes.length; vidx++) { model.rowStarts.push(model.numRows); var webgpuvol = model.volumes[vidx]; var actor = webgpuvol.getRenderable(); var volMapr = actor.getMapper(); var vprop = actor.getProperty(); var _image = volMapr.getInputData(); var scalars = _image.getPointData() && _image.getPointData().getScalars(); var numComp = scalars.getNumberOfComponents(); var iComps = vprop.getIndependentComponents(); var numIComps = iComps ? numComp : 1; model.numRows += numIComps; } // allocate the image array var colorArray = new Uint8Array(model.numRows * 2 * model.rowLength * 4); var opacityArray = new Float32Array(model.numRows * 2 * model.rowLength); var imgRow = 0; var tmpTable = new Float32Array(model.rowLength * 3); var rowLength = model.rowLength; for (var _vidx = 0; _vidx < model.volumes.length; _vidx++) { var _webgpuvol = model.volumes[_vidx]; var _actor = _webgpuvol.getRenderable(); var _volMapr = _actor.getMapper(); var _vprop = _actor.getProperty(); var _image2 = _volMapr.getInputData(); var _scalars = _image2.getPointData() && _image2.getPointData().getScalars(); var _numComp = _scalars.getNumberOfComponents(); var _iComps = _vprop.getIndependentComponents(); var _numIComps = _iComps ? _numComp : 1; for (var c = 0; c < _numIComps; ++c) { var cfun = _vprop.getRGBTransferFunction(c); var cRange = cfun.getRange(); cfun.getTable(cRange[0], cRange[1], rowLength, tmpTable, 1); var ioffset = imgRow * rowLength * 4; for (var _i = 0; _i < rowLength; ++_i) { colorArray[ioffset + _i * 4] = 255.0 * tmpTable[_i * 3]; colorArray[ioffset + _i * 4 + 1] = 255.0 * tmpTable[_i * 3 + 1]; colorArray[ioffset + _i * 4 + 2] = 255.0 * tmpTable[_i * 3 + 2]; colorArray[ioffset + _i * 4 + 3] = 255.0; for (var co = 0; co < 4; co++) { colorArray[ioffset + (rowLength + _i) * 4 + co] = colorArray[ioffset + _i * 4 + co]; } } var ofun = _vprop.getScalarOpacity(c); var opacityFactor = model.sampleDist / _vprop.getScalarOpacityUnitDistance(c); var oRange = ofun.getRange(); ofun.getTable(oRange[0], oRange[1], rowLength, tmpTable, 1); // adjust for sample distance etc ioffset = imgRow * rowLength; for (var _i2 = 0; _i2 < rowLength; ++_i2) { opacityArray[ioffset + _i2] = 1.0 - Math.pow(1.0 - tmpTable[_i2], opacityFactor); opacityArray[ioffset + _i2 + rowLength] = opacityArray[ioffset + _i2]; } imgRow += 2; } } { var treq = { nativeArray: colorArray, width: model.rowLength, height: model.numRows * 2, depth: 1, format: 'rgba8unorm' }; var newTex = device.getTextureManager().getTexture(treq); var tview = newTex.createView(); tview.setName('tfunTexture'); model.textureViews[2] = tview; } { var _treq = { nativeArray: opacityArray, width: model.rowLength, height: model.numRows * 2, depth: 1, format: 'r32float' }; var _newTex = device.getTextureManager().getTexture(_treq); var _tview = _newTex.createView(); _tview.setName('ofunTexture'); model.textureViews[3] = _tview; } model.lutBuildTime.modified(); }; publicAPI.updateSSBO = function (device) { // if any of // - color or opacity tfun ranges changed - volume Mtime // - any volume matrix changed - volume MTime // - stabilized center changed - ren.stabilizedMTime // - any volume's input data worldtoindex or dimensions changed - input's mtime // var mtime = Math.max(publicAPI.getMTime(), model.WebGPURenderer.getStabilizedTime()); for (var i = 0; i < model.volumes.length; i++) { var vol = model.volumes[i].getRenderable(); var image = vol.getMapper().getInputData(); mtime = Math.max(mtime, vol.getMTime(), image.getMTime()); } if (mtime < model.SSBO.getSendTime()) { return; } // create the volumeSBBO var center = model.WebGPURenderer.getStabilizedCenterByReference(); model.SSBO.clearData(); model.SSBO.setNumberOfInstances(model.volumes.length); // create SCTC matrices SC -> world -> model -> index -> tcoord // // when doing coord conversions from A to C recall // the order is mat4.mult(AtoC, BtoC, AtoB); // var marray = new Float64Array(model.volumes.length * 16); var tstepArray = new Float64Array(model.volumes.length * 4); var spacingArray = new Float64Array(model.volumes.length * 4); for (var vidx = 0; vidx < model.volumes.length; vidx++) { var webgpuvol = model.volumes[vidx]; var actor = webgpuvol.getRenderable(); var volMapr = actor.getMapper(); var _image3 = volMapr.getInputData(); identity(tmpMat4); translate(tmpMat4, tmpMat4, center); // tmpMat4 is now SC->World var _vol = model.volumes[vidx]; var mcwcmat = _vol.getRenderable().getMatrix(); transpose(tmp2Mat4, mcwcmat); invert(tmp2Mat4, tmp2Mat4); // tmp2Mat4 is now world to model multiply(tmpMat4, tmp2Mat4, tmpMat4); // tmp4Mat is now SC->Model // the method on the data is world to index but the volume is in // model coordinates so really in this context it is model to index var modelToIndex = _image3.getWorldToIndex(); transpose(tmp2Mat4, modelToIndex); multiply(tmpMat4, tmp2Mat4, tmpMat4); // tmpMat4 is now SC -> Index var dims = _image3.getDimensions(); identity(tmp2Mat4); scale(tmp2Mat4, tmp2Mat4, [1.0 / dims[0], 1.0 / dims[1], 1.0 / dims[2]]); multiply(tmpMat4, tmp2Mat4, tmpMat4); // tmpMat4 is now SC -> Tcoord for (var j = 0; j < 16; j++) { marray[vidx * 16 + j] = tmpMat4[j]; } tstepArray[vidx * 4] = 1.0 / dims[0]; tstepArray[vidx * 4 + 1] = 1.0 / dims[1]; tstepArray[vidx * 4 + 2] = 1.0 / dims[2]; tstepArray[vidx * 4 + 3] = 1.0; var spacing = _image3.getSpacing(); spacingArray[vidx * 4] = spacing[0]; spacingArray[vidx * 4 + 1] = spacing[1]; spacingArray[vidx * 4 + 2] = spacing[2]; spacingArray[vidx * 4 + 3] = 1.0; } model.SSBO.addEntry('SCTCMatrix', 'mat4x4<f32>'); model.SSBO.addEntry('tstep', 'vec4<f32>'); model.SSBO.addEntry('spacing', 'vec4<f32>'); model.SSBO.setAllInstancesFromArray('SCTCMatrix', marray); model.SSBO.setAllInstancesFromArray('tstep', tstepArray); model.SSBO.setAllInstancesFromArray('spacing', spacingArray); model.SSBO.send(device); // now create the componentSSBO model.componentSSBO.clearData(); model.componentSSBO.setNumberOfInstances(model.numRows); var cScaleArray = new Float64Array(model.numRows); var cShiftArray = new Float64Array(model.numRows); var oScaleArray = new Float64Array(model.numRows); var oShiftArray = new Float64Array(model.numRows); var gominArray = new Float64Array(model.numRows); var gomaxArray = new Float64Array(model.numRows); var goshiftArray = new Float64Array(model.numRows); var goscaleArray = new Float64Array(model.numRows); var rowIdx = 0; for (var _vidx2 = 0; _vidx2 < model.volumes.length; _vidx2++) { var _webgpuvol2 = model.volumes[_vidx2]; var _actor2 = _webgpuvol2.getRenderable(); var _volMapr2 = _actor2.getMapper(); var vprop = _actor2.getProperty(); var _image4 = _volMapr2.getInputData(); var scalars = _image4.getPointData() && _image4.getPointData().getScalars(); var numComp = scalars.getNumberOfComponents(); var iComps = vprop.getIndependentComponents(); // const numIComps = iComps ? numComp : 1; var volInfo = { scale: [255.0], offset: [0.0] }; // three levels of shift scale combined into one // for performance in the fragment shader for (var compIdx = 0; compIdx < numComp; compIdx++) { var target = iComps ? compIdx : 0; var sscale = volInfo.scale[compIdx]; var ofun = vprop.getScalarOpacity(target); var oRange = ofun.getRange(); var oscale = sscale / (oRange[1] - oRange[0]); var oshift = (volInfo.offset[compIdx] - oRange[0]) / (oRange[1] - oRange[0]); oShiftArray[rowIdx] = oshift; oScaleArray[rowIdx] = oscale; var cfun = vprop.getRGBTransferFunction(target); var cRange = cfun.getRange(); cShiftArray[rowIdx] = (volInfo.offset[compIdx] - cRange[0]) / (cRange[1] - cRange[0]); cScaleArray[rowIdx] = sscale / (cRange[1] - cRange[0]); // todo sscale for dependent should be based off of the A channel? // not target (which is 0 in that case) var useGO = vprop.getUseGradientOpacity(target); if (useGO) { var gomin = vprop.getGradientOpacityMinimumOpacity(target); var gomax = vprop.getGradientOpacityMaximumOpacity(target); gominArray[rowIdx] = gomin; gomaxArray[rowIdx] = gomax; var goRange = [vprop.getGradientOpacityMinimumValue(target), vprop.getGradientOpacityMaximumValue(target)]; goscaleArray[rowIdx] = sscale * (gomax - gomin) / (goRange[1] - goRange[0]); goshiftArray[rowIdx] = -goRange[0] * (gomax - gomin) / (goRange[1] - goRange[0]) + gomin; } else { gominArray[rowIdx] = 1.0; gomaxArray[rowIdx] = 1.0; goscaleArray[rowIdx] = 0.0; goshiftArray[rowIdx] = 1.0; } rowIdx++; } } model.componentSSBO.addEntry('cScale', 'f32'); model.componentSSBO.addEntry('cShift', 'f32'); model.componentSSBO.addEntry('oScale', 'f32'); model.componentSSBO.addEntry('oShift', 'f32'); model.componentSSBO.addEntry('goShift', 'f32'); model.componentSSBO.addEntry('goScale', 'f32'); model.componentSSBO.addEntry('gomin', 'f32'); model.componentSSBO.addEntry('gomax', 'f32'); model.componentSSBO.setAllInstancesFromArray('cScale', cScaleArray); model.componentSSBO.setAllInstancesFromArray('cShift', cShiftArray); model.componentSSBO.setAllInstancesFromArray('oScale', oScaleArray); model.componentSSBO.setAllInstancesFromArray('oShift', oShiftArray); model.componentSSBO.setAllInstancesFromArray('goScale', goscaleArray); model.componentSSBO.setAllInstancesFromArray('goShift', goshiftArray); model.componentSSBO.setAllInstancesFromArray('gomin', gominArray); model.componentSSBO.setAllInstancesFromArray('gomax', gomaxArray); model.componentSSBO.send(device); }; publicAPI.updateBuffers = function (device) { // compute the min step size var sampleDist = model.volumes[0].getRenderable().getMapper().getSampleDistance(); for (var i = 0; i < model.volumes.length; i++) { var vol = model.volumes[i]; var volMapr = vol.getRenderable().getMapper(); var sd = volMapr.getSampleDistance(); if (sd < sampleDist) { sampleDist = sd; } } if (model.sampleDist !== sampleDist) { model.sampleDist = sampleDist; model.UBO.setValue('SampleDistance', sampleDist); model.UBO.sendIfNeeded(device); } publicAPI.updateLUTImage(device); publicAPI.updateSSBO(device); // add in 3d volume textures for (var vidx = 0; vidx < model.volumes.length; vidx++) { var webgpuvol = model.volumes[vidx]; var actor = webgpuvol.getRenderable(); var _volMapr3 = actor.getMapper(); var image = _volMapr3.getInputData(); var treq = { imageData: image, source: image }; var newTex = device.getTextureManager().getTexture(treq); if (!model.textureViews[vidx + 4] || model.textureViews[vidx + 4].getTexture() !== newTex) { var tview = newTex.createView(); tview.setName("volTexture".concat(vidx)); model.textureViews[vidx + 4] = tview; } } }; publicAPI.computePipelineHash = function () { var blendMode = model.volumes[0].getRenderable().getMapper().getBlendMode(); model.blendMode = blendMode; model.pipelineHash = "volfsq".concat(model.volumes.length, "b").concat(model.blendMode); }; // marks modified when needed publicAPI.setVolumes = function (val) { if (!model.volumes || model.volumes.length !== val.length) { model.volumes = _toConsumableArray(val); publicAPI.modified(); return; } for (var i = 0; i < val.length; i++) { if (val[i] !== model.volumes[i]) { model.volumes = _toConsumableArray(val); publicAPI.modified(); return; } } }; var superclassBuild = publicAPI.build; publicAPI.build = function (renderEncoder, device) { publicAPI.computePipelineHash(); publicAPI.updateBuffers(device); if (!model.clampSampler) { model.clampSampler = vtkWebGPUSampler.newInstance(); model.clampSampler.setName('clampSampler'); model.clampSampler.create(device, { minFilter: 'linear', maxFilter: 'linear' }); } superclassBuild(renderEncoder, device); }; var superclassGetBindables = publicAPI.getBindables; publicAPI.getBindables = function () { var bindables = superclassGetBindables(); bindables.push(model.componentSSBO); bindables.push(model.clampSampler); return bindables; }; } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- var DEFAULT_VALUES = { volumes: null, rowLength: 1024 }; // ---------------------------------------------------------------------------- function extend(publicAPI, model) { var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; Object.assign(model, DEFAULT_VALUES, initialValues); // Inheritance vtkWebGPUFullScreenQuad.extend(publicAPI, model, initialValues); model.fragmentShaderTemplate = volFragTemplate; model.UBO = vtkWebGPUUniformBuffer.newInstance(); model.UBO.setName('mapperUBO'); model.UBO.addEntry('SampleDistance', 'f32'); model.SSBO = vtkWebGPUStorageBuffer.newInstance(); model.SSBO.setName('volumeSSBO'); model.componentSSBO = vtkWebGPUStorageBuffer.newInstance(); model.componentSSBO.setName('componentSSBO'); model.lutBuildTime = {}; macro.obj(model.lutBuildTime, { mtime: 0 }); // Object methods vtkWebGPUVolumePassFSQ(publicAPI, model); } // ---------------------------------------------------------------------------- var newInstance = macro.newInstance(extend, 'vtkWebGPUVolumePassFSQ'); // ---------------------------------------------------------------------------- var vtkWebGPUVolumePassFSQ$1 = { newInstance: newInstance, extend: extend }; export default vtkWebGPUVolumePassFSQ$1; export { extend, newInstance };