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

feat: option to hide cross section background in 3D slice views #663

Open
wants to merge 17 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
2 changes: 2 additions & 0 deletions src/data_panel_layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export interface ViewerUIState
inputEventBindings: InputEventBindings;
crossSectionBackgroundColor: TrackableRGB;
perspectiveViewBackgroundColor: TrackableRGB;
hideCrossSectionBackgroundIn3D: TrackableBoolean;
}

export interface DataDisplayLayout extends RefCounted {
Expand Down Expand Up @@ -180,6 +181,7 @@ export function getCommonViewerState(viewer: ViewerUIState) {
selectedLayer: viewer.selectedLayer,
visibility: viewer.visibility,
scaleBarOptions: viewer.scaleBarOptions,
hideCrossSectionBackgroundIn3D: viewer.hideCrossSectionBackgroundIn3D,
};
}

Expand Down
4 changes: 4 additions & 0 deletions src/layer_group_viewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export interface LayerGroupViewerState {
visibleLayerRoles: WatchableSet<RenderLayerRole>;
crossSectionBackgroundColor: TrackableRGB;
perspectiveViewBackgroundColor: TrackableRGB;
hideCrossSectionBackgroundIn3D: TrackableBoolean;
}

export interface LayerGroupViewerOptions {
Expand Down Expand Up @@ -357,6 +358,9 @@ export class LayerGroupViewer extends RefCounted {
get enableAdaptiveDownsampling() {
return this.viewerState.enableAdaptiveDownsampling;
}
get hideCrossSectionBackgroundIn3D() {
return this.viewerState.hideCrossSectionBackgroundIn3D;
}
get showScaleBar() {
return this.viewerState.showScaleBar;
}
Expand Down
1 change: 1 addition & 0 deletions src/layer_groups_layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ function getCommonViewerState(viewer: Viewer) {
velocity: viewer.velocity.addRef(),
crossSectionBackgroundColor: viewer.crossSectionBackgroundColor,
perspectiveViewBackgroundColor: viewer.perspectiveViewBackgroundColor,
hideCrossSectionBackgroundIn3D: viewer.hideCrossSectionBackgroundIn3D,
};
}

Expand Down
20 changes: 16 additions & 4 deletions src/perspective_view/panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export interface PerspectiveViewerState extends RenderedDataViewerState {
showSliceViewsCheckbox?: boolean;
crossSectionBackgroundColor: TrackableRGB;
perspectiveViewBackgroundColor: TrackableRGB;
hideCrossSectionBackgroundIn3D: TrackableBoolean;
rpc: RPC;
}

Expand Down Expand Up @@ -270,6 +271,7 @@ class PerspectiveViewState extends PerspectiveViewStateBase {

export class PerspectivePanel extends RenderedDataPanel {
declare viewer: PerspectiveViewerState;
sliceViewRenderHelper: SliceViewRenderHelper;

projectionParameters: Owned<DerivedProjectionParameters>;

Expand Down Expand Up @@ -321,10 +323,6 @@ export class PerspectivePanel extends RenderedDataPanel {
);

private axesLineHelper = this.registerDisposer(AxesLineHelper.get(this.gl));
sliceViewRenderHelper = this.registerDisposer(
SliceViewRenderHelper.get(this.gl, perspectivePanelEmit),
);

protected offscreenFramebuffer = this.registerDisposer(
new FramebufferConfiguration(this.gl, {
colorBuffers: [
Expand Down Expand Up @@ -406,6 +404,15 @@ export class PerspectivePanel extends RenderedDataPanel {
viewer: PerspectiveViewerState,
) {
super(context, element, viewer);
this.sliceViewRenderHelper = this.registerDisposer(
SliceViewRenderHelper.get(
this.gl,
perspectivePanelEmit,
this.viewer,
true /*perspectivePanel*/,
),
);

this.projectionParameters = this.registerDisposer(
new DerivedProjectionParameters({
navigationState: this.navigationState,
Expand Down Expand Up @@ -580,6 +587,11 @@ export class PerspectivePanel extends RenderedDataPanel {
this.registerDisposer(
viewer.wireFrame.changed.add(() => this.scheduleRedraw()),
);
this.registerDisposer(
viewer.hideCrossSectionBackgroundIn3D.changed.add(() =>
this.scheduleRedraw(),
),
);
this.sliceViews.changed.add(() => this.scheduleRedraw());
}

Expand Down
92 changes: 75 additions & 17 deletions src/sliceview/frontend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import type {
DisplayDimensionRenderInfo,
NavigationState,
} from "#src/navigation_state.js";
import type { PerspectiveViewerState } from "#src/perspective_view/panel.js";
import { updateProjectionParametersFromInverseViewAndProjection } from "#src/projection_parameters.js";
import type {
ChunkDisplayTransformParameters,
Expand Down Expand Up @@ -60,6 +61,7 @@ import {
SliceViewProjectionParameters,
} from "#src/sliceview/base.js";
import { ChunkLayout } from "#src/sliceview/chunk_layout.js";
import type { SliceViewerState } from "#src/sliceview/panel.js";
import { SliceViewRenderLayer } from "#src/sliceview/renderlayer.js";
import type { WatchableValueInterface } from "#src/trackable_value.js";
import type { Borrowed, Disposer, Owned } from "#src/util/disposable.js";
Expand All @@ -71,6 +73,8 @@ import { getObjectId } from "#src/util/object_id.js";
import { NullarySignal } from "#src/util/signal.js";
import { withSharedVisibility } from "#src/visibility_priority/frontend.js";
import type { GL } from "#src/webgl/context.js";
import type { ParameterizedContextDependentShaderGetter } from "#src/webgl/dynamic_shader.js";
import { parameterizedContextDependentShaderGetter } from "#src/webgl/dynamic_shader.js";
import type { HistogramSpecifications } from "#src/webgl/empirical_cdf.js";
import { TextureHistogramGenerator } from "#src/webgl/empirical_cdf.js";
import type { TextureBuffer } from "#src/webgl/offscreen.js";
Expand All @@ -79,8 +83,7 @@ import {
FramebufferConfiguration,
makeTextureBuffers,
} from "#src/webgl/offscreen.js";
import type { ShaderModule, ShaderProgram } from "#src/webgl/shader.js";
import { ShaderBuilder } from "#src/webgl/shader.js";
import type { ShaderModule, ShaderBuilder } from "#src/webgl/shader.js";
import { getSquareCornersBuffer } from "#src/webgl/square_corners_buffer.js";
import type { RPC } from "#src/worker_rpc.js";
import { registerSharedObjectOwner } from "#src/worker_rpc.js";
Expand Down Expand Up @@ -719,41 +722,83 @@ export class SliceViewChunk extends Chunk {
*/
export class SliceViewRenderHelper extends RefCounted {
private copyVertexPositionsBuffer;
private shader: ShaderProgram;

private textureCoordinateAdjustment = new Float32Array(4);
private shaderGetter: ParameterizedContextDependentShaderGetter<
{ emitter: ShaderModule; isProjection: boolean },
boolean
>;

constructor(
public gl: GL,
defineShader(
builder: ShaderBuilder,
hideTransparent: boolean,
isProjection: boolean,
emitter: ShaderModule,
) {
super();
this.copyVertexPositionsBuffer = getSquareCornersBuffer(this.gl);

const builder = new ShaderBuilder(gl);
builder.addVarying("vec2", "vTexCoord");
builder.addUniform("sampler2D", "uSampler");
builder.addInitializer((shader) => {
gl.uniform1i(shader.uniform("uSampler"), 0);
this.gl.uniform1i(shader.uniform("uSampler"), 0);
});
builder.addUniform("vec4", "uColorFactor");
builder.addUniform("vec4", "uBackgroundColor");
builder.addUniform("mat4", "uProjectionMatrix");
builder.addUniform("vec4", "uTextureCoordinateAdjustment");
builder.require(emitter);
builder.setFragmentMain(`
const glsl_fragmentMainStart = `
vec4 sampledColor = texture(uSampler, vTexCoord);
if (sampledColor.a == 0.0) {
if (sampledColor.a == 0.0) {`;
let glsl_fragmentMainEnd: string;
if (hideTransparent && isProjection) {
glsl_fragmentMainEnd = `
discard;
}
else {
emit(sampledColor * uColorFactor, 0u);
}
`;
} else {
glsl_fragmentMainEnd = `
sampledColor = uBackgroundColor;
}
emit(sampledColor * uColorFactor, 0u);
`);
`;
}
builder.setFragmentMain(`${glsl_fragmentMainStart}${glsl_fragmentMainEnd}`);
builder.addAttribute("vec4", "aVertexPosition");
builder.setVertexMain(`
vTexCoord = uTextureCoordinateAdjustment.xy + 0.5 * (aVertexPosition.xy + 1.0) * uTextureCoordinateAdjustment.zw;
gl_Position = uProjectionMatrix * aVertexPosition;
`);
this.shader = this.registerDisposer(builder.build());
}

constructor(
public gl: GL,
private emitter: ShaderModule,
private viewer: SliceViewerState | PerspectiveViewerState,
private isProjection: boolean,
) {
super();

this.copyVertexPositionsBuffer = getSquareCornersBuffer(this.gl);
this.shaderGetter = parameterizedContextDependentShaderGetter(
this,
this.gl,
{
memoizeKey: "sliceview/SliceViewRenderHelper",
parameters: this.viewer.hideCrossSectionBackgroundIn3D,
getContextKey: ({ emitter, isProjection }) =>
`${getObjectId(emitter)}${isProjection}`,
defineShader: (builder, context, hideTransparent) => {
this.defineShader(
builder,
hideTransparent,
context.isProjection,
context.emitter,
);
},
},
);
}

draw(
Expand All @@ -766,11 +811,19 @@ gl_Position = uProjectionMatrix * aVertexPosition;
xEnd: number,
yEnd: number,
) {
const { gl, shader, textureCoordinateAdjustment } = this;
const { gl, textureCoordinateAdjustment } = this;
textureCoordinateAdjustment[0] = xStart;
textureCoordinateAdjustment[1] = yStart;
textureCoordinateAdjustment[2] = xEnd - xStart;
textureCoordinateAdjustment[3] = yEnd - yStart;
const shaderResult = this.shaderGetter({
emitter: this.emitter,
isProjection: this.isProjection,
});
const shader = shaderResult.shader;
if (shader === null) {
throw new Error("Shader compilation failed in SliceViewRenderHelper.");
}
shader.bind();
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
Expand Down Expand Up @@ -799,10 +852,15 @@ gl_Position = uProjectionMatrix * aVertexPosition;
gl.bindTexture(gl.TEXTURE_2D, null);
}

static get(gl: GL, emitter: ShaderModule) {
static get(
gl: GL,
emitter: ShaderModule,
viewer: SliceViewerState | PerspectiveViewerState,
isProjection: boolean,
) {
return gl.memoize.get(
`sliceview/SliceViewRenderHelper:${getObjectId(emitter)}`,
() => new SliceViewRenderHelper(gl, emitter),
() => new SliceViewRenderHelper(gl, emitter, viewer, isProjection),
);
}
}
Expand Down
19 changes: 16 additions & 3 deletions src/sliceview/panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export interface SliceViewerState extends RenderedDataViewerState {
wireFrame: TrackableBoolean;
scaleBarOptions: TrackableScaleBarOptions;
crossSectionBackgroundColor: TrackableRGB;
hideCrossSectionBackgroundIn3D: TrackableBoolean;
}

export enum OffscreenTextures {
Expand Down Expand Up @@ -101,11 +102,10 @@ const tempVec4 = vec4.create();

export class SliceViewPanel extends RenderedDataPanel {
declare viewer: SliceViewerState;
private sliceViewRenderHelper;

private axesLineHelper = this.registerDisposer(AxesLineHelper.get(this.gl));
private sliceViewRenderHelper = this.registerDisposer(
SliceViewRenderHelper.get(this.gl, sliceViewPanelEmitColor),
);

private colorFactor = vec4.fromValues(1, 1, 1, 1);
private pickIDs = new PickIDManager();

Expand Down Expand Up @@ -167,7 +167,20 @@ export class SliceViewPanel extends RenderedDataPanel {
viewer: SliceViewerState,
) {
super(context, element, viewer);

this.sliceViewRenderHelper = this.registerDisposer(
SliceViewRenderHelper.get(
this.gl,
sliceViewPanelEmitColor,
this.viewer,
false /*sliceViewPanel*/,
),
);

viewer.wireFrame.changed.add(() => this.scheduleRedraw());
viewer.hideCrossSectionBackgroundIn3D.changed.add(() =>
this.scheduleRedraw(),
);
registerActionListener(
element,
"rotate-via-mouse-drag",
Expand Down
1 change: 1 addition & 0 deletions src/ui/default_input_event_bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export function getDefaultGlobalBindings() {
map.set("keyv", "toggle-default-annotations");
map.set("keya", "toggle-axis-lines");
map.set("keyo", "toggle-orthographic-projection");
map.set("keyt", "toggle-hide-cross-section-background-3d");

for (let i = 1; i <= 9; ++i) {
map.set("digit" + i, "toggle-layer-" + i);
Expand Down
4 changes: 4 additions & 0 deletions src/ui/viewer_settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ export class ViewerSettingsPanel extends SidePanel {
addCheckbox("Show axis lines", viewer.showAxisLines);
addCheckbox("Show scale bar", viewer.showScaleBar);
addCheckbox("Show cross sections in 3-d", viewer.showPerspectiveSliceViews);
addCheckbox(
"Hide sections background 3-d",
viewer.hideCrossSectionBackgroundIn3D,
);
addCheckbox("Show default annotations", viewer.showDefaultAnnotations);
addCheckbox(
"Show chunk statistics",
Expand Down
8 changes: 8 additions & 0 deletions src/viewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,10 @@ class TrackableViewerState extends CompoundTrackable {
this.add("showDefaultAnnotations", viewer.showDefaultAnnotations);

this.add("showSlices", viewer.showPerspectiveSliceViews);
this.add(
"hideCrossSectionBackgroundIn3D",
viewer.hideCrossSectionBackgroundIn3D,
);
this.add(
"gpuMemoryLimit",
viewer.dataContext.chunkQueueManager.capacities.gpuMemory.sizeLimit,
Expand Down Expand Up @@ -469,6 +473,7 @@ export class Viewer extends RefCounted implements ViewerState {
enableAdaptiveDownsampling = new TrackableBoolean(true, true);
showScaleBar = new TrackableBoolean(true, true);
showPerspectiveSliceViews = new TrackableBoolean(true, true);
hideCrossSectionBackgroundIn3D = new TrackableBoolean(true, true);
visibleLayerRoles = allRenderLayerRoles();
showDefaultAnnotations = new TrackableBoolean(true, true);
crossSectionBackgroundColor = new TrackableRGB(
Expand Down Expand Up @@ -1119,6 +1124,9 @@ export class Viewer extends RefCounted implements ViewerState {
this.showPerspectiveSliceViews.toggle(),
);
this.bindAction("toggle-show-statistics", () => this.showStatistics());
this.bindAction("toggle-hide-cross-section-background-3d", () =>
this.hideCrossSectionBackgroundIn3D.toggle(),
);
}

toggleHelpPanel() {
Expand Down
Loading