Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VolumeMapper & OpenGLTexture: support updating image regions #3077

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
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
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ test.onlyIfWebGL('Test ImageDataOutlineFilter', (t) => {
return promise;
}

[
return [
testImageDataOutlineAsLines,
testImageDataOutlineAsFaces,
gc.releaseResources,
Expand Down
22 changes: 20 additions & 2 deletions Sources/Rendering/Core/ImageResliceMapper/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import vtkAbstractImageMapper, {
IAbstractImageMapperInitialValues,
} from '../AbstractImageMapper';
import vtkImageData from '../../../Common/DataModel/ImageData';
import vtkPlane from '../../../Common/DataModel/Plane';
import vtkPolyData from '../../../Common/DataModel/PolyData';
import { Bounds, Nullable, Vector3 } from '../../../types';
import { Bounds, Extent } from '../../../types';
import { SlabTypes } from './Constants';

interface ICoincidentTopology {
Expand Down Expand Up @@ -234,6 +233,25 @@ export interface vtkImageResliceMapper extends vtkAbstractImageMapper {
* @param {vtkPolyData} slicePolyData The polydata to slice the volume with. Default: null
*/
setSlicePolyData(slicePolyData: vtkPolyData): boolean;

/**
* Tells the mapper to only update the specified extents.
*
* If there are zero extents, the mapper updates the entire volume texture.
* Otherwise, the mapper will only update the texture by the specified extents
* during the next render call.
*
* This array is cleared after a successful render.
* @param extents
*/
setUpdatedExtents(extents: Extent[]): boolean;

/**
* Retrieves the updated extents.
*
* This array is cleared after every successful render.
*/
getUpdatedExtents(): Extent[];
}

/**
Expand Down
9 changes: 6 additions & 3 deletions Sources/Rendering/Core/ImageResliceMapper/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,20 @@ function vtkImageResliceMapper(publicAPI, model) {
// Object factory
// ----------------------------------------------------------------------------

const DEFAULT_VALUES = {
const defaultValues = (initialValues) => ({
slabThickness: 0.0,
slabTrapezoidIntegration: 0,
slabType: SlabTypes.MEAN,
slicePlane: null,
slicePolyData: null,
};
updatedExtents: [],
...initialValues,
});

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

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

// Build VTK API
vtkAbstractImageMapper.extend(publicAPI, model, initialValues);
Expand All @@ -61,6 +63,7 @@ export function extend(publicAPI, model, initialValues = {}) {
'slabType',
'slicePlane',
'slicePolyData',
'updatedExtents',
]);
CoincidentTopologyHelper.implementCoincidentTopologyMethods(publicAPI, model);

Expand Down
35 changes: 34 additions & 1 deletion Sources/Rendering/Core/VolumeMapper/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import vtkPiecewiseFunction from '../../../Common/DataModel/PiecewiseFunction';
import { Bounds, Range } from '../../../types';
import { Bounds, Range, Extent } from '../../../types';
import vtkAbstractMapper3D, {
IAbstractMapper3DInitialValues,
} from '../AbstractMapper3D';
Expand Down Expand Up @@ -281,6 +281,39 @@ export interface vtkVolumeMapper extends vtkAbstractMapper3D {
*/
setLAOKernelRadius(LAOKernelRadius: number): void;

/**
* Set kernel size for local ambient occlusion. It specifies the number of rays that are randomly sampled in the hemisphere.
* Value is clipped between 1 and 32.
* @param LAOKernelSize
*/
setLAOKernelSize(LAOKernelSize: number): void;

/**
* Set kernel radius for local ambient occlusion. It specifies the number of samples that are considered on each random ray.
* Value must be greater than or equal to 1.
* @param LAOKernelRadius
*/
setLAOKernelRadius(LAOKernelRadius: number): void;

/**
* Tells the mapper to only update the specified extents.
*
* If there are zero extents, the mapper updates the entire volume texture.
* Otherwise, the mapper will only update the texture by the specified extents
* during the next render call.
*
* This array is cleared after a successful render.
* @param extents
*/
setUpdatedExtents(extents: Extent[]): boolean;

/**
* Retrieves the updated extents.
*
* This array is cleared after every successful render.
*/
getUpdatedExtents(): Extent[];

/**
*
*/
Expand Down
9 changes: 6 additions & 3 deletions Sources/Rendering/Core/VolumeMapper/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ function vtkVolumeMapper(publicAPI, model) {
// ----------------------------------------------------------------------------

// TODO: what values to use for averageIPScalarRange to get GLSL to use max / min values like [-Math.inf, Math.inf]?
const DEFAULT_VALUES = {
const defaultValues = (initialValues) => ({
bounds: [1, -1, 1, -1, 1, -1],
sampleDistance: 1.0,
imageSampleDistance: 1.0,
Expand All @@ -163,12 +163,14 @@ const DEFAULT_VALUES = {
localAmbientOcclusion: false,
LAOKernelSize: 15,
LAOKernelRadius: 7,
};
updatedExtents: [],
...initialValues,
});

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

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

vtkAbstractMapper3D.extend(publicAPI, model, initialValues);

Expand All @@ -190,6 +192,7 @@ export function extend(publicAPI, model, initialValues = {}) {
'localAmbientOcclusion',
'LAOKernelSize',
'LAOKernelRadius',
'updatedExtents',
]);

macro.setGetArray(publicAPI, model, ['ipScalarRange'], 2);
Expand Down
33 changes: 24 additions & 9 deletions Sources/Rendering/OpenGL/ImageResliceMapper/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,24 +220,39 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {

const tex = model._openGLRenderWindow.getGraphicsResourceForObject(scalars);
const reBuildTex = !tex?.vtkObj?.getHandle() || tex?.hash !== toString;
if (reBuildTex) {
if (!model.openGLTexture) {
model.openGLTexture = vtkOpenGLTexture.newInstance();
model.openGLTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
}
// Build the image scalar texture
const dims = image.getDimensions();
// Use norm16 for the 3D texture if the extension is available
const hasUpdatedExtents = !!model.renderable.getUpdatedExtents().length;

if (!model.openGLTexture) {
model.openGLTexture = vtkOpenGLTexture.newInstance();
model.openGLTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
}
Comment on lines +225 to +228
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code has changed and there will be conflicts here
Look at how shared textures are handled in the mappers to make sure you do not break the resource sharing or create memory leaks


// reset the scalars texture if there are no updated extents
if (reBuildTex && !hasUpdatedExtents) {
// Use norm16 for scalar texture if the extension is available
model.openGLTexture.setOglNorm16Ext(
model.context.getExtension('EXT_texture_norm16')
);

model.openGLTexture.releaseGraphicsResources(model._openGLRenderWindow);
model.openGLTexture.resetFormatAndType();
}

if (reBuildTex || hasUpdatedExtents) {
// If hasUpdatedExtents, then the texture is partially updated
const updatedExtents = [...model.renderable.getUpdatedExtents()];
// clear the array to acknowledge the update.
model.renderable.setUpdatedExtents([]);

// Build the image scalar texture
const dims = image.getDimensions();
model.openGLTexture.create3DFilterableFromDataArray(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should consider transitioning from positional arguments to object-based arguments. It would make it easier to track which arguments have default values and which ones need to be added. It wouldn't be a breaking change since this are not really public APIs that people use IMO

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good idea to consider. Probably best to wrap up such a change with the next major version increment anyways to be safe from a public API standpoint (even if it's mostly used internally).

dims[0],
dims[1],
dims[2],
scalars
scalars,
false,
updatedExtents
);
if (scalars) {
model._openGLRenderWindow.setGraphicsResourceForObject(
Expand Down
23 changes: 19 additions & 4 deletions Sources/Rendering/OpenGL/Texture/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Wrap, Filter } from './Constants';
import vtkOpenGLRenderWindow from '../RenderWindow';
import { Nullable } from '../../../types';
import { Extent, Nullable } from '../../../types';
import { VtkDataTypes } from '../../../Common/Core/DataArray';
import { vtkViewNode } from '../../../Rendering/SceneGraph/ViewNode';
import { vtkObject } from '../../../interfaces';
Expand Down Expand Up @@ -256,12 +256,16 @@ export interface vtkOpenGLTexture extends vtkViewNode {

/**
* Creates a 3D texture from raw data.
*
* updatedExtents is currently incompatible with webgl1, since there's no extent scaling.
*
* @param width The width of the texture.
* @param height The height of the texture.
* @param depth The depth of the texture.
* @param numComps The number of components in the texture.
* @param dataType The data type of the texture.
* @param data The raw data for the texture.
* @param updatedExtents Only update the specified extents (default: [])
* @returns {boolean} True if the texture was successfully created, false otherwise.
*/
create3DFromRaw(
Expand All @@ -270,18 +274,23 @@ export interface vtkOpenGLTexture extends vtkViewNode {
depth: number,
numComps: number,
dataType: VtkDataTypes,
data: any
data: any,
updatedExtents?: Extent[]
): boolean;

/**
* Creates a 3D filterable texture from raw data, with a preference for size over accuracy if necessary.
*
* updatedExtents is currently incompatible with webgl1, since there's no extent scaling.
*
* @param width The width of the texture.
* @param height The height of the texture.
* @param depth The depth of the texture.
* @param numComps The number of components in the texture.
* @param dataType The data type of the texture.
* @param values The raw data for the texture.
* @param preferSizeOverAccuracy Whether to prefer texture size over accuracy.
* @param updatedExtents Only update the specified extents (default: [])
* @returns {boolean} True if the texture was successfully created, false otherwise.
*/
create3DFilterableFromRaw(
Expand All @@ -291,24 +300,30 @@ export interface vtkOpenGLTexture extends vtkViewNode {
numComps: number,
dataType: VtkDataTypes,
values: any,
preferSizeOverAccuracy: boolean
preferSizeOverAccuracy: boolean,
updatedExtents?: Extent[]
): boolean;

/**
* Creates a 3D filterable texture from a data array, with a preference for size over accuracy if necessary.
*
* updatedExtents is currently incompatible with webgl1, since there's no extent scaling.
*
* @param width The width of the texture.
* @param height The height of the texture.
* @param depth The depth of the texture.
* @param dataArray The data array to use for the texture.
* @param preferSizeOverAccuracy Whether to prefer texture size over accuracy.
* @param updatedExtents Only update the specified extents (default: [])
* @returns {boolean} True if the texture was successfully created, false otherwise.
*/
create3DFilterableFromDataArray(
width: number,
height: number,
depth: number,
dataArray: any,
preferSizeOverAccuracy: boolean
preferSizeOverAccuracy: boolean,
updatedExtents?: Extent[]
): boolean;

/**
Expand Down
Loading
Loading