Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion Sources/Rendering/Core/AbstractMapper/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { mat4 } from 'gl-matrix';
import { vtkAlgorithm, vtkObject } from '../../../interfaces';
import vtkPlane from '../../../Common/DataModel/Plane';
import { mat4 } from 'gl-matrix';
import { Vector4 } from '../../../types';

/**
*
Expand Down Expand Up @@ -31,6 +32,12 @@ export interface vtkAbstractMapper extends vtkAbstractMapperBase {
*/
getClippingPlanes(): vtkPlane[];

/**
* Get the modified time of the clipping planes list.
* @return {Number} The modified time.
*/
getClippingPlanesMTime(): number;

/**
* Remove all clipping planes.
* @return true if there were planes, false otherwise.
Expand All @@ -50,9 +57,25 @@ export interface vtkAbstractMapper extends vtkAbstractMapperBase {
*/
setClippingPlanes(planes: vtkPlane[]): void;

/**
* Get the ith clipping plane transformed from world coordinates into the
* target coordinate system defined by the provided world-to-coordinates
* matrix.
* @param {mat4} worldToCoords
* @param {Number} i
* @param {Number[]} [hnormal]
*/
getClippingPlaneInCoords(
worldToCoords: mat4,
i: number,
hnormal?: Vector4 | Float64Array
): Vector4 | Float64Array | undefined;

/**
* Get the ith clipping plane as a homogeneous plane equation.
* Use getNumberOfClippingPlanes() to get the number of planes.
* This API expects a coordinates-to-world matrix and preserves the legacy
* behavior used by existing data-coordinate callers.
* @param {mat4} propMatrix
* @param {Number} i
* @param {Number[]} hnormal
Expand Down
66 changes: 47 additions & 19 deletions Sources/Rendering/Core/AbstractMapper/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,31 @@
import { mat4, vec4 } from 'gl-matrix';
import macro from 'vtk.js/Sources/macros';

const { vtkErrorMacro } = macro;

// ----------------------------------------------------------------------------
// vtkAbstractMapper methods
// ----------------------------------------------------------------------------

const tmpClipMatrix = new Float64Array(16);
const tmpClipWorldPlane = new Float64Array(4);

function getClipPlaneEquation(plane, out) {
const normal = plane.getNormalByReference();
const origin = plane.getOriginByReference();

out[0] = normal[0];
out[1] = normal[1];
out[2] = normal[2];
out[3] = -(
normal[0] * origin[0] +
normal[1] * origin[1] +
normal[2] * origin[2]
);
Comment thread
daker marked this conversation as resolved.

return out;
}

function vtkAbstractMapper(publicAPI, model) {
model.classHierarchy.push('vtkAbstractMapper');
publicAPI.update = () => {
Expand Down Expand Up @@ -45,6 +67,14 @@ function vtkAbstractMapper(publicAPI, model) {

publicAPI.getClippingPlanes = () => model.clippingPlanes;

publicAPI.getClippingPlanesMTime = () => {
let mtime = 0;
for (let i = 0; i < model.clippingPlanes.length; i++) {
mtime = Math.max(mtime, model.clippingPlanes[i].getMTime());
}
return mtime;
};

publicAPI.setClippingPlanes = (planes) => {
if (!planes) {
return;
Expand All @@ -59,34 +89,32 @@ function vtkAbstractMapper(publicAPI, model) {
}
};

publicAPI.getClippingPlaneInDataCoords = (propMatrix, i, hnormal) => {
publicAPI.getClippingPlaneInCoords = (worldToCoords, i, hnormal) => {
Comment thread
daker marked this conversation as resolved.
if (i < 0 || i >= model.clippingPlanes?.length) {
vtkErrorMacro(`Clipping plane index ${i} is out of range.`);
return undefined;
}
const outHNormal = hnormal || new Float64Array(4);
getClipPlaneEquation(model.clippingPlanes[i], tmpClipWorldPlane);
mat4.invert(tmpClipMatrix, worldToCoords);
mat4.transpose(tmpClipMatrix, tmpClipMatrix);
vec4.transformMat4(outHNormal, tmpClipWorldPlane, tmpClipMatrix);
return outHNormal;
};

publicAPI.getClippingPlaneInDataCoords = (coordsToWorld, i, hnormal) => {
const clipPlanes = model.clippingPlanes;
const mat = propMatrix;

if (clipPlanes) {
const n = clipPlanes.length;
if (i >= 0 && i < n) {
// Get the plane
const plane = clipPlanes[i];
const normal = plane.getNormal();
const origin = plane.getOrigin();

// Compute the plane equation
const v1 = normal[0];
const v2 = normal[1];
const v3 = normal[2];
const v4 = -(v1 * origin[0] + v2 * origin[1] + v3 * origin[2]);

// Transform normal from world to data coords
hnormal[0] = v1 * mat[0] + v2 * mat[4] + v3 * mat[8] + v4 * mat[12];
hnormal[1] = v1 * mat[1] + v2 * mat[5] + v3 * mat[9] + v4 * mat[13];
hnormal[2] = v1 * mat[2] + v2 * mat[6] + v3 * mat[10] + v4 * mat[14];
hnormal[3] = v1 * mat[3] + v2 * mat[7] + v3 * mat[11] + v4 * mat[15];
getClipPlaneEquation(clipPlanes[i], tmpClipWorldPlane);
vec4.transformMat4(hnormal, tmpClipWorldPlane, coordsToWorld);

return;
}
}
macro.vtkErrorMacro(`Clipping plane index ${i} is out of range.`);
vtkErrorMacro(`Clipping plane index ${i} is out of range.`);
};
}

Expand Down
52 changes: 49 additions & 3 deletions Sources/Rendering/WebGPU/CellArrayMapper/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,18 @@ import vtkWebGPUShaderCache from 'vtk.js/Sources/Rendering/WebGPU/ShaderCache';
import vtkWebGPUUniformBuffer from 'vtk.js/Sources/Rendering/WebGPU/UniformBuffer';
import vtkWebGPUSimpleMapper from 'vtk.js/Sources/Rendering/WebGPU/SimpleMapper';
import vtkWebGPUTypes from 'vtk.js/Sources/Rendering/WebGPU/Types';
import {
addClipPlaneEntries,
getClippingPlaneEquationsInCoords,
getClipPlaneShaderChecks,
MAX_CLIPPING_PLANES,
} from 'vtk.js/Sources/Rendering/WebGPU/Helpers/ClippingPlanes';

const { BufferUsage, PrimitiveTypes } = vtkWebGPUBufferManager;
const { Representation } = vtkProperty;
const { ScalarMode } = vtkMapper;
const { CoordinateSystem } = vtkProp;
const { DisplayLocation } = vtkProperty2D;

const vtkWebGPUPolyDataVS = `
//VTK::Renderer::Dec

Expand Down Expand Up @@ -380,6 +385,8 @@ fn main(
}
`;

const tmp2Mat4 = new Float64Array(16);

function isEdges(hash) {
// edge pipelines have "edge" in them
return hash.indexOf('edge') >= 0;
Expand Down Expand Up @@ -432,13 +439,15 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
publicAPI.updateUBO = () => {
const actor = model.WebGPUActor.getRenderable();
const ppty = actor.getProperty();
const clippingPlanesMTime = model.renderable.getClippingPlanesMTime();
const backfaceProperty = actor.getBackfaceProperty?.() ?? ppty;
const utime = model.UBO.getSendTime();
if (
publicAPI.getMTime() <= utime &&
ppty.getMTime() <= utime &&
backfaceProperty.getMTime() <= utime &&
model.renderable.getMTime() <= utime
model.renderable.getMTime() <= utime &&
clippingPlanesMTime <= utime
) {
return;
}
Expand Down Expand Up @@ -541,6 +550,24 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
model.UBO.setValue('LineWidth', ppty.getLineWidth());
model.UBO.setValue('Opacity', ppty.getOpacity());
model.UBO.setValue('PropID', model.WebGPUActor.getPropID());
model.UBO.setValue('NumClipPlanes', 0);

if (!model.is2D && model.useRendererMatrix) {
const center = model.WebGPURenderer.getStabilizedCenterByReference();
mat4.fromTranslation(tmp2Mat4, [-center[0], -center[1], -center[2]]);
const numClipPlanes = getClippingPlaneEquationsInCoords(
model.renderable,
tmp2Mat4,
model.clipPlanes
);
model.UBO.setValue('NumClipPlanes', numClipPlanes);

if (numClipPlanes > 0) {
for (let i = 0; i < numClipPlanes; i++) {
model.UBO.setArray(`ClipPlane${i}`, model.clipPlanes[i]);
}
}
}

// Only send if needed
model.UBO.sendIfNeeded(model.WebGPURenderWindow.getDevice());
Expand Down Expand Up @@ -586,9 +613,11 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
const vDesc = pipeline.getShaderDescription('vertex');
vDesc.addBuiltinOutput('vec4<f32>', '@builtin(position) Position');
if (!vDesc.hasOutput('vertexVC')) vDesc.addOutput('vec4<f32>', 'vertexVC');
if (!vDesc.hasOutput('vertexSC')) vDesc.addOutput('vec4<f32>', 'vertexSC');
let code = vDesc.getCode();
if (model.useRendererMatrix) {
code = vtkWebGPUShaderCache.substitute(code, '//VTK::Position::Impl', [
' output.vertexSC = mapperUBO.BCSCMatrix * vec4<f32>(vertexBC.xyz, 1.0);',
' var pCoord: vec4<f32> = rendererUBO.SCPCMatrix*mapperUBO.BCSCMatrix*vertexBC;',
' output.vertexVC = rendererUBO.SCVCMatrix * mapperUBO.BCSCMatrix * vec4<f32>(vertexBC.xyz, 1.0);',
'//VTK::Position::Impl',
Expand Down Expand Up @@ -635,6 +664,19 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
]).result;

vDesc.setCode(code);

const fDesc = pipeline.getShaderDescription('fragment');
code = fDesc.getCode();
const clipPlaneChecks = getClipPlaneShaderChecks({
countName: 'mapperUBO.NumClipPlanes',
planePrefix: 'mapperUBO.ClipPlane',
positionName: 'input.vertexSC',
});
code = vtkWebGPUShaderCache.substitute(code, '//VTK::Position::Impl', [
...clipPlaneChecks,
'//VTK::Position::Impl',
]).result;
fDesc.setCode(code);
};
model.shaderReplacements.set(
'replaceShaderPosition',
Expand Down Expand Up @@ -1451,7 +1493,6 @@ export function extend(publicAPI, model, initiaLalues = {}) {
model.vertexShaderTemplate = vtkWebGPUPolyDataVS;

model._tmpMat3 = mat3.identity(new Float64Array(9));
model._tmpMat4 = mat4.identity(new Float64Array(16));

// UBO
model.UBO = vtkWebGPUUniformBuffer.newInstance({ label: 'mapperUBO' });
Expand Down Expand Up @@ -1491,6 +1532,8 @@ export function extend(publicAPI, model, initiaLalues = {}) {
model.UBO.addEntry('ClipNear', 'f32');
model.UBO.addEntry('ClipFar', 'f32');
model.UBO.addEntry('Time', 'u32');
addClipPlaneEntries(model.UBO, 'ClipPlane');
model.UBO.addEntry('NumClipPlanes', 'u32');

// Build VTK API
macro.setGet(publicAPI, model, [
Expand All @@ -1503,6 +1546,9 @@ export function extend(publicAPI, model, initiaLalues = {}) {
]);

model.textures = [];
model.clipPlanes = Array.from({ length: MAX_CLIPPING_PLANES }, () => [
0.0, 0.0, 0.0, 0.0,
]);

// Object methods
vtkWebGPUCellArrayMapper(publicAPI, model);
Expand Down
34 changes: 34 additions & 0 deletions Sources/Rendering/WebGPU/Helpers/ClippingPlanes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
export const MAX_CLIPPING_PLANES = 6;

export function addClipPlaneEntries(buffer, prefix) {
for (let i = 0; i < MAX_CLIPPING_PLANES; i++) {
buffer.addEntry(`${prefix}${i}`, 'vec4<f32>');
}
}

export function getClipPlaneShaderChecks({
countName,
planePrefix,
positionName,
returnValue = 'discard',
}) {
const checks = [];
for (let i = 0; i < MAX_CLIPPING_PLANES; i++) {
checks.push(
` if (${countName} > ${i}u && dot(${planePrefix}${i}, ${positionName}) < 0.0) { ${returnValue}; }`
);
}
return checks;
}

export function getClippingPlaneEquationsInCoords(
mapper,
worldToCoords,
outPlanes
) {
const count = mapper.getClippingPlanes().length;
for (let i = 0; i < count; i++) {
mapper.getClippingPlaneInCoords(worldToCoords, i, outPlanes[i]);
}
return count;
}
Loading
Loading