Bläddra i källkod

added option to toggle far clipping

Alexander Rose 5 år sedan
förälder
incheckning
cad95403aa

+ 2 - 1
src/examples/proteopedia-wrapper/index.ts

@@ -385,7 +385,8 @@ class MolStarProteopediaWrapper {
             // const position = Vec3.sub(Vec3.zero(), sphere.center, asmCenter);
             // Vec3.normalize(position, position);
             // Vec3.scaleAndAdd(position, sphere.center, position, sphere.radius);
-            const snapshot = this.plugin.canvas3d!.camera.getFocus(sphere.center, Math.max(sphere.radius, 5));
+            const radius = Math.max(sphere.radius, 5)
+            const snapshot = this.plugin.canvas3d!.camera.getFocus(sphere.center, radius, radius);
             PluginCommands.Camera.SetSnapshot.dispatch(this.plugin, { snapshot, durationMs: 250 });
         }
     }

+ 22 - 15
src/mol-canvas3d/camera.ts

@@ -82,12 +82,12 @@ class Camera {
         return Camera.copySnapshot(Camera.createDefaultSnapshot(), this.state);
     }
 
-    getFocus(target: Vec3, radius: number, up?: Vec3, dir?: Vec3): Partial<Camera.Snapshot> {
+    getFocus(target: Vec3, radiusNear: number, radiusFar: number, up?: Vec3, dir?: Vec3): Partial<Camera.Snapshot> {
         const fov = this.state.fov
         const { width, height } = this.viewport
         const aspect = width / height
         const aspectFactor = (height < width ? 1 : aspect)
-        const targetDistance = Math.abs((radius / aspectFactor) / Math.sin(fov / 2))
+        const targetDistance = Math.abs((radiusNear / aspectFactor) / Math.sin(fov / 2))
 
         Vec3.sub(this.deltaDirection, this.target, this.position)
         if (dir) Vec3.matchDirection(this.deltaDirection, dir, this.deltaDirection)
@@ -96,15 +96,18 @@ class Camera {
 
         const state = Camera.copySnapshot(Camera.createDefaultSnapshot(), this.state)
         state.target = Vec3.clone(target)
-        state.radius = radius
+        state.radiusNear = radiusNear
+        state.radiusFar = radiusFar
         state.position = Vec3.clone(this.newPosition)
         if (up) Vec3.matchDirection(state.up, up, state.up)
 
         return state
     }
 
-    focus(target: Vec3, radius: number, durationMs?: number, up?: Vec3, dir?: Vec3) {
-        if (radius > 0) this.setState(this.getFocus(target, radius, up, dir), durationMs);
+    focus(target: Vec3, radiusNear: number, radiusFar: number, durationMs?: number, up?: Vec3, dir?: Vec3) {
+        if (radiusNear > 0 && radiusFar > 0) {
+            this.setState(this.getFocus(target, radiusNear, radiusFar, up, dir), durationMs);
+        }
     }
 
     project(out: Vec4, point: Vec3) {
@@ -158,8 +161,10 @@ namespace Camera {
             up: Vec3.create(0, 1, 0),
             target: Vec3.create(0, 0, 0),
 
-            radius: 10,
+            radiusNear: 10,
+            radiusFar: 10,
             fog: 50,
+            clipFar: true
         };
     }
 
@@ -171,8 +176,10 @@ namespace Camera {
         up: Vec3
         target: Vec3
 
-        radius: number
+        radiusNear: number
+        radiusFar: number
         fog: number
+        clipFar: boolean
     }
 
     export function copySnapshot(out: Snapshot, source?: Partial<Snapshot>) {
@@ -185,8 +192,10 @@ namespace Camera {
         if (typeof source.up !== 'undefined') Vec3.copy(out.up, source.up);
         if (typeof source.target !== 'undefined') Vec3.copy(out.target, source.target);
 
-        if (typeof source.radius !== 'undefined') out.radius = source.radius;
+        if (typeof source.radiusNear !== 'undefined') out.radiusNear = source.radiusNear;
+        if (typeof source.radiusFar !== 'undefined') out.radiusFar = source.radiusFar;
         if (typeof source.fog !== 'undefined') out.fog = source.fog;
+        if (typeof source.clipFar !== 'undefined') out.clipFar = source.clipFar;
 
         return out;
     }
@@ -253,17 +262,15 @@ function updatePers(camera: Camera) {
 }
 
 function updateClip(camera: Camera) {
-    const { radius, mode, fog } = camera.state
+    const { radiusNear, radiusFar, mode, fog, clipFar } = camera.state
 
     const cDist = Vec3.distance(camera.position, camera.target)
-    const bRadius = Math.max(1, radius)
-
-    let near = cDist - bRadius
-    let far = cDist + bRadius
+    let near = cDist - radiusNear
+    let far = cDist + (clipFar ? radiusNear : radiusFar)
 
     const fogNearFactor = -(50 - fog) / 50
-    let fogNear = cDist - (bRadius * fogNearFactor)
-    let fogFar = cDist + bRadius
+    let fogNear = cDist - (radiusNear * fogNearFactor)
+    let fogFar = far
 
     if (mode === 'perspective') {
         // set at least to 5 to avoid slow sphere impostor rendering

+ 3 - 1
src/mol-canvas3d/camera/transition.ts

@@ -79,7 +79,9 @@ namespace CameraTransitionManager {
         // Lerp target, position & radius
         Vec3.lerp(out.target, source.target, target.target, t);
         Vec3.lerp(out.position, source.position, target.position, t);
-        out.radius = lerp(source.radius, target.radius, t);
+        out.radiusNear = lerp(source.radiusNear, target.radiusNear, t);
+        // TODO take change of `clipFar` into account
+        out.radiusFar = lerp(source.radiusFar, target.radiusFar, t);
 
         // Lerp fov & fog
         out.fov = lerp(source.fov, target.fov, t);

+ 14 - 3
src/mol-canvas3d/canvas3d.ts

@@ -33,10 +33,12 @@ import { DrawPass } from './passes/draw';
 import { PickPass } from './passes/pick';
 import { Task } from '../mol-task';
 import { ImagePass, ImageProps } from './passes/image';
+import { Sphere3D } from '../mol-math/geometry';
 
 export const Canvas3DParams = {
     cameraMode: PD.Select('perspective', [['perspective', 'Perspective'], ['orthographic', 'Orthographic']]),
     cameraFog: PD.Numeric(50, { min: 1, max: 100, step: 1 }),
+    cameraClipFar: PD.Boolean(true),
     cameraResetDurationMs: PD.Numeric(250, { min: 0, max: 1000, step: 1 }, { description: 'The time it takes to reset the camera.' }),
     transparentBackground: PD.Boolean(false),
 
@@ -73,6 +75,7 @@ interface Canvas3D {
     /** Focuses camera on scene's bounding sphere, centered and zoomed. */
     resetCamera: () => void
     readonly camera: Camera
+    readonly boundingSphere: Readonly<Sphere3D>
     downloadScreenshot: () => void
     getPixelData: (variant: GraphicsRenderVariant) => PixelData
     setProps: (props: Partial<Canvas3DProps>) => void
@@ -127,7 +130,8 @@ namespace Canvas3D {
         const camera = new Camera({
             position: Vec3.create(0, 0, 100),
             mode: p.cameraMode,
-            fog: p.cameraFog
+            fog: p.cameraFog,
+            clipFar: p.cameraClipFar
         })
 
         const controls = TrackballControls.create(input, camera, p.trackball)
@@ -240,7 +244,8 @@ namespace Canvas3D {
 
             return runTask(scene.commit()).then(() => {
                 if (cameraResetRequested && !scene.isCommiting) {
-                    camera.focus(scene.boundingSphere.center, scene.boundingSphere.radius)
+                    const { center, radius } = scene.boundingSphere
+                    camera.focus(center, radius, radius)
                     cameraResetRequested = false
                 }
                 if (debugHelper.isEnabled) debugHelper.update()
@@ -320,11 +325,13 @@ namespace Canvas3D {
                 if (scene.isCommiting) {
                     cameraResetRequested = true
                 } else {
-                    camera.focus(scene.boundingSphere.center, scene.boundingSphere.radius, p.cameraResetDurationMs)
+                    const { center, radius } = scene.boundingSphere
+                    camera.focus(center, radius, radius, p.cameraResetDurationMs)
                     requestDraw(true);
                 }
             },
             camera,
+            boundingSphere: scene.boundingSphere,
             downloadScreenshot: () => {
                 // TODO
             },
@@ -346,6 +353,9 @@ namespace Canvas3D {
                 if (props.cameraFog !== undefined && props.cameraFog !== camera.state.fog) {
                     camera.setState({ fog: props.cameraFog })
                 }
+                if (props.cameraClipFar !== undefined && props.cameraClipFar !== camera.state.clipFar) {
+                    camera.setState({ clipFar: props.cameraClipFar })
+                }
                 if (props.cameraResetDurationMs !== undefined) p.cameraResetDurationMs = props.cameraResetDurationMs
                 if (props.transparentBackground !== undefined) p.transparentBackground = props.transparentBackground
 
@@ -364,6 +374,7 @@ namespace Canvas3D {
                 return {
                     cameraMode: camera.state.mode,
                     cameraFog: camera.state.fog,
+                    cameraClipFar: camera.state.clipFar,
                     cameraResetDurationMs: p.cameraResetDurationMs,
                     transparentBackground: p.transparentBackground,
 

+ 3 - 3
src/mol-canvas3d/controls/trackball.ts

@@ -208,8 +208,8 @@ namespace TrackballControls {
         function focusCamera() {
             const factor = (_focusEnd[1] - _focusStart[1]) * p.zoomSpeed
             if (factor !== 0.0) {
-                const radius = Math.max(1, camera.state.radius + 10 * factor)
-                camera.setState({ radius })
+                const radiusNear = Math.max(1, camera.state.radiusNear + 10 * factor)
+                camera.setState({ radiusNear })
             }
 
             if (p.staticMoving) {
@@ -343,7 +343,7 @@ namespace TrackballControls {
             if (dragFocus) Vec2.copy(_focusEnd, mouseOnScreenVec2)
             if (dragFocusZoom) {
                 const dist = Vec3.distance(camera.state.position, camera.state.target);
-                camera.setState({ radius: dist / 5 })
+                camera.setState({ radiusNear: dist / 5 })
             }
             if (dragPan) Vec2.copy(_panEnd, mouseOnScreenVec2)
         }

+ 2 - 2
src/mol-plugin-ui/structure/selection.tsx

@@ -65,7 +65,7 @@ export class StructureSelectionControls<P, S extends StructureSelectionControlsS
         const { origin, dirA, dirC } = principalAxes.boxAxes
         const { sphere } = this.plugin.helpers.structureSelectionManager.getBoundary()
         const radius = Math.max(sphere.radius + extraRadius, minRadius);
-        this.plugin.canvas3d?.camera.focus(origin, radius, durationMs, dirA, dirC);
+        this.plugin.canvas3d?.camera.focus(origin, radius, this.plugin.canvas3d.boundingSphere.radius, durationMs, dirA, dirC);
     }
 
     focusLoci(loci: StructureElement.Loci) {
@@ -74,7 +74,7 @@ export class StructureSelectionControls<P, S extends StructureSelectionControlsS
             if (this.plugin.helpers.structureSelectionManager.stats.elementCount === 0) return
             const { sphere } = StructureElement.Loci.getBoundary(loci)
             const radius = Math.max(sphere.radius + extraRadius, minRadius);
-            this.plugin.canvas3d?.camera.focus(sphere.center, radius, durationMs);
+            this.plugin.canvas3d?.camera.focus(sphere.center, radius, this.plugin.canvas3d.boundingSphere.radius, durationMs);
         }
     }
 

+ 7 - 1
src/mol-plugin-ui/viewport/simple-settings.tsx

@@ -25,6 +25,7 @@ const SimpleSettingsParams = {
     occlusion: PD.Boolean(false, { description: 'Darken occluded crevices with the ambient occlusion effect' }),
     outline: PD.Boolean(false, { description: 'Draw outline around 3D objects' }),
     fog: PD.Boolean(false, { description: 'Show fog in the distance' }),
+    clipFar: PD.Boolean(true, { description: 'Clip scene in the distance' }),
 };
 
 export class SimpleSettingsControl extends PluginUIComponent {
@@ -81,6 +82,10 @@ export class SimpleSettingsControl extends PluginUIComponent {
             PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: {
                 cameraFog: p.value ? 50 : 1,
             } });
+        } else if (p.name === 'clipFar') {;
+            PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: {
+                cameraClipFar: p.value,
+            } });
         }
     }
 
@@ -117,7 +122,8 @@ export class SimpleSettingsControl extends PluginUIComponent {
             renderStyle,
             occlusion: this.plugin.canvas3d?.props.postprocessing.occlusionEnable,
             outline: this.plugin.canvas3d?.props.postprocessing.outlineEnable,
-            fog: this.plugin.canvas3d ? this.plugin.canvas3d.props.cameraFog > 1 : false
+            fog: this.plugin.canvas3d ? this.plugin.canvas3d.props.cameraFog > 1 : false,
+            clipFar: this.plugin.canvas3d?.props.cameraClipFar
         }
     }
 

+ 1 - 1
src/mol-plugin/behavior/dynamic/camera.ts

@@ -47,7 +47,7 @@ export const FocusLoci = PluginBehavior.create<FocusLociProps>({
                         const sphere = Loci.getBoundingSphere(loci);
                         if (sphere) {
                             const radius = Math.max(sphere.radius + p.extraRadius, p.minRadius);
-                            this.ctx.canvas3d.camera.focus(sphere.center, radius, p.durationMs);
+                            this.ctx.canvas3d.camera.focus(sphere.center, radius, this.ctx.canvas3d.boundingSphere.radius, p.durationMs);
                         }
                     }
                 }