@thewtex/vtk.js-esm
Version:
Visualization Toolkit for the Web
291 lines (226 loc) • 14.9 kB
JavaScript
import { newInstance as newInstance$1, vtkErrorMacro as vtkErrorMacro$1 } from '../../macro.js';
import vtkWebGPUPolyDataMapper from './PolyDataMapper.js';
import vtkWebGPUBufferManager from './BufferManager.js';
import vtkWebGPUShaderCache from './ShaderCache.js';
import { registerOverride } from './ViewNodeFactory.js';
var BufferUsage = vtkWebGPUBufferManager.BufferUsage,
PrimitiveTypes = vtkWebGPUBufferManager.PrimitiveTypes;
var vtkErrorMacro = vtkErrorMacro$1; // Vertices
// 013 - 032 - 324 - 453
//
// _.4---_.5
// .-* .-*
// 2-----3
// | /|
// | / |
// | / |
// | / |
// |/ |
// 0-----1
//
// coord for each points
// 0: 000
// 1: 100
// 2: 001
// 3: 101
// 4: 011
// 5: 111
var vtkWebGPUStickMapperVS = "\n//VTK::Renderer::Dec\n\n//VTK::Mapper::Dec\n\n//VTK::Color::Dec\n\n//VTK::IOStructs::Dec\n\n[[stage(vertex)]]\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n let offsetsArray: array<vec3<f32>, 12> = array<vec3<f32>, 12>(\n vec3<f32>(-1.0, -1.0, -1.0),\n vec3<f32>(1.0, -1.0, -1.0),\n vec3<f32>(1.0, -1.0, 1.0),\n\n vec3<f32>(-1.0, -1.0, -1.0),\n vec3<f32>(1.0, -1.0, 1.0),\n vec3<f32>(-1.0, -1.0, 1.0),\n\n vec3<f32>(-1.0, -1.0, 1.0),\n vec3<f32>(1.0, -1.0, 1.0),\n vec3<f32>(1.0, 1.0, 1.0),\n\n vec3<f32>(-1.0, -1.0, 1.0),\n vec3<f32>(1.0, 1.0, 1.0),\n vec3<f32>(-1.0, 1.0, 1.0)\n );\n\n var output : vertexOutput;\n\n var vertexVC: vec4<f32> = rendererUBO.SCVCMatrix * mapperUBO.BCSCMatrix * vec4<f32>(vertexBC.x, vertexBC.y, vertexBC.z, 1.0);\n\n //VTK::Color::Impl\n\n // compute the projected vertex position\n output.centerVC = vertexVC.xyz;\n output.radiusVC = radiusMC;\n output.lengthVC = length(orientMC);\n output.orientVC = (rendererUBO.WCVCNormals * vec4<f32>(normalize(orientMC), 0.0)).xyz;\n\n // make sure it is pointing out of the screen\n if (output.orientVC.z < 0.0)\n {\n output.orientVC = -output.orientVC;\n }\n\n // make the basis\n var xbase: vec3<f32>;\n var ybase: vec3<f32>;\n var dir: vec3<f32> = vec3<f32>(0.0,0.0,1.0);\n if (rendererUBO.cameraParallel == 0u)\n {\n dir = normalize(-vertexVC.xyz);\n }\n if (abs(dot(dir,output.orientVC)) == 1.0)\n {\n xbase = normalize(cross(vec3<f32>(0.0,1.0,0.0),output.orientVC));\n ybase = cross(xbase,output.orientVC);\n }\n else\n {\n xbase = normalize(cross(output.orientVC,dir));\n ybase = cross(output.orientVC,xbase);\n }\n\n\n var vertIdx: u32 = input.vertexIndex % 12u;\n var offsets: vec3<f32> = offsetsArray[vertIdx];\n\n vertexVC = vec4<f32>(vertexVC.xyz +\n output.radiusVC * offsets.x * xbase +\n output.radiusVC * offsets.y * ybase +\n 0.5 * output.lengthVC * offsets.z * output.orientVC, 1.0);\n\n output.vertexVC = vertexVC;\n\n //VTK::Position::Impl\n\n return output;\n}\n"; // ----------------------------------------------------------------------------
// vtkWebGPUStickMapper methods
// ----------------------------------------------------------------------------
function vtkWebGPUStickMapper(publicAPI, model) {
// Set our className
model.classHierarchy.push('vtkWebGPUStickMapper');
publicAPI.replaceShaderNormal = function (hash, pipeline, vertexInput) {
var vDesc = pipeline.getShaderDescription('vertex');
vDesc.addOutput('vec4<f32>', 'vertexVC');
vDesc.addOutput('vec3<f32>', 'centerVC');
vDesc.addOutput('vec3<f32>', 'orientVC');
vDesc.addOutput('f32', 'radiusVC');
vDesc.addOutput('f32', 'lengthVC');
vDesc.addBuiltinInput('u32', '[[builtin(vertex_index)]] vertexIndex');
var fDesc = pipeline.getShaderDescription('fragment');
fDesc.addBuiltinOutput('f32', '[[builtin(frag_depth)]] fragDepth');
var stickFrag = "\n // compute the eye position and unit direction\n var vertexVC: vec4<f32>;\n var EyePos: vec3<f32>;\n var EyeDir: vec3<f32>;\n\n if (rendererUBO.cameraParallel != 0u)\n {\n EyePos = vec3<f32>(input.vertexVC.x, input.vertexVC.y, input.vertexVC.z + 3.0*input.radiusVC);\n EyeDir = vec3<f32>(0.0, 0.0, -1.0);\n }\n else\n {\n EyeDir = input.vertexVC.xyz;\n EyePos = vec3<f32>(0.0,0.0,0.0);\n var lengthED: f32 = length(EyeDir);\n EyeDir = normalize(EyeDir);\n // we adjust the EyePos to be closer if it is too far away\n // to prevent floating point precision noise\n if (lengthED > input.radiusVC*3.0)\n {\n EyePos = input.vertexVC.xyz - EyeDir*3.0*input.radiusVC;\n }\n }\n // translate to Sphere center\n EyePos = EyePos - input.centerVC;\n\n // rotate to new basis\n // base1, base2, orientVC\n var base1: vec3<f32>;\n if (abs(input.orientVC.z) < 0.99)\n {\n base1 = normalize(cross(input.orientVC,vec3<f32>(0.0,0.0,1.0)));\n }\n else\n {\n base1 = normalize(cross(input.orientVC,vec3<f32>(0.0,1.0,0.0)));\n }\n var base2: vec3<f32> = cross(input.orientVC,base1);\n EyePos = vec3<f32>(dot(EyePos,base1),dot(EyePos,base2),dot(EyePos,input.orientVC));\n EyeDir = vec3<f32>(dot(EyeDir,base1),dot(EyeDir,base2),dot(EyeDir,input.orientVC));\n\n // scale to radius 1.0\n EyePos = EyePos * (1.0 / input.radiusVC);\n\n // find the intersection\n var a: f32 = EyeDir.x*EyeDir.x + EyeDir.y*EyeDir.y;\n var b: f32 = 2.0*(EyePos.x*EyeDir.x + EyePos.y*EyeDir.y);\n var c: f32 = EyePos.x*EyePos.x + EyePos.y*EyePos.y - 1.0;\n var d: f32 = b*b - 4.0*a*c;\n var normal: vec3<f32> = vec3<f32>(0.0,0.0,1.0);\n if (d < 0.0) { discard; }\n else\n {\n var t: f32 = (-b - sqrt(d))*(0.5 / a);\n var tz: f32 = EyePos.z + t*EyeDir.z;\n var iPoint: vec3<f32> = EyePos + t*EyeDir;\n if (abs(iPoint.z)*input.radiusVC > input.lengthVC*0.5)\n {\n // test for end cap\n var t2: f32 = (-b + sqrt(d))*(0.5 / a);\n var tz2: f32 = EyePos.z + t2*EyeDir.z;\n if (tz2*input.radiusVC > input.lengthVC*0.5 || tz*input.radiusVC < -0.5*input.lengthVC) { discard; }\n else\n {\n normal = input.orientVC;\n var t3: f32 = (input.lengthVC*0.5/input.radiusVC - EyePos.z)/EyeDir.z;\n iPoint = EyePos + t3*EyeDir;\n vertexVC = vec4<f32>(input.radiusVC*(iPoint.x*base1 + iPoint.y*base2 + iPoint.z*input.orientVC) + input.centerVC, 1.0);\n }\n }\n else\n {\n // The normal is the iPoint.xy rotated back into VC\n normal = iPoint.x*base1 + iPoint.y*base2;\n // rescale rerotate and translate\n vertexVC = vec4<f32>(input.radiusVC*(normal + iPoint.z*input.orientVC) + input.centerVC, 1.0);\n }\n // compute the pixel's depth\n var pos: vec4<f32> = rendererUBO.VCPCMatrix * vertexVC;\n output.fragDepth = pos.z / pos.w;\n }\n ";
var code = fDesc.getCode();
code = vtkWebGPUShaderCache.substitute(code, '//VTK::Normal::Impl', [stickFrag]).result;
fDesc.setCode(code);
};
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.Position = rendererUBO.VCPCMatrix*vertexVC;']).result;
vDesc.setCode(code);
}; // compute a unique hash for a pipeline, this needs to be unique enough to
// capture any pipeline code changes (which includes shader changes)
// or vertex input changes/ bind groups/ etc
publicAPI.computePipelineHash = function (vertexInput) {
var pipelineHash = 'stm';
if (vertexInput.hasAttribute("colorVI")) {
pipelineHash += "c";
}
pipelineHash += model.renderEncoder.getPipelineHash();
return pipelineHash;
}; // was originally buildIBOs() but not using IBOs right now
publicAPI.buildPrimitives = function () {
var poly = model.currentInput;
var device = model.WebGPURenderWindow.getDevice();
model.renderable.mapScalars(poly, 1.0); // handle triangles
var i = PrimitiveTypes.Triangles;
var points = poly.getPoints();
var pointData = poly.getPointData();
var numPoints = points.getNumberOfPoints();
var pointArray = points.getData();
var primHelper = model.primitives[i];
primHelper.setNumberOfInstances(numPoints);
primHelper.setNumberOfVertices(12);
var vertexInput = model.primitives[i].getVertexInput();
var buffRequest = {
hash: points.getMTime(),
source: points,
time: points.getMTime(),
usage: BufferUsage.RawVertex,
format: 'float32x3'
};
if (!device.getBufferManager().hasBuffer(buffRequest)) {
// xyz v1 v2 v3
var tmpVBO = new Float32Array(numPoints * 3);
var pointIdx = 0;
var vboIdx = 0;
for (var id = 0; id < numPoints; ++id) {
pointIdx = id * 3;
tmpVBO[vboIdx++] = pointArray[pointIdx];
tmpVBO[vboIdx++] = pointArray[pointIdx + 1];
tmpVBO[vboIdx++] = pointArray[pointIdx + 2];
}
buffRequest.nativeArray = tmpVBO;
var buff = device.getBufferManager().getBuffer(buffRequest);
vertexInput.addBuffer(buff, ['vertexBC'], 'instance');
} // compute offset VBO
var scales = null;
if (model.renderable.getScaleArray() != null && pointData.hasArray(model.renderable.getScaleArray())) {
scales = pointData.getArray(model.renderable.getScaleArray()).getData();
}
var defaultRadius = model.renderable.getRadius();
if (scales || defaultRadius !== model._lastRadius) {
buffRequest = {
hash: scales,
source: scales,
time: scales ? pointData.getArray(model.renderable.getScaleArray()).getMTime() : 0,
usage: BufferUsage.RawVertex,
format: 'float32'
};
if (!device.getBufferManager().hasBuffer(buffRequest)) {
var _tmpVBO = new Float32Array(numPoints);
var _vboIdx = 0;
for (var _id = 0; _id < numPoints; ++_id) {
var radius = model.renderable.getRadius();
if (scales) {
radius = scales[_id * 2 + 1];
}
_tmpVBO[_vboIdx++] = radius;
}
buffRequest.nativeArray = _tmpVBO;
var _buff = device.getBufferManager().getBuffer(buffRequest);
vertexInput.addBuffer(_buff, ['radiusMC'], 'instance');
}
model._lastRadius = defaultRadius;
}
var orientationArray = null;
if (model.renderable.getOrientationArray() != null && pointData.hasArray(model.renderable.getOrientationArray())) {
orientationArray = pointData.getArray(model.renderable.getOrientationArray()).getData();
} else {
vtkErrorMacro(['Error setting orientationArray.\n', 'You have to specify the stick orientation']);
}
buffRequest = {
hash: scales,
source: orientationArray,
time: pointData.getArray(model.renderable.getOrientationArray()).getMTime(),
usage: BufferUsage.RawVertex,
format: 'float32x3'
};
if (!device.getBufferManager().hasBuffer(buffRequest)) {
// xyz v1 v2 v3
var _tmpVBO2 = new Float32Array(numPoints * 3);
var _pointIdx = 0;
var _vboIdx2 = 0;
for (var _id2 = 0; _id2 < numPoints; ++_id2) {
_pointIdx = _id2 * 3;
var length = model.renderable.getLength();
if (scales) {
length = scales[_id2 * 2];
}
_tmpVBO2[_vboIdx2++] = orientationArray[_pointIdx] * length;
_tmpVBO2[_vboIdx2++] = orientationArray[_pointIdx + 1] * length;
_tmpVBO2[_vboIdx2++] = orientationArray[_pointIdx + 2] * length;
}
buffRequest.nativeArray = _tmpVBO2;
var _buff2 = device.getBufferManager().getBuffer(buffRequest);
vertexInput.addBuffer(_buff2, ['orientMC'], 'instance');
}
model.renderable.mapScalars(poly, 1.0); // deal with colors but only if modified
var haveColors = false;
if (model.renderable.getScalarVisibility()) {
var c = model.renderable.getColorMapColors();
if (c) {
buffRequest = {
hash: c,
source: c,
time: c.getMTime(),
usage: BufferUsage.RawVertex,
format: 'unorm8x4'
};
if (!device.getBufferManager().hasBuffer(buffRequest)) {
var colorComponents = c.getNumberOfComponents();
if (colorComponents !== 4) {
vtkErrorMacro('this should be 4');
}
var _tmpVBO3 = new Uint8Array(numPoints * 4);
var _vboIdx3 = 0;
var colorData = c.getData();
for (var _id3 = 0; _id3 < numPoints; ++_id3) {
var colorIdx = _id3 * colorComponents;
_tmpVBO3[_vboIdx3++] = colorData[colorIdx];
_tmpVBO3[_vboIdx3++] = colorData[colorIdx + 1];
_tmpVBO3[_vboIdx3++] = colorData[colorIdx + 2];
_tmpVBO3[_vboIdx3++] = colorData[colorIdx + 3];
}
buffRequest.nativeArray = _tmpVBO3;
var _buff3 = device.getBufferManager().getBuffer(buffRequest);
vertexInput.addBuffer(_buff3, ['colorVI'], 'instance');
}
haveColors = true;
}
}
if (!haveColors) {
vertexInput.removeBufferIfPresent('colorVI');
}
primHelper.setPipelineHash(publicAPI.computePipelineHash(vertexInput));
primHelper.setWebGPURenderer(model.WebGPURenderer);
primHelper.setTopology('triangle-list');
primHelper.build(model.renderEncoder, device);
primHelper.registerToDraw();
};
} // ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------
var DEFAULT_VALUES = {}; // ----------------------------------------------------------------------------
function extend(publicAPI, model) {
var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
Object.assign(model, DEFAULT_VALUES, initialValues); // Inheritance
vtkWebGPUPolyDataMapper.extend(publicAPI, model, initialValues);
model.primitives[PrimitiveTypes.Triangles].setVertexShaderTemplate(vtkWebGPUStickMapperVS); // Object methods
vtkWebGPUStickMapper(publicAPI, model);
var sr = model.primitives[PrimitiveTypes.Triangles].getShaderReplacements();
sr.set('replaceShaderPosition', publicAPI.replaceShaderPosition);
sr.set('replaceShaderNormal', publicAPI.replaceShaderNormal);
} // ----------------------------------------------------------------------------
var newInstance = newInstance$1(extend, 'vtkWebGPUStickMapper'); // ----------------------------------------------------------------------------
var index = {
newInstance: newInstance,
extend: extend
}; // Register ourself to WebGPU backend if imported
registerOverride('vtkStickMapper', newInstance);
export default index;
export { extend, newInstance };