UNPKG

@kitware/vtk.js

Version:

Visualization Toolkit for the Web

997 lines (793 loc) 42.6 kB
import _slicedToArray from '@babel/runtime/helpers/slicedToArray'; import { newInstance as newInstance$1, obj, get, vtkErrorMacro as vtkErrorMacro$1 } from '../../macros.js'; import { mat4, vec3 } from 'gl-matrix'; import vtkClosedPolyLineToSurfaceFilter from '../../Filters/General/ClosedPolyLineToSurfaceFilter.js'; import vtkCubeSource from '../../Filters/Sources/CubeSource.js'; import vtkCutter from '../../Filters/Core/Cutter.js'; import vtkDataArray from '../../Common/Core/DataArray.js'; import vtkHelper from './Helper.js'; import { f as vtkMath } from '../../Common/Core/Math/index.js'; import vtkOpenGLTexture from './Texture.js'; import vtkPlane from '../../Common/DataModel/Plane.js'; import vtkPolyData from '../../Common/DataModel/PolyData.js'; import vtkReplacementShaderMapper from './ReplacementShaderMapper.js'; import vtkShaderProgram from './ShaderProgram.js'; import vtkViewNode from '../SceneGraph/ViewNode.js'; import { v as vtkImageResliceMapperVS } from './glsl/vtkImageResliceMapperVS.glsl.js'; import { v as vtkImageResliceMapperFS } from './glsl/vtkImageResliceMapperFS.glsl.js'; import InterpolationType from '../Core/ImageProperty/Constants.js'; import { VtkDataTypes } from '../../Common/Core/DataArray/Constants.js'; import { Filter } from './Texture/Constants.js'; import { Representation } from '../Core/Property/Constants.js'; import { registerOverride } from './ViewNodeFactory.js'; var vtkErrorMacro = vtkErrorMacro$1; // ---------------------------------------------------------------------------- // helper methods // ---------------------------------------------------------------------------- function computeFnToString(property, fn, numberOfComponents) { var pwfun = fn.apply(property); if (pwfun) { var iComps = property.getIndependentComponents(); return "".concat(property.getMTime(), "-").concat(iComps, "-").concat(numberOfComponents); } return '0'; } function safeMatrixMultiply(matrixArray, matrixType, tmpMat) { matrixType.identity(tmpMat); return matrixArray.reduce(function (res, matrix, index) { if (index === 0) { return matrix ? matrixType.copy(res, matrix) : matrixType.identity(res); } return matrix ? matrixType.multiply(res, res, matrix) : res; }, tmpMat); } // ---------------------------------------------------------------------------- // vtkOpenGLImageResliceMapper methods // ---------------------------------------------------------------------------- function vtkOpenGLImageResliceMapper(publicAPI, model) { // Set our className model.classHierarchy.push('vtkOpenGLImageResliceMapper'); publicAPI.buildPass = function (prepass) { if (prepass) { model.currentRenderPass = null; model._openGLImageSlice = publicAPI.getFirstAncestorOfType('vtkOpenGLImageSlice'); model._openGLRenderer = publicAPI.getFirstAncestorOfType('vtkOpenGLRenderer'); var ren = model._openGLRenderer.getRenderable(); model._openGLCamera = model._openGLRenderer.getViewNodeFor(ren.getActiveCamera()); model._openGLRenderWindow = model._openGLRenderer.getParent(); model.context = model._openGLRenderWindow.getContext(); model.tris.setOpenGLRenderWindow(model._openGLRenderWindow); if (!model.openGLTexture) { model.openGLTexture = vtkOpenGLTexture.newInstance(); } model.openGLTexture.setOpenGLRenderWindow(model._openGLRenderWindow); model.colorTexture.setOpenGLRenderWindow(model._openGLRenderWindow); model.pwfTexture.setOpenGLRenderWindow(model._openGLRenderWindow); } }; publicAPI.translucentPass = function (prepass, renderPass) { if (prepass) { model.currentRenderPass = renderPass; publicAPI.render(); } }; publicAPI.zBufferPass = function (prepass) { if (prepass) { model.haveSeenDepthRequest = true; model.renderDepth = true; publicAPI.render(); model.renderDepth = false; } }; publicAPI.opaqueZBufferPass = function (prepass) { return publicAPI.zBufferPass(prepass); }; publicAPI.opaquePass = function (prepass) { if (prepass) { publicAPI.render(); } }; publicAPI.getCoincidentParameters = function (ren, actor) { if (model.renderable.getResolveCoincidentTopology()) { return model.renderable.getCoincidentTopologyPolygonOffsetParameters(); } return null; }; // Renders myself publicAPI.render = function () { var actor = model._openGLImageSlice.getRenderable(); var ren = model._openGLRenderer.getRenderable(); publicAPI.renderPiece(ren, actor); }; publicAPI.renderPiece = function (ren, actor) { publicAPI.invokeEvent({ type: 'StartEvent' }); model.renderable.update(); model.currentInput = model.renderable.getInputData(); if (!model.currentInput) { vtkErrorMacro('No input!'); return; } publicAPI.updateResliceGeometry(); publicAPI.renderPieceStart(ren, actor); publicAPI.renderPieceDraw(ren, actor); publicAPI.renderPieceFinish(ren, actor); publicAPI.invokeEvent({ type: 'EndEvent' }); }; publicAPI.renderPieceStart = function (ren, actor) { // make sure the BOs are up to date publicAPI.updateBufferObjects(ren, actor); var iType = actor.getProperty().getInterpolationType(); if (iType === InterpolationType.NEAREST) { model.openGLTexture.setMinificationFilter(Filter.NEAREST); model.openGLTexture.setMagnificationFilter(Filter.NEAREST); model.colorTexture.setMinificationFilter(Filter.NEAREST); model.colorTexture.setMagnificationFilter(Filter.NEAREST); model.pwfTexture.setMinificationFilter(Filter.NEAREST); model.pwfTexture.setMagnificationFilter(Filter.NEAREST); } else { model.openGLTexture.setMinificationFilter(Filter.LINEAR); model.openGLTexture.setMagnificationFilter(Filter.LINEAR); model.colorTexture.setMinificationFilter(Filter.LINEAR); model.colorTexture.setMagnificationFilter(Filter.LINEAR); model.pwfTexture.setMinificationFilter(Filter.LINEAR); model.pwfTexture.setMagnificationFilter(Filter.LINEAR); } // No buffer objects bound. model.lastBoundBO = null; }; publicAPI.renderPieceDraw = function (ren, actor) { var gl = model.context; // render the texture model.openGLTexture.activate(); model.colorTexture.activate(); model.pwfTexture.activate(); // update shaders if required publicAPI.updateShaders(model.tris, ren, actor); // Finally draw gl.drawArrays(gl.TRIANGLES, 0, model.tris.getCABO().getElementCount()); model.tris.getVAO().release(); model.openGLTexture.deactivate(); model.colorTexture.deactivate(); model.pwfTexture.deactivate(); }; publicAPI.renderPieceFinish = function (ren, actor) {}; publicAPI.updateBufferObjects = function (ren, actor) { // Rebuild buffer objects if needed if (publicAPI.getNeedToRebuildBufferObjects(ren, actor)) { publicAPI.buildBufferObjects(ren, actor); } }; publicAPI.getNeedToRebuildBufferObjects = function (ren, actor) { return model.VBOBuildTime.getMTime() < publicAPI.getMTime() || model.VBOBuildTime.getMTime() < actor.getMTime() || model.VBOBuildTime.getMTime() < model.renderable.getMTime() || model.VBOBuildTime.getMTime() < actor.getProperty().getMTime() || model.VBOBuildTime.getMTime() < model.currentInput.getMTime() || model.VBOBuildTime.getMTime() < model.resliceGeom.getMTime(); }; publicAPI.buildBufferObjects = function (ren, actor) { var _image$getPointData; var image = model.currentInput; if (!image) { return; } var scalars = (_image$getPointData = image.getPointData()) === null || _image$getPointData === void 0 ? void 0 : _image$getPointData.getScalars(); if (!scalars) { return; } var numComp = scalars.getNumberOfComponents(); if (!model._externalOpenGLTexture) { var _toString = "".concat(image.getMTime(), "A").concat(scalars.getMTime()); if (model.openGLTextureString !== _toString) { // Build the image scalar texture var dims = image.getDimensions(); // Use norm16 for the 3D texture if the extension is available model.openGLTexture.getOglNorm16Ext(model.context.getExtension('EXT_texture_norm16')); model.openGLTexture.releaseGraphicsResources(model._openGLRenderWindow); model.openGLTexture.resetFormatAndType(); model.openGLTexture.create3DFilterableFromDataArray(dims[0], dims[1], dims[2], scalars); model.openGLTextureString = _toString; } } var ppty = actor.getProperty(); var iComps = ppty.getIndependentComponents(); var numIComps = iComps ? numComp : 1; var textureHeight = iComps ? 2 * numIComps : 1; var cfunToString = computeFnToString(ppty, ppty.getRGBTransferFunction, numIComps); if (model.colorTextureString !== cfunToString) { var cWidth = 1024; var cSize = cWidth * textureHeight * 3; var cTable = new Uint8Array(cSize); var cfun = ppty.getRGBTransferFunction(); if (cfun) { var tmpTable = new Float32Array(cWidth * 3); for (var c = 0; c < numIComps; c++) { cfun = ppty.getRGBTransferFunction(c); var cRange = cfun.getRange(); cfun.getTable(cRange[0], cRange[1], cWidth, tmpTable, 1); if (iComps) { for (var i = 0; i < cWidth * 3; i++) { cTable[c * cWidth * 6 + i] = 255.0 * tmpTable[i]; cTable[c * cWidth * 6 + i + cWidth * 3] = 255.0 * tmpTable[i]; } } else { for (var _i = 0; _i < cWidth * 3; _i++) { cTable[c * cWidth * 6 + _i] = 255.0 * tmpTable[_i]; } } } model.colorTexture.releaseGraphicsResources(model._openGLRenderWindow); model.colorTexture.resetFormatAndType(); model.colorTexture.create2DFromRaw(cWidth, textureHeight, 3, VtkDataTypes.UNSIGNED_CHAR, cTable); } else { for (var _i2 = 0; _i2 < cWidth * 3; ++_i2) { cTable[_i2] = 255.0 * _i2 / ((cWidth - 1) * 3); cTable[_i2 + 1] = 255.0 * _i2 / ((cWidth - 1) * 3); cTable[_i2 + 2] = 255.0 * _i2 / ((cWidth - 1) * 3); } model.colorTexture.create2DFromRaw(cWidth, 1, 3, VtkDataTypes.UNSIGNED_CHAR, cTable); } model.colorTextureString = cfunToString; } // Build piecewise function buffer. This buffer is used either // for component weighting or opacity, depending on whether we're // rendering components independently or not. var pwfunToString = computeFnToString(ppty, ppty.getPiecewiseFunction, numIComps); if (model.pwfTextureString !== pwfunToString) { var pwfWidth = 1024; var pwfSize = pwfWidth * textureHeight; var pwfTable = new Uint8Array(pwfSize); var pwfun = ppty.getPiecewiseFunction(); // support case where pwfun is added/removed model.pwfTexture.releaseGraphicsResources(model._openGLRenderWindow); model.pwfTexture.resetFormatAndType(); if (pwfun) { var pwfFloatTable = new Float32Array(pwfSize); var _tmpTable = new Float32Array(pwfWidth); for (var _c = 0; _c < numIComps; ++_c) { pwfun = ppty.getPiecewiseFunction(_c); if (pwfun === null) { // Piecewise constant max if no function supplied for this component pwfFloatTable.fill(1.0); } else { var pwfRange = pwfun.getRange(); pwfun.getTable(pwfRange[0], pwfRange[1], pwfWidth, _tmpTable, 1); // adjust for sample distance etc if (iComps) { for (var _i3 = 0; _i3 < pwfWidth; _i3++) { pwfFloatTable[_c * pwfWidth * 2 + _i3] = _tmpTable[_i3]; pwfFloatTable[_c * pwfWidth * 2 + _i3 + pwfWidth] = _tmpTable[_i3]; } } else { for (var _i4 = 0; _i4 < pwfWidth; _i4++) { pwfFloatTable[_c * pwfWidth * 2 + _i4] = _tmpTable[_i4]; } } } } model.pwfTexture.create2DFromRaw(pwfWidth, textureHeight, 1, VtkDataTypes.FLOAT, pwfFloatTable); } else { // default is opaque pwfTable.fill(255.0); model.pwfTexture.create2DFromRaw(pwfWidth, 1, 1, VtkDataTypes.UNSIGNED_CHAR, pwfTable); } model.pwfTextureString = pwfunToString; } var vboString = "".concat(model.resliceGeom.getMTime(), "A").concat(model.renderable.getSlabThickness()); if (!model.tris.getCABO().getElementCount() || model.VBOBuildString !== vboString) { var points = vtkDataArray.newInstance({ numberOfComponents: 3, values: model.resliceGeom.getPoints().getData() }); points.setName('points'); var cells = vtkDataArray.newInstance({ numberOfComponents: 1, values: model.resliceGeom.getPolys().getData() }); var options = { points: points, cellOffset: 0 }; if (model.renderable.getSlabThickness() > 0.0) { var n = model.resliceGeom.getPointData().getNormals(); if (!n) { vtkErrorMacro('Slab mode requested without normals'); } else { options.normals = n; } } model.tris.getCABO().createVBO(cells, 'polys', Representation.SURFACE, options); } model.VBOBuildString = vboString; model.VBOBuildTime.modified(); }; publicAPI.updateShaders = function (cellBO, ren, actor) { model.lastBoundBO = cellBO; // has something changed that would require us to recreate the shader? if (publicAPI.getNeedToRebuildShaders(cellBO, ren, actor)) { var shaders = { Vertex: null, Fragment: null, Geometry: null }; publicAPI.buildShaders(shaders, ren, actor); // compile and bind the program if needed var newShader = model._openGLRenderWindow.getShaderCache().readyShaderProgramArray(shaders.Vertex, shaders.Fragment, shaders.Geometry); // if the shader changed reinitialize the VAO if (newShader !== cellBO.getProgram()) { cellBO.setProgram(newShader); // reset the VAO as the shader has changed cellBO.getVAO().releaseGraphicsResources(); } cellBO.getShaderSourceTime().modified(); } else { model._openGLRenderWindow.getShaderCache().readyShaderProgram(cellBO.getProgram()); } cellBO.getVAO().bind(); publicAPI.setMapperShaderParameters(cellBO, ren, actor); publicAPI.setCameraShaderParameters(cellBO, ren, actor); publicAPI.setPropertyShaderParameters(cellBO, ren, actor); }; publicAPI.setMapperShaderParameters = function (cellBO, ren, actor) { var program = cellBO.getProgram(); if (cellBO.getCABO().getElementCount() && (model.VBOBuildTime.getMTime() > cellBO.getAttributeUpdateTime().getMTime() || cellBO.getShaderSourceTime().getMTime() > cellBO.getAttributeUpdateTime().getMTime())) { // Set the 3D texture if (program.isUniformUsed('texture1')) { program.setUniformi('texture1', model.openGLTexture.getTextureUnit()); } // Set the plane vertex attributes if (program.isAttributeUsed('vertexWC')) { if (!cellBO.getVAO().addAttributeArray(program, cellBO.getCABO(), 'vertexWC', cellBO.getCABO().getVertexOffset(), cellBO.getCABO().getStride(), model.context.FLOAT, 3, model.context.FALSE)) { vtkErrorMacro('Error setting vertexWC in shader VAO.'); } } // If we are doing slab mode, we need normals if (program.isAttributeUsed('normalWC')) { if (!cellBO.getVAO().addAttributeArray(program, cellBO.getCABO(), 'normalWC', cellBO.getCABO().getNormalOffset(), cellBO.getCABO().getStride(), model.context.FLOAT, 3, model.context.FALSE)) { vtkErrorMacro('Error setting normalWC in shader VAO.'); } } if (program.isUniformUsed('slabThickness')) { program.setUniformf('slabThickness', model.renderable.getSlabThickness()); } if (program.isUniformUsed('spacing')) { program.setUniform3fv('spacing', model.currentInput.getSpacing()); } if (program.isUniformUsed('slabType')) { program.setUniformi('slabType', model.renderable.getSlabType()); } if (program.isUniformUsed('slabType')) { program.setUniformi('slabType', model.renderable.getSlabType()); } if (program.isUniformUsed('slabTrapezoid')) { program.setUniformi('slabTrapezoid', model.renderable.getSlabTrapezoidIntegration()); } var shiftScaleEnabled = cellBO.getCABO().getCoordShiftAndScaleEnabled(); var inverseShiftScaleMatrix = shiftScaleEnabled ? cellBO.getCABO().getInverseShiftAndScaleMatrix() : null; // Set the world->texture matrix if (program.isUniformUsed('WCTCMatrix')) { var image = model.currentInput; mat4.identity(model.tmpMat4); var bounds = image.getBounds(); var sc = [bounds[1] - bounds[0], bounds[3] - bounds[2], bounds[5] - bounds[4]]; var o = [bounds[0], bounds[2], bounds[4]]; var q = [0, 0, 0, 1]; mat4.fromRotationTranslationScale(model.tmpMat4, q, o, sc); mat4.invert(model.tmpMat4, model.tmpMat4); if (inverseShiftScaleMatrix) { mat4.multiply(model.tmpMat4, model.tmpMat4, inverseShiftScaleMatrix); } program.setUniformMatrix('WCTCMatrix', model.tmpMat4); } cellBO.getAttributeUpdateTime().modified(); } // Depth request if (model.haveSeenDepthRequest) { cellBO.getProgram().setUniformi('depthRequest', model.renderDepth ? 1 : 0); } // handle coincident if (cellBO.getProgram().isUniformUsed('coffset')) { var cp = publicAPI.getCoincidentParameters(ren, actor); cellBO.getProgram().setUniformf('coffset', cp.offset); // cfactor isn't always used when coffset is. if (cellBO.getProgram().isUniformUsed('cfactor')) { cellBO.getProgram().setUniformf('cfactor', cp.factor); } } }; publicAPI.setCameraShaderParameters = function (cellBO, ren, actor) { // [WMVP]C == {world, model, view, projection} coordinates // e.g. WCPC == world to projection coordinate transformation var keyMats = model._openGLCamera.getKeyMatrices(ren); var actMats = model._openGLImageSlice.getKeyMatrices(); var shiftScaleEnabled = cellBO.getCABO().getCoordShiftAndScaleEnabled(); var inverseShiftScaleMatrix = shiftScaleEnabled ? cellBO.getCABO().getInverseShiftAndScaleMatrix() : null; var program = cellBO.getProgram(); if (program.isUniformUsed('MCPCMatrix')) { mat4.identity(model.tmpMat4); program.setUniformMatrix('MCPCMatrix', safeMatrixMultiply([keyMats.wcpc, actMats.mcwc, inverseShiftScaleMatrix], mat4, model.tmpMat4)); } if (program.isUniformUsed('MCVCMatrix')) { mat4.identity(model.tmpMat4); program.setUniformMatrix('MCVCMatrix', safeMatrixMultiply([keyMats.wcvc, actMats.mcwc, inverseShiftScaleMatrix], mat4, model.tmpMat4)); } }; publicAPI.setPropertyShaderParameters = function (cellBO, ren, actor) { var program = cellBO.getProgram(); var ppty = actor.getProperty(); var opacity = ppty.getOpacity(); program.setUniformf('opacity', opacity); // Component mix // Independent components: Mixed according to component weights // Dependent components: Mixed using the following logic: // - 2 comps => LA // - 3 comps => RGB + opacity from pwf // - 4 comps => RGBA var numComp = model.openGLTexture.getComponents(); var iComps = ppty.getIndependentComponents(); if (iComps) { for (var i = 0; i < numComp; ++i) { program.setUniformf("mix".concat(i), ppty.getComponentWeight(i)); } } // Color opacity map var volInfo = model.openGLTexture.getVolumeInfo(); // three levels of shift scale combined into one // for performance in the fragment shader for (var _i5 = 0; _i5 < numComp; _i5++) { var cw = ppty.getColorWindow(); var cl = ppty.getColorLevel(); var target = iComps ? _i5 : 0; var cfun = ppty.getRGBTransferFunction(target); if (cfun && ppty.getUseLookupTableScalarRange()) { var cRange = cfun.getRange(); cw = cRange[1] - cRange[0]; cl = 0.5 * (cRange[1] + cRange[0]); } var scale = volInfo.scale[_i5] / cw; var shift = (volInfo.offset[_i5] - cl) / cw + 0.5; program.setUniformf("cshift".concat(_i5), shift); program.setUniformf("cscale".concat(_i5), scale); } var texColorUnit = model.colorTexture.getTextureUnit(); program.setUniformi('colorTexture1', texColorUnit); // pwf shift/scale for (var _i6 = 0; _i6 < numComp; _i6++) { var pwfScale = 1.0; var pwfShift = 0.0; var _target = iComps ? _i6 : 0; var pwfun = ppty.getPiecewiseFunction(_target); if (pwfun) { var pwfRange = pwfun.getRange(); var length = pwfRange[1] - pwfRange[0]; var mid = 0.5 * (pwfRange[0] + pwfRange[1]); pwfScale = volInfo.scale[_i6] / length; pwfShift = (volInfo.offset[_i6] - mid) / length + 0.5; } program.setUniformf("pwfshift".concat(_i6), pwfShift); program.setUniformf("pwfscale".concat(_i6), pwfScale); } var texOpacityUnit = model.pwfTexture.getTextureUnit(); program.setUniformi('pwfTexture1', texOpacityUnit); // Background color program.setUniform4fv('backgroundColor', model.renderable.getBackgroundColor()); }; publicAPI.getNeedToRebuildShaders = function (cellBO, ren, actor) { // has something changed that would require us to recreate the shader? // candidates are // property modified (representation interpolation and lighting) // input modified // light complexity changed // render pass shader replacement changed var tNumComp = model.openGLTexture.getComponents(); var iComp = actor.getProperty().getIndependentComponents(); var slabTh = model.renderable.getSlabThickness(); var slabType = model.renderable.getSlabType(); var slabTrap = model.renderable.getSlabTrapezoidIntegration(); // has the render pass shader replacement changed? Two options var needRebuild = false; if (!model.currentRenderPass && model.lastRenderPassShaderReplacement || model.currentRenderPass && model.currentRenderPass.getShaderReplacement() !== model.lastRenderPassShaderReplacement) { needRebuild = true; } if (needRebuild || model.lastHaveSeenDepthRequest !== model.haveSeenDepthRequest || cellBO.getProgram() === 0 || model.lastTextureComponents !== tNumComp || model.lastIndependentComponents !== iComp || model.lastSlabThickness !== slabTh || model.lastSlabType !== slabType || model.lastSlabTrapezoidIntegration !== slabTrap) { model.lastHaveSeenDepthRequest = model.haveSeenDepthRequest; model.lastTextureComponents = tNumComp; model.lastIndependentComponents = iComp; model.lastSlabThickness = slabTh; model.lastSlabType = slabType; model.lastSlabTrapezoidIntegration = slabTrap; return true; } return false; }; publicAPI.getShaderTemplate = function (shaders, ren, actor) { shaders.Vertex = vtkImageResliceMapperVS; shaders.Fragment = vtkImageResliceMapperFS; shaders.Geometry = ''; }; publicAPI.replaceShaderValues = function (shaders, ren, actor) { publicAPI.replaceShaderTCoord(shaders, ren, actor); publicAPI.replaceShaderPositionVC(shaders, ren, actor); if (model.haveSeenDepthRequest) { var FSSource = shaders.Fragment; FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::ZBuffer::Dec', 'uniform int depthRequest;').result; FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::ZBuffer::Impl', ['if (depthRequest == 1) {', 'float iz = floor(gl_FragCoord.z*65535.0 + 0.1);', 'float rf = floor(iz/256.0)/255.0;', 'float gf = mod(iz,256.0)/255.0;', 'gl_FragData[0] = vec4(rf, gf, 0.0, 1.0); }']).result; shaders.Fragment = FSSource; } publicAPI.replaceShaderCoincidentOffset(shaders, ren, actor); }; publicAPI.replaceShaderTCoord = function (shaders, ren, actor) { var VSSource = shaders.Vertex; var GSSource = shaders.Geometry; var FSSource = shaders.Fragment; var tcoordVSDec = ['uniform mat4 WCTCMatrix;', 'out vec3 fragTexCoord;']; var slabThickness = model.renderable.getSlabThickness(); VSSource = vtkShaderProgram.substitute(VSSource, '//VTK::TCoord::Dec', tcoordVSDec).result; var tcoordVSImpl = ['fragTexCoord = (WCTCMatrix * vertexWC).xyz;']; VSSource = vtkShaderProgram.substitute(VSSource, '//VTK::TCoord::Impl', tcoordVSImpl).result; var tNumComp = model.openGLTexture.getComponents(); var iComps = actor.getProperty().getIndependentComponents(); var tcoordFSDec = ['in vec3 fragTexCoord;', 'uniform highp sampler3D texture1;', 'uniform mat4 WCTCMatrix;', // color shift and scale 'uniform float cshift0;', 'uniform float cscale0;', // pwf shift and scale 'uniform float pwfshift0;', 'uniform float pwfscale0;', // color and pwf textures 'uniform sampler2D colorTexture1;', 'uniform sampler2D pwfTexture1;', // opacity 'uniform float opacity;', // background color 'uniform vec4 backgroundColor;']; if (iComps) { for (var comp = 1; comp < tNumComp; comp++) { tcoordFSDec = tcoordFSDec.concat([// color shift and scale "uniform float cshift".concat(comp, ";"), "uniform float cscale".concat(comp, ";"), // weighting shift and scale "uniform float pwfshift".concat(comp, ";"), "uniform float pwfscale".concat(comp, ";")]); } // the heights defined below are the locations // for the up to four components of the tfuns // the tfuns have a height of 2XnumComps pixels so the // values are computed to hit the middle of the two rows // for that component switch (tNumComp) { case 1: tcoordFSDec = tcoordFSDec.concat(['uniform float mix0;', '#define height0 0.5']); break; case 2: tcoordFSDec = tcoordFSDec.concat(['uniform float mix0;', 'uniform float mix1;', '#define height0 0.25', '#define height1 0.75']); break; case 3: tcoordFSDec = tcoordFSDec.concat(['uniform float mix0;', 'uniform float mix1;', 'uniform float mix2;', '#define height0 0.17', '#define height1 0.5', '#define height2 0.83']); break; case 4: tcoordFSDec = tcoordFSDec.concat(['uniform float mix0;', 'uniform float mix1;', 'uniform float mix2;', 'uniform float mix3;', '#define height0 0.125', '#define height1 0.375', '#define height2 0.625', '#define height3 0.875']); break; default: vtkErrorMacro('Unsupported number of independent coordinates.'); } } if (slabThickness > 0.0) { tcoordFSDec = tcoordFSDec.concat(['uniform vec3 spacing;', 'uniform float slabThickness;', 'uniform int slabType;', 'uniform int slabTrapezoid;']); tcoordFSDec = tcoordFSDec.concat(['vec4 compositeValue(vec4 currVal, vec4 valToComp, int trapezoid)', '{', ' vec4 retVal = vec4(1.0);', ' if (slabType == 0) // min', ' {', ' retVal = min(currVal, valToComp);', ' }', ' else if (slabType == 1) // max', ' {', ' retVal = max(currVal, valToComp);', ' }', ' else if (slabType == 3) // sum', ' {', ' retVal = currVal + (trapezoid > 0 ? 0.5 * valToComp : valToComp); ', ' }', ' else // mean', ' {', ' retVal = currVal + (trapezoid > 0 ? 0.5 * valToComp : valToComp); ', ' }', ' return retVal;', '}']); } FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::TCoord::Dec', tcoordFSDec).result; var tcoordFSImpl = ['if (any(greaterThan(fragTexCoord, vec3(1.0))) || any(lessThan(fragTexCoord, vec3(0.0))))', '{', ' // set the background color and exit', ' gl_FragData[0] = backgroundColor;', ' return;', '}', 'vec4 tvalue = texture(texture1, fragTexCoord);']; if (slabThickness > 0.0) { tcoordFSImpl = tcoordFSImpl.concat(['// Get the first and last samples', 'int numSlices = 1;', 'vec3 normalxspacing = normalWCVSOutput * spacing * 0.5;', 'float distTraveled = length(normalxspacing);', 'int trapezoid = 0;', 'while (distTraveled < slabThickness * 0.5)', '{', ' distTraveled += length(normalxspacing);', ' float fnumSlices = float(numSlices);', ' if (distTraveled > slabThickness * 0.5)', ' {', ' // Before stepping outside the slab, sample at the boundaries', ' normalxspacing = normalWCVSOutput * slabThickness * 0.5 / fnumSlices;', ' trapezoid = slabTrapezoid;', ' }', ' vec3 fragTCoordNeg = (WCTCMatrix * vec4(vertexWCVSOutput.xyz - fnumSlices * normalxspacing, 1.0)).xyz;', ' if (!any(greaterThan(fragTCoordNeg, vec3(1.0))) && !any(lessThan(fragTCoordNeg, vec3(0.0))))', ' {', ' vec4 newVal = texture(texture1, fragTCoordNeg);', ' tvalue = compositeValue(tvalue, newVal, trapezoid);', ' numSlices += 1;', ' }', ' vec3 fragTCoordPos = (WCTCMatrix * vec4(vertexWCVSOutput.xyz + fnumSlices * normalxspacing, 1.0)).xyz;', ' if (!any(greaterThan(fragTCoordNeg, vec3(1.0))) && !any(lessThan(fragTCoordNeg, vec3(0.0))))', ' {', ' vec4 newVal = texture(texture1, fragTCoordPos);', ' tvalue = compositeValue(tvalue, newVal, trapezoid);', ' numSlices += 1;', ' }', '}', '// Finally, if slab type is *mean*, divide the sum by the numSlices', 'if (slabType == 2)', '{', ' tvalue = tvalue / float(numSlices);', '}']); } if (iComps) { var rgba = ['r', 'g', 'b', 'a']; for (var _comp = 0; _comp < tNumComp; ++_comp) { tcoordFSImpl = tcoordFSImpl.concat(["vec3 tcolor".concat(_comp, " = mix").concat(_comp, " * texture2D(colorTexture1, vec2(tvalue.").concat(rgba[_comp], " * cscale").concat(_comp, " + cshift").concat(_comp, ", height").concat(_comp, ")).rgb;"), "float compWeight".concat(_comp, " = mix").concat(_comp, " * texture2D(pwfTexture1, vec2(tvalue.").concat(rgba[_comp], " * pwfscale").concat(_comp, " + pwfshift").concat(_comp, ", height").concat(_comp, ")).r;")]); } switch (tNumComp) { case 1: tcoordFSImpl = tcoordFSImpl.concat(['gl_FragData[0] = vec4(tcolor0.rgb, compWeight0 * opacity);']); break; case 2: tcoordFSImpl = tcoordFSImpl.concat(['float weightSum = compWeight0 + compWeight1;', 'gl_FragData[0] = vec4(vec3((tcolor0.rgb * (compWeight0 / weightSum)) + (tcolor1.rgb * (compWeight1 / weightSum))), opacity);']); break; case 3: tcoordFSImpl = tcoordFSImpl.concat(['float weightSum = compWeight0 + compWeight1 + compWeight2;', 'gl_FragData[0] = vec4(vec3((tcolor0.rgb * (compWeight0 / weightSum)) + (tcolor1.rgb * (compWeight1 / weightSum)) + (tcolor2.rgb * (compWeight2 / weightSum))), opacity);']); break; case 4: tcoordFSImpl = tcoordFSImpl.concat(['float weightSum = compWeight0 + compWeight1 + compWeight2 + compWeight3;', 'gl_FragData[0] = vec4(vec3((tcolor0.rgb * (compWeight0 / weightSum)) + (tcolor1.rgb * (compWeight1 / weightSum)) + (tcolor2.rgb * (compWeight2 / weightSum)) + (tcolor3.rgb * (compWeight3 / weightSum))), opacity);']); break; default: vtkErrorMacro('Unsupported number of independent coordinates.'); } } else { // dependent components switch (tNumComp) { case 1: tcoordFSImpl = tcoordFSImpl.concat(['// Dependent components', 'float intensity = tvalue.r;', 'vec3 tcolor = texture2D(colorTexture1, vec2(intensity * cscale0 + cshift0, 0.5)).rgb;', 'float scalarOpacity = texture2D(pwfTexture1, vec2(intensity * pwfscale0 + pwfshift0, 0.5)).r;', 'gl_FragData[0] = vec4(tcolor, scalarOpacity * opacity);']); break; case 2: tcoordFSImpl = tcoordFSImpl.concat(['float intensity = tvalue.r*cscale0 + cshift0;', 'gl_FragData[0] = vec4(texture2D(colorTexture1, vec2(intensity, 0.5)).rgb, pwfscale0*tvalue.g + pwfshift0);']); break; case 3: tcoordFSImpl = tcoordFSImpl.concat(['vec4 tcolor = cscale0*tvalue + cshift0;', 'gl_FragData[0] = vec4(texture2D(colorTexture1, vec2(tcolor.r,0.5)).r,', ' texture2D(colorTexture1, vec2(tcolor.g,0.5)).r,', ' texture2D(colorTexture1, vec2(tcolor.b,0.5)).r, opacity);']); break; default: tcoordFSImpl = tcoordFSImpl.concat(['vec4 tcolor = cscale0*tvalue + cshift0;', 'gl_FragData[0] = vec4(texture2D(colorTexture1, vec2(tcolor.r,0.5)).r,', ' texture2D(colorTexture1, vec2(tcolor.g,0.5)).r,', ' texture2D(colorTexture1, vec2(tcolor.b,0.5)).r, tcolor.a);']); } } FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::TCoord::Impl', tcoordFSImpl).result; shaders.Vertex = VSSource; shaders.Fragment = FSSource; shaders.Geometry = GSSource; }; publicAPI.replaceShaderPositionVC = function (shaders, ren, actor) { var VSSource = shaders.Vertex; var GSSource = shaders.Geometry; var FSSource = shaders.Fragment; var slabThickness = model.renderable.getSlabThickness(); var posVCVSDec = ['attribute vec4 vertexWC;']; // Add a unique hash to the shader to ensure that the shader program is unique to this mapper. posVCVSDec = posVCVSDec.concat(["//".concat(publicAPI.getMTime()).concat(model.resliceGeomUpdateString)]); if (slabThickness > 0.0) { posVCVSDec = posVCVSDec.concat(['attribute vec3 normalWC;', 'varying vec3 normalWCVSOutput;', 'varying vec4 vertexWCVSOutput;']); } VSSource = vtkShaderProgram.substitute(VSSource, '//VTK::PositionVC::Dec', posVCVSDec).result; var posVCVSImpl = ['gl_Position = MCPCMatrix * vertexWC;']; if (slabThickness > 0.0) { posVCVSImpl = posVCVSImpl.concat(['normalWCVSOutput = normalWC;', 'vertexWCVSOutput = vertexWC;']); } VSSource = vtkShaderProgram.substitute(VSSource, '//VTK::PositionVC::Impl', posVCVSImpl).result; VSSource = vtkShaderProgram.substitute(VSSource, '//VTK::Camera::Dec', ['uniform mat4 MCPCMatrix;', 'uniform mat4 MCVCMatrix;']).result; var posVCFSDec = []; if (slabThickness > 0.0) { posVCFSDec = posVCFSDec.concat(['varying vec3 normalWCVSOutput;', 'varying vec4 vertexWCVSOutput;']); } FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::PositionVC::Dec', posVCFSDec).result; shaders.Vertex = VSSource; shaders.Geometry = GSSource; shaders.Fragment = FSSource; }; function isVectorAxisAligned(n) { vtkMath.normalize(n); var tmpN = [0, 0, 0]; for (var i = 0; i < 3; ++i) { vec3.zero(tmpN); tmpN[i] = 1.0; var dotP = vtkMath.dot(n, tmpN); if (dotP < -0.999 || dotP > 0.999) { return [true, i]; } } return [false, 2]; } publicAPI.updateResliceGeometry = function () { var resGeomString = ''; var image = model.currentInput; var imageBounds = image === null || image === void 0 ? void 0 : image.getBounds(); // Orthogonal slicing by default var orthoSlicing = true; var orthoAxis = 2; if (model.renderable.getSlicePolyData()) { resGeomString = resGeomString.concat("PolyData".concat(model.renderable.getSlicePolyData().getMTime())); } else if (model.renderable.getSlicePlane()) { resGeomString = resGeomString.concat("Plane".concat(model.renderable.getSlicePlane().getMTime())); if (image) { resGeomString = resGeomString.concat("Image".concat(image.getMTime())); } // Check to see if we can bypass oblique slicing related bounds computation var _isVectorAxisAligned = isVectorAxisAligned(model.renderable.getSlicePlane().getNormal()); var _isVectorAxisAligned2 = _slicedToArray(_isVectorAxisAligned, 2); orthoSlicing = _isVectorAxisAligned2[0]; orthoAxis = _isVectorAxisAligned2[1]; } else { var _model$renderable$get; // Create a default slice plane here var plane = vtkPlane.newInstance(); plane.setNormal(0, 0, 1); var bds = [0, 1, 0, 1, 0, 1]; if (image) { bds = imageBounds; } plane.setOrigin(bds[0], bds[2], 0.5 * (bds[5] + bds[4])); model.renderable.setSlicePlane(plane); resGeomString = resGeomString.concat("Plane".concat((_model$renderable$get = model.renderable.getSlicePlane()) === null || _model$renderable$get === void 0 ? void 0 : _model$renderable$get.getMTime())); if (image) { resGeomString = resGeomString.concat("Image".concat(image.getMTime())); } } if (!model.resliceGeom || model.resliceGeomUpdateString !== resGeomString) { if (model.renderable.getSlicePolyData()) { model.resliceGeom = model.renderable.getSlicePolyData(); } else if (model.renderable.getSlicePlane()) { var bounds = image ? imageBounds : [0, 1, 0, 1, 0, 1]; if (!orthoSlicing) { var cube = vtkCubeSource.newInstance(); cube.setCenter(0.5 * (bounds[0] + bounds[1]), 0.5 * (bounds[2] + bounds[3]), 0.5 * (bounds[4] + bounds[5])); cube.setXLength(bounds[1] - bounds[0]); cube.setYLength(bounds[3] - bounds[2]); cube.setZLength(bounds[5] - bounds[4]); var cutter = vtkCutter.newInstance(); cutter.setInputConnection(cube.getOutputPort()); cutter.setCutFunction(model.renderable.getSlicePlane()); var pds = vtkClosedPolyLineToSurfaceFilter.newInstance(); pds.setInputConnection(cutter.getOutputPort()); pds.update(); model.resliceGeom = pds.getOutputData(); // The above method does not generate point normals // Set it manually here. var n = model.renderable.getSlicePlane().getNormal(); var npts = model.resliceGeom.getNumberOfPoints(); vtkMath.normalize(n); var normalsData = new Float32Array(npts * 3); for (var i = 0; i < npts; ++i) { normalsData[3 * i] = n[0]; normalsData[3 * i + 1] = n[1]; normalsData[3 * i + 2] = n[2]; } var normals = vtkDataArray.newInstance({ numberOfComponents: 3, values: normalsData, name: 'Normals' }); model.resliceGeom.getPointData().setNormals(normals); } else { var ptsArray = new Float32Array(12); var o = model.renderable.getSlicePlane().getOrigin(); var otherAxes = [(orthoAxis + 1) % 3, (orthoAxis + 2) % 3].sort(); var ptIdx = 0; for (var _i7 = 0; _i7 < 2; ++_i7) { for (var j = 0; j < 2; ++j) { ptsArray[ptIdx + orthoAxis] = o[orthoAxis]; ptsArray[ptIdx + otherAxes[0]] = bounds[2 * otherAxes[0] + j]; ptsArray[ptIdx + otherAxes[1]] = bounds[2 * otherAxes[1] + _i7]; ptIdx += 3; } } var cellArray = new Uint16Array(8); cellArray[0] = 3; cellArray[1] = 0; cellArray[2] = 1; cellArray[3] = 3; cellArray[4] = 3; cellArray[5] = 0; cellArray[6] = 3; cellArray[7] = 2; var _n = model.renderable.getSlicePlane().getNormal(); vtkMath.normalize(_n); var _normalsData = new Float32Array(12); for (var _i8 = 0; _i8 < 4; ++_i8) { _normalsData[3 * _i8] = _n[0]; _normalsData[3 * _i8 + 1] = _n[1]; _normalsData[3 * _i8 + 2] = _n[2]; } if (!model.resliceGeom) { model.resliceGeom = vtkPolyData.newInstance(); } model.resliceGeom.getPoints().setData(ptsArray, 3); model.resliceGeom.getPolys().setData(cellArray, 1); var _normals = vtkDataArray.newInstance({ numberOfComponents: 3, values: _normalsData, name: 'Normals' }); model.resliceGeom.getPointData().setNormals(_normals); model.resliceGeom.modified(); } } else { vtkErrorMacro('Something went wrong.', 'A default slice plane should have been created in the beginning of', 'updateResliceGeometry.'); } model.resliceGeomUpdateString = resGeomString; } }; publicAPI.setOpenGLTexture = function (oglTex) { if (oglTex) { model.openGLTexture = oglTex; model._externalOpenGLTexture = true; } }; } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- var DEFAULT_VALUES = { VBOBuildTime: {}, VBOBuildString: null, haveSeenDepthRequest: false, lastHaveSeenDepthRequest: false, lastIndependentComponents: false, lastTextureComponents: 0, lastSlabThickness: 0, lastSlabTrapezoidIntegration: 0, lastSlabType: -1, openGLTexture: null, openGLTextureString: null, colorTextureString: null, pwfTextureString: null, resliceGeom: null, resliceGeomUpdateString: null, tris: null, colorTexture: null, pwfTexture: null, _externalOpenGLTexture: false }; // ---------------------------------------------------------------------------- function extend(publicAPI, model) { var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; Object.assign(model, DEFAULT_VALUES, initialValues); // Inheritance vtkViewNode.extend(publicAPI, model, initialValues); vtkReplacementShaderMapper.implementReplaceShaderCoincidentOffset(publicAPI, model, initialValues); vtkReplacementShaderMapper.implementBuildShadersWithReplacements(publicAPI, model, initialValues); model.tris = vtkHelper.newInstance(); model.openGLTexture = vtkOpenGLTexture.newInstance(); model.colorTexture = vtkOpenGLTexture.newInstance(); model.pwfTexture = vtkOpenGLTexture.newInstance(); model.VBOBuildTime = {}; obj(model.VBOBuildTime); // model.modelToView = mat4.identity(new Float64Array(16)); model.tmpMat4 = mat4.identity(new Float64Array(16)); get(publicAPI, model, ['openGLTexture']); // Object methods vtkOpenGLImageResliceMapper(publicAPI, model); } // ---------------------------------------------------------------------------- var newInstance = newInstance$1(extend, 'vtkOpenGLImageResliceMapper'); // ---------------------------------------------------------------------------- var vtkImageResliceMapper = { newInstance: newInstance, extend: extend }; // Register ourself to OpenGL backend if imported registerOverride('vtkImageResliceMapper', newInstance); export { vtkImageResliceMapper as default, extend, newInstance };