Explorar o código

Merge branch 'master' of https://github.com/molstar/molstar

David Sehnal %!s(int64=5) %!d(string=hai) anos
pai
achega
d4107e273d

+ 47 - 46
src/mol-canvas3d/passes/postprocessing.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -17,6 +17,7 @@ import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { RenderTarget } from '../../mol-gl/webgl/render-target';
 import { DrawPass } from './draw';
 import { Camera } from '../../mol-canvas3d/camera';
+import { produce } from 'immer';
 
 import quad_vert from '../../mol-gl/shader/quad.vert'
 import postprocessing_frag from '../../mol-gl/shader/postprocessing.frag'
@@ -47,14 +48,21 @@ const PostprocessingSchema = {
 }
 
 export const PostprocessingParams = {
-    occlusionEnable: PD.Boolean(false),
-    occlusionKernelSize: PD.Numeric(4, { min: 1, max: 32, step: 1 }),
-    occlusionBias: PD.Numeric(0.5, { min: 0, max: 1, step: 0.01 }),
-    occlusionRadius: PD.Numeric(32, { min: 0, max: 256, step: 1 }),
-
-    outlineEnable: PD.Boolean(false),
-    outlineScale: PD.Numeric(1, { min: 0, max: 10, step: 1 }),
-    outlineThreshold: PD.Numeric(0.8, { min: 0, max: 1, step: 0.01 }),
+    occlusion: PD.MappedStatic('off', {
+        on: PD.Group({
+            kernelSize: PD.Numeric(4, { min: 1, max: 32, step: 1 }),
+            bias: PD.Numeric(0.5, { min: 0, max: 1, step: 0.01 }),
+            radius: PD.Numeric(64, { min: 0, max: 256, step: 1 }),
+        }),
+        off: PD.Group({})
+    }, { cycle: true, description: 'Darken occluded crevices with the ambient occlusion effect' }),
+    outline: PD.MappedStatic('off', {
+        on: PD.Group({
+            scale: PD.Numeric(1, { min: 0, max: 10, step: 1 }),
+            threshold: PD.Numeric(0.8, { min: 0, max: 1, step: 0.01 }),
+        }),
+        off: PD.Group({})
+    }, { cycle: true, description: 'Draw outline around 3D objects' })
 }
 export type PostprocessingProps = PD.Values<typeof PostprocessingParams>
 
@@ -75,14 +83,14 @@ function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, d
         uFogFar: ValueCell.create(10000),
         uFogColor: ValueCell.create(Vec3.create(1, 1, 1)),
 
-        dOcclusionEnable: ValueCell.create(p.occlusionEnable),
-        dOcclusionKernelSize: ValueCell.create(p.occlusionKernelSize),
-        uOcclusionBias: ValueCell.create(p.occlusionBias),
-        uOcclusionRadius: ValueCell.create(p.occlusionRadius),
+        dOcclusionEnable: ValueCell.create(p.occlusion.name === 'on'),
+        dOcclusionKernelSize: ValueCell.create(p.occlusion.name === 'on' ? p.occlusion.params.kernelSize : 4),
+        uOcclusionBias: ValueCell.create(p.occlusion.name === 'on' ? p.occlusion.params.bias : 0.5),
+        uOcclusionRadius: ValueCell.create(p.occlusion.name === 'on' ? p.occlusion.params.radius : 64),
 
-        dOutlineEnable: ValueCell.create(p.outlineEnable),
-        uOutlineScale: ValueCell.create(p.outlineScale * ctx.pixelRatio),
-        uOutlineThreshold: ValueCell.create(p.outlineThreshold),
+        dOutlineEnable: ValueCell.create(p.outline.name === 'on'),
+        uOutlineScale: ValueCell.create((p.outline.name === 'on' ? p.outline.params.scale : 1) * ctx.pixelRatio),
+        uOutlineThreshold: ValueCell.create(p.outline.name === 'on' ? p.outline.params.threshold : 0.8),
 
         dPackedDepth: ValueCell.create(packedDepth),
     }
@@ -108,7 +116,7 @@ export class PostprocessingPass {
     }
 
     get enabled() {
-        return this.props.occlusionEnable || this.props.outlineEnable
+        return this.props.occlusion.name === 'on' || this.props.outline.name === 'on'
     }
 
     setSize(width: number, height: number) {
@@ -117,35 +125,28 @@ export class PostprocessingPass {
     }
 
     setProps(props: Partial<PostprocessingProps>) {
-        if (props.occlusionEnable !== undefined) {
-            this.props.occlusionEnable = props.occlusionEnable
-            ValueCell.update(this.renderable.values.dOcclusionEnable, props.occlusionEnable)
-        }
-        if (props.occlusionKernelSize !== undefined) {
-            this.props.occlusionKernelSize = props.occlusionKernelSize
-            ValueCell.update(this.renderable.values.dOcclusionKernelSize, props.occlusionKernelSize)
-        }
-        if (props.occlusionBias !== undefined) {
-            this.props.occlusionBias = props.occlusionBias
-            ValueCell.update(this.renderable.values.uOcclusionBias, props.occlusionBias)
-        }
-        if (props.occlusionRadius !== undefined) {
-            this.props.occlusionRadius = props.occlusionRadius
-            ValueCell.update(this.renderable.values.uOcclusionRadius, props.occlusionRadius)
-        }
-
-        if (props.outlineEnable !== undefined) {
-            this.props.outlineEnable = props.outlineEnable
-            ValueCell.update(this.renderable.values.dOutlineEnable, props.outlineEnable)
-        }
-        if (props.outlineScale !== undefined) {
-            this.props.outlineScale = props.outlineScale
-            ValueCell.update(this.renderable.values.uOutlineScale, props.outlineScale * this.webgl.pixelRatio)
-        }
-        if (props.outlineThreshold !== undefined) {
-            this.props.outlineThreshold = props.outlineThreshold
-            ValueCell.update(this.renderable.values.uOutlineThreshold, props.outlineThreshold)
-        }
+        this.props = produce(this.props, p => {
+            if (props.occlusion !== undefined) {
+                p.occlusion.name = props.occlusion.name
+                ValueCell.updateIfChanged(this.renderable.values.dOcclusionEnable, props.occlusion.name === 'on')
+                if (props.occlusion.name === 'on') {
+                    p.occlusion.params = { ...props.occlusion.params }
+                    ValueCell.updateIfChanged(this.renderable.values.dOcclusionKernelSize, props.occlusion.params.kernelSize)
+                    ValueCell.updateIfChanged(this.renderable.values.uOcclusionBias, props.occlusion.params.bias)
+                    ValueCell.updateIfChanged(this.renderable.values.uOcclusionRadius, props.occlusion.params.radius)
+                }
+            }
+
+            if (props.outline !== undefined) {
+                p.outline.name = props.outline.name
+                ValueCell.updateIfChanged(this.renderable.values.dOutlineEnable, props.outline.name === 'on')
+                if (props.outline.name === 'on') {
+                    p.outline.params = { ...props.outline.params }
+                    ValueCell.updateIfChanged(this.renderable.values.uOutlineScale, props.outline.params.scale)
+                    ValueCell.updateIfChanged(this.renderable.values.uOutlineThreshold, props.outline.params.threshold)
+                }
+            }
+        })
 
         this.renderable.update()
     }

+ 64 - 35
src/mol-gl/renderer.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -55,22 +55,63 @@ export const RendererParams = {
     interiorColorFlag: PD.Boolean(true, { label: 'Use Interior Color' }),
     interiorColor: PD.Color(Color.fromNormalizedRgb(0.3, 0.3, 0.3)),
 
-    lightIntensity: PD.Numeric(0.6, { min: 0.0, max: 1.0, step: 0.01 }),
-    ambientIntensity: PD.Numeric(0.4, { min: 0.0, max: 1.0, step: 0.01 }),
-
-    metalness: PD.Numeric(0.0, { min: 0.0, max: 1.0, step: 0.01 }),
-    roughness: PD.Numeric(1.0, { min: 0.0, max: 1.0, step: 0.01 }),
-    reflectivity: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }),
-
     highlightColor: PD.Color(Color.fromNormalizedRgb(1.0, 0.4, 0.6)),
     selectColor: PD.Color(Color.fromNormalizedRgb(0.2, 1.0, 0.1)),
+
+    style: PD.MappedStatic('matte', {
+        custom: PD.Group({
+            lightIntensity: PD.Numeric(0.6, { min: 0.0, max: 1.0, step: 0.01 }),
+            ambientIntensity: PD.Numeric(0.4, { min: 0.0, max: 1.0, step: 0.01 }),
+            metalness: PD.Numeric(0.0, { min: 0.0, max: 1.0, step: 0.01 }),
+            roughness: PD.Numeric(1.0, { min: 0.0, max: 1.0, step: 0.01 }),
+            reflectivity: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }),
+        }, { isExpanded: true }),
+        flat: PD.Group({}),
+        matte: PD.Group({}),
+        glossy: PD.Group({}),
+        metallic: PD.Group({}),
+        plastic: PD.Group({}),
+    }, { label: 'Render Style', description: 'Style in which the 3D scene is rendered' }),
 }
 export type RendererProps = PD.Values<typeof RendererParams>
 
+function getStyle(props: RendererProps['style']) {
+    switch (props.name) {
+        case 'custom':
+            return props.params
+        case 'flat':
+            return {
+                lightIntensity: 0, ambientIntensity: 1,
+                metalness: 0, roughness: 0.4, reflectivity: 0.5
+            }
+        case 'matte':
+            return {
+                lightIntensity: 0.6, ambientIntensity: 0.4,
+                metalness: 0, roughness: 1, reflectivity: 0.5
+            }
+        case 'glossy':
+            return {
+                lightIntensity: 0.6, ambientIntensity: 0.4,
+                metalness: 0, roughness: 0.4, reflectivity: 0.5
+            }
+        case 'metallic':
+            return {
+                lightIntensity: 0.6, ambientIntensity: 0.4,
+                metalness: 0.4, roughness: 0.6, reflectivity: 0.5
+            }
+        case 'plastic':
+            return {
+                lightIntensity: 0.6, ambientIntensity: 0.4,
+                metalness: 0, roughness: 0.2, reflectivity: 0.5
+            }
+    }
+}
+
 namespace Renderer {
     export function create(ctx: WebGLContext, props: Partial<RendererProps> = {}): Renderer {
         const { gl, state, stats } = ctx
         const p = deepClone({ ...PD.getDefaultValues(RendererParams), ...props })
+        const style = getStyle(p.style)
 
         const viewport = Viewport()
         const bgColor = Color.toVec3Normalized(Vec3(), p.backgroundColor)
@@ -112,12 +153,12 @@ namespace Renderer {
             uTransparentBackground: ValueCell.create(0),
 
             // the following are general 'material' uniforms
-            uLightIntensity: ValueCell.create(p.lightIntensity),
-            uAmbientIntensity: ValueCell.create(p.ambientIntensity),
+            uLightIntensity: ValueCell.create(style.lightIntensity),
+            uAmbientIntensity: ValueCell.create(style.ambientIntensity),
 
-            uMetalness: ValueCell.create(p.metalness),
-            uRoughness: ValueCell.create(p.roughness),
-            uReflectivity: ValueCell.create(p.reflectivity),
+            uMetalness: ValueCell.create(style.metalness),
+            uRoughness: ValueCell.create(style.roughness),
+            uReflectivity: ValueCell.create(style.reflectivity),
 
             uPickingAlphaThreshold: ValueCell.create(p.pickingAlphaThreshold),
 
@@ -276,28 +317,6 @@ namespace Renderer {
                     ValueCell.update(globalUniforms.uInteriorColor, Color.toVec3Normalized(globalUniforms.uInteriorColor.ref.value, p.interiorColor))
                 }
 
-                if (props.lightIntensity !== undefined && props.lightIntensity !== p.lightIntensity) {
-                    p.lightIntensity = props.lightIntensity
-                    ValueCell.update(globalUniforms.uLightIntensity, p.lightIntensity)
-                }
-                if (props.ambientIntensity !== undefined && props.ambientIntensity !== p.ambientIntensity) {
-                    p.ambientIntensity = props.ambientIntensity
-                    ValueCell.update(globalUniforms.uAmbientIntensity, p.ambientIntensity)
-                }
-
-                if (props.metalness !== undefined && props.metalness !== p.metalness) {
-                    p.metalness = props.metalness
-                    ValueCell.update(globalUniforms.uMetalness, p.metalness)
-                }
-                if (props.roughness !== undefined && props.roughness !== p.roughness) {
-                    p.roughness = props.roughness
-                    ValueCell.update(globalUniforms.uRoughness, p.roughness)
-                }
-                if (props.reflectivity !== undefined && props.reflectivity !== p.reflectivity) {
-                    p.reflectivity = props.reflectivity
-                    ValueCell.update(globalUniforms.uReflectivity, p.reflectivity)
-                }
-
                 if (props.highlightColor !== undefined && props.highlightColor !== p.highlightColor) {
                     p.highlightColor = props.highlightColor
                     ValueCell.update(globalUniforms.uHighlightColor, Color.toVec3Normalized(globalUniforms.uHighlightColor.ref.value, p.highlightColor))
@@ -306,6 +325,16 @@ namespace Renderer {
                     p.selectColor = props.selectColor
                     ValueCell.update(globalUniforms.uSelectColor, Color.toVec3Normalized(globalUniforms.uSelectColor.ref.value, p.selectColor))
                 }
+
+                if (props.style !== undefined) {
+                    p.style = props.style
+                    Object.assign(style, getStyle(props.style))
+                    ValueCell.updateIfChanged(globalUniforms.uLightIntensity, style.lightIntensity)
+                    ValueCell.updateIfChanged(globalUniforms.uAmbientIntensity, style.ambientIntensity)
+                    ValueCell.updateIfChanged(globalUniforms.uMetalness, style.metalness)
+                    ValueCell.updateIfChanged(globalUniforms.uRoughness, style.roughness)
+                    ValueCell.updateIfChanged(globalUniforms.uReflectivity, style.reflectivity)
+                }
             },
             setViewport: (x: number, y: number, width: number, height: number) => {
                 gl.viewport(x, y, width, height)

+ 14 - 38
src/mol-plugin-ui/viewport/simple-settings.tsx

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  * @author David Sehnal <david.sehnal@gmail.com>
@@ -38,9 +38,9 @@ const SimpleSettingsParams = {
         'transparent': PD.EmptyGroup(),
         'opaque': PD.Group({ color: PD.Color(Color(0xFCFBF9), { description: 'Custom background color' }) }, { isFlat: true })
     }, { description: 'Background of the 3D canvas' }),
-    renderStyle: PD.Select('glossy', PD.arrayToOptions(['flat', 'matte', 'glossy', 'metallic']), { description: 'Style in which the 3D scene is rendered' }),
-    occlusion: PD.Boolean(false, { description: 'Darken occluded crevices with the ambient occlusion effect' }),
-    outline: PD.Boolean(false, { description: 'Draw outline around 3D objects' }),
+    renderStyle: Canvas3DParams.renderer.params.style,
+    occlusion: Canvas3DParams.postprocessing.params.occlusion,
+    outline: Canvas3DParams.postprocessing.params.outline,
     fog: PD.Boolean(false, { description: 'Show fog in the distance' }),
     clipFar: PD.Boolean(true, { description: 'Clip scene in the distance' }),
 };
@@ -48,43 +48,28 @@ const SimpleSettingsParams = {
 type SimpleSettingsParams = typeof SimpleSettingsParams
 const SimpleSettingsMapping = ParamMapping({
     params: SimpleSettingsParams,
-    target(ctx: PluginContext) { 
+    target(ctx: PluginContext) {
         const layout: SimpleSettingsParams['layout']['defaultValue'] = [];
         if (ctx.layout.state.regionState.top !== 'hidden') layout.push('sequence');
         if (ctx.layout.state.regionState.bottom !== 'hidden') layout.push('log');
         if (ctx.layout.state.regionState.left !== 'hidden') layout.push('left');
         return { canvas: ctx.canvas3d?.props!, layout };
     }
-})({ 
+})({
     values(props, ctx) {
         const { canvas } = props;
         const renderer = canvas.renderer;
 
-        let renderStyle: SimpleSettingsParams['renderStyle']['defaultValue'] = 'custom' as any;
-        if (renderer) {
-            if (renderer.lightIntensity === 0 && renderer.ambientIntensity === 1 && renderer.roughness === 0.4 && renderer.metalness === 0) {
-                renderStyle = 'flat'
-            } else if (renderer.lightIntensity === 0.6 && renderer.ambientIntensity === 0.4) {
-                if (renderer.roughness === 1 && renderer.metalness === 0) {
-                    renderStyle = 'matte'
-                } else if (renderer.roughness === 0.4 && renderer.metalness === 0) {
-                    renderStyle = 'glossy'
-                } else if (renderer.roughness === 0.6 && renderer.metalness === 0.4) {
-                    renderStyle = 'metallic'
-                }
-            }
-        }
-
         return {
             layout: props.layout,
             spin: !!canvas.trackball.spin,
             camera: canvas.cameraMode,
-            background:  (renderer.backgroundColor === ColorNames.white && canvas.transparentBackground) 
+            background:  (renderer.backgroundColor === ColorNames.white && canvas.transparentBackground)
                 ? { name: 'transparent', params: { } }
                 : { name: 'opaque', params: { color: renderer.backgroundColor } },
-            renderStyle,
-            occlusion: canvas.postprocessing.occlusionEnable,
-            outline: canvas.postprocessing.outlineEnable,
+            renderStyle: renderer.style,
+            occlusion: canvas.postprocessing.occlusion,
+            outline: canvas.postprocessing.outline,
             fog: ctx.canvas3d ? canvas.cameraFog > 1 : false,
             clipFar: canvas.cameraClipFar
         };
@@ -95,18 +80,9 @@ const SimpleSettingsMapping = ParamMapping({
         canvas.cameraMode = s.camera;
         canvas.transparentBackground = s.background.name === 'transparent';
         canvas.renderer.backgroundColor = s.background.name === 'transparent' ? ColorNames.white : s.background.params.color;
-        switch (s.renderStyle) {
-            case 'flat': Object.assign(canvas.renderer, { lightIntensity: 0, ambientIntensity: 1, roughness: 0.4, metalness: 0 }); break;
-            case 'matte':  Object.assign(canvas.renderer, { lightIntensity: 0.6, ambientIntensity: 0.4, roughness: 1, metalness: 0 }); break;
-            case 'glossy':  Object.assign(canvas.renderer, { lightIntensity: 0.6, ambientIntensity: 0.4, roughness: 0.4, metalness: 0 }); break;
-            case 'metallic':  Object.assign(canvas.renderer, { lightIntensity: 0.6, ambientIntensity: 0.4, roughness: 0.6, metalness: 0.4 }); break;
-        }
-        canvas.postprocessing.occlusionEnable = s.occlusion;
-        if (s.occlusion) { 
-            canvas.postprocessing.occlusionBias = 0.5;
-            canvas.postprocessing.occlusionRadius = 64;
-        }
-        canvas.postprocessing.outlineEnable = s.outline;
+        canvas.renderer.style = s.renderStyle
+        canvas.postprocessing.occlusion = s.occlusion;
+        canvas.postprocessing.outline = s.outline;
         canvas.cameraFog = s.fog ? 50 : 0;
         canvas.cameraClipFar = s.clipFar;
 
@@ -122,7 +98,7 @@ const SimpleSettingsMapping = ParamMapping({
             s.regionState.left = hideLeft ? 'hidden' : ctx.behaviors.layout.leftPanelTabName.value === 'none' ? 'collapsed' : 'full';
         });
         await PluginCommands.Layout.Update(ctx, { state });
-        
+
         if (hideLeft) {
             PluginCommands.State.SetCurrentObject(ctx, { state: ctx.state.dataState, ref: StateTransform.RootRef });
         }