diff --git a/src/mol-canvas3d/camera.ts b/src/mol-canvas3d/camera.ts index 01d54d6deacc14bff2f11bc204ada3e974d2ae06..2a0792fd5754ea5385e4999396e03a99fc5f78e2 100644 --- a/src/mol-canvas3d/camera.ts +++ b/src/mol-canvas3d/camera.ts @@ -10,9 +10,21 @@ import { Viewport, cameraProject, cameraUnproject } from './camera/util'; import { CameraTransitionManager } from './camera/transition'; import { BehaviorSubject } from 'rxjs'; -export { Camera }; +export { ICamera, Camera }; + +interface ICamera { + readonly viewport: Viewport, + readonly view: Mat4, + readonly projection: Mat4, + readonly state: Readonly<Camera.Snapshot>, + readonly viewOffset: Camera.ViewOffset, + readonly far: number, + readonly near: number, + readonly fogFar: number, + readonly fogNear: number, +} -class Camera { +class Camera implements ICamera { readonly view: Mat4 = Mat4.identity(); readonly projection: Mat4 = Mat4.identity(); readonly projectionView: Mat4 = Mat4.identity(); @@ -26,12 +38,7 @@ class Camera { readonly viewport: Viewport; readonly state: Readonly<Camera.Snapshot> = Camera.createDefaultSnapshot(); - readonly viewOffset: Camera.ViewOffset = { - enabled: false, - fullWidth: 1, fullHeight: 1, - offsetX: 0, offsetY: 0, - width: 1, height: 1 - } + readonly viewOffset = Camera.ViewOffset(); near = 1 far = 10000 @@ -157,6 +164,15 @@ namespace Camera { height: number } + export function ViewOffset(): ViewOffset { + return { + enabled: false, + fullWidth: 1, fullHeight: 1, + offsetX: 0, offsetY: 0, + width: 1, height: 1 + }; + } + export function setViewOffset(out: ViewOffset, fullWidth: number, fullHeight: number, offsetX: number, offsetY: number, width: number, height: number) { out.fullWidth = fullWidth; out.fullHeight = fullHeight; @@ -166,6 +182,16 @@ namespace Camera { out.height = height; } + export function copyViewOffset(out: ViewOffset, view: ViewOffset) { + out.enabled = view.enabled; + out.fullWidth = view.fullWidth; + out.fullHeight = view.fullHeight; + out.offsetX = view.offsetX; + out.offsetY = view.offsetY; + out.width = view.width; + out.height = view.height; + } + export function createDefaultSnapshot(): Snapshot { return { mode: 'perspective', diff --git a/src/mol-canvas3d/camera/stereo.ts b/src/mol-canvas3d/camera/stereo.ts new file mode 100644 index 0000000000000000000000000000000000000000..8aa206b48cd63794cfd139d61a85be91b1753379 --- /dev/null +++ b/src/mol-canvas3d/camera/stereo.ts @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + * + * Adapted from three.js, The MIT License, Copyright © 2010-2020 three.js authors + */ + +import { Mat4 } from '../../mol-math/linear-algebra'; +import { ParamDefinition } from '../../mol-util/param-definition'; +import { Camera, ICamera } from '../camera'; +import { Viewport } from './util'; + +export class StereoCamera { + readonly left: ICamera = new EyeCamera(); + readonly right: ICamera = new EyeCamera(); + + update(camera: Camera, params: StereoCameraParams) { + update(camera, params, this.left as EyeCamera, this.right as EyeCamera); + } +} + +class EyeCamera implements ICamera { + viewport = Viewport.create(0, 0, 0, 0); + view = Mat4(); + projection = Mat4(); + state: Readonly<Camera.Snapshot> = Camera.createDefaultSnapshot(); + viewOffset: Readonly<Camera.ViewOffset> = Camera.ViewOffset(); + far: number = 0; + near: number = 0; + fogFar: number = 0; + fogNear: number = 0; +} + +export const StereoCameraParams = { + aspect: ParamDefinition.Numeric(1, { min: 0.1, max: 3, step: 0.01 }), + eyeSeparation: ParamDefinition.Numeric(0.064, { min: 0.01, max: 0.5, step: 0.001 }), + focus: ParamDefinition.Numeric(10, { min: 1, max: 100, step: 0.01 }), +}; +export type StereoCameraParams = ParamDefinition.Values<typeof StereoCameraParams> + +const eyeLeft = Mat4.identity(), eyeRight = Mat4.identity(); + +function update(camera: ICamera, params: StereoCameraParams, left: EyeCamera, right: EyeCamera) { + // Copy the states + + Viewport.copy(left.viewport, camera.viewport); + Mat4.copy(left.view, camera.view); + Mat4.copy(left.projection, camera.projection); + Camera.copySnapshot(left.state, camera.state); + Camera.copyViewOffset(left.viewOffset, camera.viewOffset); + left.far = camera.far; + left.near = camera.near; + left.fogFar = camera.fogFar; + left.fogNear = camera.fogNear; + + Viewport.copy(right.viewport, camera.viewport); + Mat4.copy(right.view, camera.view); + Mat4.copy(right.projection, camera.projection); + Camera.copySnapshot(right.state, camera.state); + Camera.copyViewOffset(right.viewOffset, camera.viewOffset); + right.far = camera.far; + right.near = camera.near; + right.fogFar = camera.fogFar; + right.fogNear = camera.fogNear; + + // update the view offsets + let w = (camera.viewport.width / 2) | 0; + + left.viewport.width = w; + right.viewport.x = w; + right.viewport.width -= w; + + // update the projection and view matrices + + const eyeSepHalf = params.eyeSeparation / 2; + const eyeSepOnProjection = eyeSepHalf * camera.near / params.focus; + const ymax = (camera.near * Math.tan(camera.state.fov * 0.5)) / /* cache.zoom */ 1; + let xmin, xmax; + + // translate xOffset + + eyeLeft[12] = - eyeSepHalf; + eyeRight[12] = eyeSepHalf; + + // for left eye + + xmin = - ymax * params.aspect + eyeSepOnProjection; + xmax = ymax * params.aspect + eyeSepOnProjection; + + left.projection[0] = 2 * camera.near / (xmax - xmin); + left.projection[8] = (xmax + xmin) / (xmax - xmin); + + Mat4.mul(left.view, left.view, eyeLeft); + + // for right eye + + xmin = - ymax * params.aspect - eyeSepOnProjection; + xmax = ymax * params.aspect - eyeSepOnProjection; + + right.projection[0] = 2 * camera.near / (xmax - xmin); + right.projection[8] = (xmax + xmin) / (xmax - xmin); + + Mat4.mul(right.view, right.view, eyeRight); +} \ No newline at end of file diff --git a/src/mol-canvas3d/canvas3d.ts b/src/mol-canvas3d/canvas3d.ts index 2a76be15e99d5f0310709719842e3da676fe7560..325eb6a9035ceb1cb074e23e15ec5165adaa9d36 100644 --- a/src/mol-canvas3d/canvas3d.ts +++ b/src/mol-canvas3d/canvas3d.ts @@ -34,6 +34,7 @@ import { isDebugMode } from '../mol-util/debug'; import { CameraHelperParams } from './helper/camera-helper'; import { produce } from 'immer'; import { HandleHelper, HandleHelperParams } from './helper/handle-helper'; +import { StereoCamera, StereoCameraParams } from './camera/stereo'; export const Canvas3DParams = { camera: PD.Group({ @@ -60,6 +61,10 @@ export const Canvas3DParams = { height: PD.Numeric(128) }) }), + stereo: PD.MappedStatic('off', { + on: PD.Group(StereoCameraParams), + off: PD.Group({}) + }, { cycle: true }), cameraResetDurationMs: PD.Numeric(250, { min: 0, max: 1000, step: 1 }, { description: 'The time it takes to reset the camera.' }), transparentBackground: PD.Boolean(false), @@ -207,6 +212,7 @@ namespace Canvas3D { fog: p.cameraFog.name === 'on' ? p.cameraFog.params.intensity : 0, clipFar: p.cameraClipping.far }, { x, y, width, height }, { pixelScale: attribs.pixelScale }); + const stereoCamera = new StereoCamera(); const controls = TrackballControls.create(input, camera, p.trackball); const renderer = Renderer.create(webgl, p.renderer); @@ -214,10 +220,13 @@ namespace Canvas3D { const handleHelper = new HandleHelper(webgl, p.handle); const interactionHelper = new Canvas3dInteractionHelper(identify, getLoci, input, camera); - const drawPass = new DrawPass(webgl, renderer, scene, camera, debugHelper, handleHelper, { + const drawPass = new DrawPass(webgl, renderer, scene, { standard: camera, stereo: stereoCamera }, debugHelper, handleHelper, { cameraHelper: p.camera.helper }); - const pickPass = new PickPass(webgl, renderer, scene, camera, handleHelper, attribs.pickScale || 0.25, drawPass); + drawPass.isStereo = p.stereo.name === 'on'; + const pickPass = new PickPass(webgl, renderer, scene, camera, stereoCamera, handleHelper, attribs.pickScale || 0.25, drawPass); + pickPass.isStereo = p.stereo.name === 'on'; + const postprocessing = new PostprocessingPass(webgl, camera, drawPass, p.postprocessing); const multiSample = new MultiSamplePass(webgl, camera, drawPass, postprocessing, p.multiSample); @@ -275,9 +284,14 @@ namespace Canvas3D { const cameraChanged = camera.update(); const multiSampleChanged = multiSample.update(force || cameraChanged); + const isStereo = p.stereo.name === 'on'; + if (force || cameraChanged || multiSampleChanged) { + if ((force || cameraChanged) && p.stereo.name === 'on') stereoCamera.update(camera, p.stereo.params); + renderer.setViewport(x, y, width, height); - if (multiSample.enabled) { + // TODO: support stereo rendering in multisampling + if (!isStereo && multiSample.enabled) { multiSample.render(true, p.transparentBackground); } else { const toDrawingBuffer = !postprocessing.enabled && scene.volumes.renderables.length === 0; @@ -490,6 +504,7 @@ namespace Canvas3D { cameraResetDurationMs: p.cameraResetDurationMs, transparentBackground: p.transparentBackground, viewport: p.viewport, + stereo: p.stereo, postprocessing: { ...postprocessing.props }, multiSample: { ...multiSample.props }, @@ -595,6 +610,11 @@ namespace Canvas3D { p.viewport = props.viewport; handleResize(); } + if (props.stereo !== undefined) { + p.stereo = props.stereo; + pickPass.isStereo = p.stereo.name === 'on'; + drawPass.isStereo = p.stereo.name === 'on'; + } if (props.postprocessing) postprocessing.setProps(props.postprocessing); if (props.multiSample) multiSample.setProps(props.multiSample); diff --git a/src/mol-canvas3d/helper/camera-helper.ts b/src/mol-canvas3d/helper/camera-helper.ts index 7b5d405210fe1b070a8810ddace253a0cb3eda27..2640a24f5375bcbeb714b740e8271df3666b96ce 100644 --- a/src/mol-canvas3d/helper/camera-helper.ts +++ b/src/mol-canvas3d/helper/camera-helper.ts @@ -6,7 +6,7 @@ import { WebGLContext } from '../../mol-gl/webgl/context'; import Scene from '../../mol-gl/scene'; -import { Camera } from '../camera'; +import { Camera, ICamera } from '../camera'; import { MeshBuilder } from '../../mol-geo/geometry/mesh/mesh-builder'; import { Vec3, Mat4 } from '../../mol-math/linear-algebra'; import { addSphere } from '../../mol-geo/geometry/mesh/builder/sphere'; @@ -86,7 +86,7 @@ export class CameraHelper { return this.props.axes.name === 'on'; } - update(camera: Camera) { + update(camera: ICamera) { if (!this.renderObject) return; updateCamera(this.camera, camera.viewport, camera.viewOffset); diff --git a/src/mol-canvas3d/passes/draw.ts b/src/mol-canvas3d/passes/draw.ts index 9db4b947753caf77dda5dcce9b0f365ca5dcfd42..aabc493527e698714f8dc2c67408725dc3203f12 100644 --- a/src/mol-canvas3d/passes/draw.ts +++ b/src/mol-canvas3d/passes/draw.ts @@ -10,7 +10,7 @@ import Renderer from '../../mol-gl/renderer'; import Scene from '../../mol-gl/scene'; import { BoundingSphereHelper } from '../helper/bounding-sphere-helper'; import { Texture } from '../../mol-gl/webgl/texture'; -import { Camera } from '../camera'; +import { ICamera } from '../camera'; import { CameraHelper, CameraHelperParams } from '../helper/camera-helper'; import { ParamDefinition as PD } from '../../mol-util/param-definition'; import { HandleHelper } from '../helper/handle-helper'; @@ -21,6 +21,7 @@ import { ShaderCode } from '../../mol-gl/shader-code'; import { createComputeRenderItem } from '../../mol-gl/webgl/render-item'; import { ValueCell } from '../../mol-util'; import { Vec2 } from '../../mol-math/linear-algebra'; +import { StereoCamera } from '../camera/stereo'; import quad_vert from '../../mol-gl/shader/quad.vert'; import depthMerge_frag from '../../mol-gl/shader/depth-merge.frag'; @@ -70,9 +71,11 @@ export class DrawPass { private depthTextureVolumes: Texture private depthMerge: DepthMergeRenderable - constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private debugHelper: BoundingSphereHelper, private handleHelper: HandleHelper, props: Partial<DrawPassProps> = {}) { + isStereo = false + + constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: { standard: ICamera, stereo?: StereoCamera }, private debugHelper: BoundingSphereHelper, private handleHelper: HandleHelper, props: Partial<DrawPassProps> = {}) { const { extensions, resources } = webgl; - const { width, height } = camera.viewport; + const { width, height } = camera.standard.viewport; this.colorTarget = webgl.createRenderTarget(width, height); this.packedDepth = !extensions.depthTexture; @@ -125,25 +128,35 @@ export class DrawPass { } render(toDrawingBuffer: boolean, transparentBackground: boolean) { - const { x, y, width, height } = this.camera.viewport; + if (this.isStereo && this.camera.stereo) { + this._render(this.camera.stereo.left, toDrawingBuffer, transparentBackground); + this._render(this.camera.stereo.right, toDrawingBuffer, transparentBackground); + } else { + this._render(this.camera.standard, toDrawingBuffer, transparentBackground); + } + } + + private _render(camera: ICamera, toDrawingBuffer: boolean, transparentBackground: boolean) { + const { x, y, width, height } = camera.viewport; if (toDrawingBuffer) { this.webgl.unbindFramebuffer(); this.renderer.setViewport(x, y, width, height); } else { this.colorTarget.bind(); - this.renderer.setViewport(0, 0, width, height); + this.renderer.setViewport(x, y, width, height); if (!this.packedDepth) { this.depthTexturePrimitives.attachFramebuffer(this.colorTarget.framebuffer, 'depth'); } } - this.renderer.render(this.scene.primitives, this.camera, 'color', true, transparentBackground, null); + this.renderer.render(this.scene.primitives, camera, 'color', true, transparentBackground, null); // do a depth pass if not rendering to drawing buffer and // extensions.depthTexture is unsupported (i.e. depthTarget is set) if (!toDrawingBuffer && this.depthTargetPrimitives) { this.depthTargetPrimitives.bind(); - this.renderer.render(this.scene.primitives, this.camera, 'depth', true, transparentBackground, null); + this.renderer.setViewport(x, y, width, height); + this.renderer.render(this.scene.primitives, camera, 'depth', true, transparentBackground, null); this.colorTarget.bind(); } @@ -154,12 +167,12 @@ export class DrawPass { this.webgl.state.depthMask(true); this.webgl.gl.clear(this.webgl.gl.DEPTH_BUFFER_BIT); } - this.renderer.render(this.scene.volumes, this.camera, 'color', false, transparentBackground, this.depthTexturePrimitives); + this.renderer.render(this.scene.volumes, camera, 'color', false, transparentBackground, this.depthTexturePrimitives); // do volume depth pass if extensions.depthTexture is unsupported (i.e. depthTarget is set) if (this.depthTargetVolumes) { this.depthTargetVolumes.bind(); - this.renderer.render(this.scene.volumes, this.camera, 'depth', true, transparentBackground, this.depthTexturePrimitives); + this.renderer.render(this.scene.volumes, camera, 'depth', true, transparentBackground, this.depthTexturePrimitives); this.colorTarget.bind(); } } @@ -168,6 +181,7 @@ export class DrawPass { if (!toDrawingBuffer) { this.depthMerge.update(); this.depthTarget.bind(); + this.renderer.setViewport(x, y, width, height); this.webgl.state.disable(this.webgl.gl.SCISSOR_TEST); this.webgl.state.disable(this.webgl.gl.BLEND); this.webgl.state.disable(this.webgl.gl.DEPTH_TEST); @@ -176,17 +190,18 @@ export class DrawPass { this.webgl.gl.clear(this.webgl.gl.COLOR_BUFFER_BIT); this.depthMerge.render(); this.colorTarget.bind(); + this.renderer.setViewport(x, y, width, height); } if (this.debugHelper.isEnabled) { this.debugHelper.syncVisibility(); - this.renderer.render(this.debugHelper.scene, this.camera, 'color', false, transparentBackground, null); + this.renderer.render(this.debugHelper.scene, camera, 'color', false, transparentBackground, null); } if (this.handleHelper.isEnabled) { - this.renderer.render(this.handleHelper.scene, this.camera, 'color', false, transparentBackground, null); + this.renderer.render(this.handleHelper.scene, camera, 'color', false, transparentBackground, null); } if (this.cameraHelper.isEnabled) { - this.cameraHelper.update(this.camera); + this.cameraHelper.update(camera); this.renderer.render(this.cameraHelper.scene, this.cameraHelper.camera, 'color', false, transparentBackground, null); } } diff --git a/src/mol-canvas3d/passes/image.ts b/src/mol-canvas3d/passes/image.ts index 41d9b1432e6917f20ed9b63cfd38dd1646588663..f3a833987ed15736c42d0bcd2759fd48d99eff6d 100644 --- a/src/mol-canvas3d/passes/image.ts +++ b/src/mol-canvas3d/passes/image.ts @@ -47,7 +47,7 @@ export class ImagePass { this._transparentBackground = p.transparentBackground; - this.drawPass = new DrawPass(webgl, renderer, scene, this._camera, debugHelper, handleHelper, p.drawPass); + this.drawPass = new DrawPass(webgl, renderer, scene, { standard: this._camera }, debugHelper, handleHelper, p.drawPass); this.postprocessing = new PostprocessingPass(webgl, this._camera, this.drawPass, p.postprocessing); this.multiSample = new MultiSamplePass(webgl, this._camera, this.drawPass, this.postprocessing, p.multiSample); diff --git a/src/mol-canvas3d/passes/multi-sample.ts b/src/mol-canvas3d/passes/multi-sample.ts index d75964b4174112145211a8d011bb91cf6f4cf0aa..f8670a50fd243e208fd12a06118bef04fac39a94 100644 --- a/src/mol-canvas3d/passes/multi-sample.ts +++ b/src/mol-canvas3d/passes/multi-sample.ts @@ -133,7 +133,8 @@ export class MultiSamplePass { for (let i = 0; i < offsetList.length; ++i) { const offset = offsetList[i]; Camera.setViewOffset(camera.viewOffset, width, height, offset[0], offset[1], width, height); - camera.update(); + // TODO: this should not be needed + // camera.update(); this.drawPass.cameraHelper.update(camera); // the theory is that equal weights for each sample lead to an accumulation of rounding diff --git a/src/mol-canvas3d/passes/pick.ts b/src/mol-canvas3d/passes/pick.ts index ea33d8a17ad8abbc1d48af0377687099695647ef..004d003e1d65dcb7330c4bfaf7586d1b6d8ecfbe 100644 --- a/src/mol-canvas3d/passes/pick.ts +++ b/src/mol-canvas3d/passes/pick.ts @@ -4,13 +4,15 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { WebGLContext } from '../../mol-gl/webgl/context'; -import { RenderTarget } from '../../mol-gl/webgl/render-target'; +import { PickingId } from '../../mol-geo/geometry/picking'; import Renderer from '../../mol-gl/renderer'; import Scene from '../../mol-gl/scene'; -import { PickingId } from '../../mol-geo/geometry/picking'; +import { WebGLContext } from '../../mol-gl/webgl/context'; +import { GraphicsRenderVariant } from '../../mol-gl/webgl/render-item'; +import { RenderTarget } from '../../mol-gl/webgl/render-target'; import { decodeFloatRGB } from '../../mol-util/float-packing'; -import { Camera } from '../camera'; +import { Camera, ICamera } from '../camera'; +import { StereoCamera } from '../camera/stereo'; import { HandleHelper } from '../helper/handle-helper'; import { DrawPass } from './draw'; @@ -23,6 +25,8 @@ export class PickPass { instancePickTarget: RenderTarget groupPickTarget: RenderTarget + isStereo = false + private objectBuffer: Uint8Array private instanceBuffer: Uint8Array private groupBuffer: Uint8Array @@ -31,7 +35,7 @@ export class PickPass { private pickWidth: number private pickHeight: number - constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private handleHelper: HandleHelper, private pickBaseScale: number, private drawPass: DrawPass) { + constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private stereoCamera: StereoCamera, private handleHelper: HandleHelper, private pickBaseScale: number, private drawPass: DrawPass) { this.pickScale = pickBaseScale / webgl.pixelRatio; this.pickWidth = Math.ceil(camera.viewport.width * this.pickScale); this.pickHeight = Math.ceil(camera.viewport.height * this.pickScale); @@ -69,25 +73,39 @@ export class PickPass { } } - render() { - const { renderer, scene, camera, handleHelper: { scene: handleScene } } = this; + private renderVariant(variant: GraphicsRenderVariant) { + if (this.isStereo) { + const w = (this.pickWidth / 2) | 0; + + this.renderer.setViewport(0, 0, w, this.pickHeight); + this._renderVariant(this.stereoCamera.left, variant); + + this.renderer.setViewport(w, 0, this.pickWidth - w, this.pickHeight); + this._renderVariant(this.stereoCamera.right, variant); + } else { + this.renderer.setViewport(0, 0, this.pickWidth, this.pickHeight); + this._renderVariant(this.camera, variant); + } + } + + private _renderVariant(camera: ICamera, variant: GraphicsRenderVariant) { + const { renderer, scene, handleHelper: { scene: handleScene } } = this; const depth = this.drawPass.depthTexturePrimitives; - renderer.setViewport(0, 0, this.pickWidth, this.pickHeight); + renderer.render(scene.primitives, camera, variant, true, false, null); + renderer.render(scene.volumes, camera, variant, false, false, depth); + renderer.render(handleScene, camera, variant, false, false, null); + } + + render() { this.objectPickTarget.bind(); - renderer.render(scene.primitives, camera, 'pickObject', true, false, null); - renderer.render(scene.volumes, camera, 'pickObject', false, false, depth); - renderer.render(handleScene, camera, 'pickObject', false, false, null); + this.renderVariant('pickObject'); this.instancePickTarget.bind(); - renderer.render(scene.primitives, camera, 'pickInstance', true, false, null); - renderer.render(scene.volumes, camera, 'pickInstance', false, false, depth); - renderer.render(handleScene, camera, 'pickInstance', false, false, null); + this.renderVariant('pickInstance'); this.groupPickTarget.bind(); - renderer.render(scene.primitives, camera, 'pickGroup', true, false, null); - renderer.render(scene.volumes, camera, 'pickGroup', false, false, depth); - renderer.render(handleScene, camera, 'pickGroup', false, false, null); + this.renderVariant('pickGroup'); this.pickDirty = false; } @@ -123,7 +141,9 @@ export class PickPass { gl.drawingBufferHeight - y < viewport.y || x > viewport.x + viewport.width || gl.drawingBufferHeight - y > viewport.y + viewport.height - ) return; + ) { + return; + } if (this.pickDirty) { this.render(); diff --git a/src/mol-canvas3d/passes/postprocessing.ts b/src/mol-canvas3d/passes/postprocessing.ts index 5f1279e42905017d8848fe4c7093aee05f899a3b..1825cb24dcb842da70fc4b19b85ca3fc58c741d6 100644 --- a/src/mol-canvas3d/passes/postprocessing.ts +++ b/src/mol-canvas3d/passes/postprocessing.ts @@ -167,6 +167,7 @@ export class PostprocessingPass { } const { x, y, width, height } = this.camera.viewport; + const { gl, state } = this.webgl; if (toDrawingBuffer) { this.webgl.unbindFramebuffer(); diff --git a/src/mol-gl/renderer.ts b/src/mol-gl/renderer.ts index f0a66e82af8c8ee396a0c70771702640a1cd11d1..dedea00b3b4c49f6b9c1260435d0377964996d80 100644 --- a/src/mol-gl/renderer.ts +++ b/src/mol-gl/renderer.ts @@ -5,7 +5,7 @@ */ import { Viewport } from '../mol-canvas3d/camera/util'; -import { Camera } from '../mol-canvas3d/camera'; +import { ICamera } from '../mol-canvas3d/camera'; import Scene from './scene'; import { WebGLContext } from './webgl/context'; @@ -43,7 +43,7 @@ interface Renderer { readonly props: Readonly<RendererProps> clear: (transparentBackground: boolean) => void - render: (group: Scene.Group, camera: Camera, variant: GraphicsRenderVariant, clear: boolean, transparentBackground: boolean, depthTexture: Texture | null) => void + render: (group: Scene.Group, camera: ICamera, variant: GraphicsRenderVariant, clear: boolean, transparentBackground: boolean, depthTexture: Texture | null) => void setProps: (props: Partial<RendererProps>) => void setViewport: (x: number, y: number, width: number, height: number) => void dispose: () => void @@ -300,7 +300,7 @@ namespace Renderer { r.render(variant); }; - const render = (group: Scene.Group, camera: Camera, variant: GraphicsRenderVariant, clear: boolean, transparentBackground: boolean, depthTexture: Texture | null) => { + const render = (group: Scene.Group, camera: ICamera, variant: GraphicsRenderVariant, clear: boolean, transparentBackground: boolean, depthTexture: Texture | null) => { ValueCell.update(globalUniforms.uModel, group.view); ValueCell.update(globalUniforms.uView, camera.view); ValueCell.update(globalUniforms.uInvView, Mat4.invert(invView, camera.view)); diff --git a/src/mol-plugin-ui/viewport/simple-settings.tsx b/src/mol-plugin-ui/viewport/simple-settings.tsx index cf72e5002ca9ab640d781007e1340666d2718a7c..fe30c31eb221a8eb4eaac82595f5789bc5c15bf5 100644 --- a/src/mol-plugin-ui/viewport/simple-settings.tsx +++ b/src/mol-plugin-ui/viewport/simple-settings.tsx @@ -62,6 +62,7 @@ const SimpleSettingsParams = { outline: Canvas3DParams.postprocessing.params.outline, fog: Canvas3DParams.cameraFog, }, { pivot: 'renderStyle' }), + stereo: Canvas3DParams.stereo, clipping: PD.Group<any>({ ...Canvas3DParams.cameraClipping.params, ...(Canvas3DParams.renderer.params.clip as any).params as any @@ -111,6 +112,7 @@ const SimpleSettingsMapping = ParamMapping({ outline: canvas.postprocessing.outline, fog: canvas.cameraFog }, + stereo: canvas.stereo, clipping: { ...canvas.cameraClipping, ...canvas.renderer.clip @@ -136,6 +138,7 @@ const SimpleSettingsMapping = ParamMapping({ variant: s.clipping.variant, objects: s.clipping.objects, }; + canvas.stereo = s.stereo; props.layout = s.layout; },