Skip to content

Commit

Permalink
Merge branch 'master' into average_ip_scalar_fix
Browse files Browse the repository at this point in the history
  • Loading branch information
GeorgeFuller committed May 24, 2021
2 parents 91878e6 + 12f97e6 commit 8319b43
Show file tree
Hide file tree
Showing 21 changed files with 706 additions and 256 deletions.
3 changes: 2 additions & 1 deletion Sources/Proxy/Core/ViewProxy/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,8 @@ function vtkViewProxy(publicAPI, model) {

// --------------------------------------------------------------------------

publicAPI.captureImage = () => model.renderWindow.captureImages()[0];
publicAPI.captureImage = ({ format = 'image/png', ...opts } = {}) =>
model.renderWindow.captureImages(format, opts)[0];

// --------------------------------------------------------------------------

Expand Down
4 changes: 2 additions & 2 deletions Sources/Rendering/Core/RenderWindow/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,11 @@ function vtkRenderWindow(publicAPI, model) {
return results;
};

publicAPI.captureImages = (format = 'image/png') => {
publicAPI.captureImages = (format = 'image/png', opts = {}) => {
macro.setImmediate(publicAPI.render);
return model.views
.map((view) =>
view.captureNextImage ? view.captureNextImage(format) : undefined
view.captureNextImage ? view.captureNextImage(format, opts) : undefined
)
.filter((i) => !!i);
};
Expand Down
13 changes: 13 additions & 0 deletions Sources/Rendering/OpenGL/RenderWindow/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,16 @@ Initialize the rendering window. This will setup all system-specific
resources. This method and Finalize() must be symmetric and it
should be possible to call them multiple times, even changing WindowId
in-between. This is what WindowRemap does.

### captureNextImage(format, options);

Capture a screenshot of the contents of this renderwindow. The options object
can include a `size` array (`[w, h]`) or a `scale` floating point value, as well
as a `resetCamera` boolean. If `size` is provided, the captured screenshot will
be of the given size (and `resetCamera` could be useful in this case if the
aspect ratio of `size` does not match the current renderwindow size). Otherwise,
if `scale` is provided, it will be multiplied by the current renderwindow size
to compute the screenshot size. If no `size` or `scale` are provided, the
current renderwindow size is assumed. The default format is "image/png".

Returns a promise that resolves to the captured screenshot.
98 changes: 94 additions & 4 deletions Sources/Rendering/OpenGL/RenderWindow/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ import { VtkDataTypes } from 'vtk.js/Sources/Common/Core/DataArray/Constants';

const { vtkDebugMacro, vtkErrorMacro } = macro;
const IS_CHROME = navigator.userAgent.indexOf('Chrome') !== -1;
const SCREENSHOT_PLACEHOLDER = {
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
};

function checkRenderTargetSupport(gl, format, type) {
// create temporary frame buffer and texture
Expand Down Expand Up @@ -522,19 +529,102 @@ function vtkOpenGLRenderWindow(publicAPI, model) {
publicAPI.invokeImageReady(screenshot);
}

publicAPI.captureNextImage = (format = 'image/png') => {
publicAPI.captureNextImage = (
format = 'image/png',
{ resetCamera = false, size = null, scale = 1 } = {}
) => {
if (model.deleted) {
return null;
}
model.imageFormat = format;
const previous = model.notifyStartCaptureImage;
model.notifyStartCaptureImage = true;

model._screenshot = {
size:
!!size || scale !== 1
? size || model.size.map((val) => val * scale)
: null,
};

return new Promise((resolve, reject) => {
const subscription = publicAPI.onImageReady((imageURL) => {
model.notifyStartCaptureImage = previous;
subscription.unsubscribe();
resolve(imageURL);
if (model._screenshot.size === null) {
model.notifyStartCaptureImage = previous;
subscription.unsubscribe();
if (model._screenshot.placeHolder) {
// resize the main canvas back to its original size and show it
model.size = model._screenshot.originalSize;

// process the resize
publicAPI.modified();

// restore the saved camera parameters, if applicable
if (model._screenshot.cameras) {
model._screenshot.cameras.forEach(({ restoreParamsFn, arg }) =>
restoreParamsFn(arg)
);
}

// Trigger a render at the original size
publicAPI.traverseAllPasses();

// Remove and clean up the placeholder, revealing the original
model.el.removeChild(model._screenshot.placeHolder);
model._screenshot.placeHolder.remove();
model._screenshot = null;
}
resolve(imageURL);
} else {
// Create a placeholder image overlay while we resize and render
const tmpImg = document.createElement('img');
tmpImg.style = SCREENSHOT_PLACEHOLDER;
tmpImg.src = imageURL;
model._screenshot.placeHolder = model.el.appendChild(tmpImg);

// hide the main canvas
model.canvas.style.display = 'none';

// remember the main canvas original size, then resize it
model._screenshot.originalSize = model.size;
model.size = model._screenshot.size;
model._screenshot.size = null;

// process the resize
publicAPI.modified();

if (resetCamera) {
// If resetCamera was requested, we first save camera parameters
// from all the renderers, so we can restore them later
model._screenshot.cameras = model.renderable
.getRenderers()
.map((renderer) => {
const camera = renderer.getActiveCamera();
const params = camera.get(
'focalPoint',
'position',
'parallelScale'
);

return {
resetCameraFn: renderer.resetCamera,
restoreParamsFn: camera.set,
// "clone" the params so we don't keep refs to properties
arg: JSON.parse(JSON.stringify(params)),
};
});

// Perform the resetCamera() on each renderer only after capturing
// the params from all active cameras, in case there happen to be
// linked cameras among the renderers.
model._screenshot.cameras.forEach(({ resetCameraFn }) =>
resetCameraFn()
);
}

// Trigger a render at the custom size
publicAPI.traverseAllPasses();
}
});
});
};
Expand Down
114 changes: 114 additions & 0 deletions Sources/Rendering/WebGPU/BindGroup/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import macro from 'vtk.js/Sources/macro';

// ----------------------------------------------------------------------------
// vtkWebGPUBindGroup methods
// ----------------------------------------------------------------------------

function vtkWebGPUBindGroup(publicAPI, model) {
// Set our className
model.classHierarchy.push('vtkWebGPUBindGroup');

publicAPI.addBindable = (bindable) => {
// only add new bindables
for (let i = 0; i < model.bindables.length; i++) {
if (model.bindables[i] === bindable) {
return;
}
}
model.bindables.push(bindable);
publicAPI.modified();
};

publicAPI.getBindGroupLayout = (device) => {
const entries = [];
for (let i = 0; i < model.bindables.length; i++) {
const entry = model.bindables[i].getBindGroupLayoutEntry();
entry.binding = i;
entries.push(entry);
}
return device.getBindGroupLayout({ entries });
};

publicAPI.getBindGroup = (device) => {
// check mtime
let mtime = publicAPI.getMTime();
for (let i = 0; i < model.bindables.length; i++) {
const tm = model.bindables[i].getBindGroupTime();
mtime = tm > mtime ? tm : mtime;
}
if (mtime < model.bindGroupTime.getMTime()) {
return model.bindGroup;
}

const entries = [];
for (let i = 0; i < model.bindables.length; i++) {
const entry = model.bindables[i].getBindGroupEntry();
entry.binding = i;
entries.push(entry);
}

model.bindGroup = device.getHandle().createBindGroup({
layout: publicAPI.getBindGroupLayout(device),
entries,
});
model.bindGroupTime.modified();

return model.bindGroup;
};

publicAPI.getShaderCode = (pipeline) => {
const lines = [];
const bgroup = pipeline.getBindGroupLayoutCount(model.name);
for (let i = 0; i < model.bindables.length; i++) {
lines.push(model.bindables[i].getShaderCode(i, bgroup));
}
return lines.join('\n');
};
}

// ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------

const DEFAULT_VALUES = {
device: null,
handle: null,
name: null,
};

// ----------------------------------------------------------------------------

export function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);

// Object methods
macro.obj(publicAPI, model);

model.bindables = [];

model.bindGroupTime = {};
macro.obj(model.bindGroupTime, { mtime: 0 });

macro.get(publicAPI, model, [
'bindGroupTime',
'handle',
'sizeInBytes',
'usage',
]);
macro.setGet(publicAPI, model, [
'name',
'device',
'arrayInformation',
'sourceTime',
]);

vtkWebGPUBindGroup(publicAPI, model);
}

// ----------------------------------------------------------------------------

export const newInstance = macro.newInstance(extend);

// ----------------------------------------------------------------------------

export default { newInstance, extend };
7 changes: 0 additions & 7 deletions Sources/Rendering/WebGPU/Camera/api.md

This file was deleted.

3 changes: 3 additions & 0 deletions Sources/Rendering/WebGPU/Camera/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ function vtkWebGPUCamera(publicAPI, model) {
model.keyMatrices.scvc
);

mat4.invert(model.keyMatrices.pcsc, model.keyMatrices.scpc);

model.keyMatrixTime.modified();
}
return model.keyMatrices;
Expand Down Expand Up @@ -89,6 +91,7 @@ export function extend(publicAPI, model, initialValues = {}) {
model.keyMatrices = {
normalMatrix: new Float64Array(16),
vcpc: new Float64Array(16),
pcsc: new Float64Array(16),
wcvc: new Float64Array(16),
scpc: new Float64Array(16),
scvc: new Float64Array(16),
Expand Down
2 changes: 1 addition & 1 deletion Sources/Rendering/WebGPU/Device/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ function vtkWebGPUDevice(publicAPI, model) {
// create one and store it
const layout = model.handle.createBindGroupLayout(val);

// we actuall yonly store the stringified version
// we actually only store the stringified version
// as that is what we always compare against
model.bindGroupLayouts.push({ sval, layout });
return layout;
Expand Down
Loading

0 comments on commit 8319b43

Please sign in to comment.