Browse Source

Merge pull request #621 from molstar/theme-strength

add theme strength to 3d repr state
Alexander Rose 2 years ago
parent
commit
7029bc41d7

+ 1 - 0
CHANGELOG.md

@@ -9,6 +9,7 @@ Note that since we don't clearly distinguish between a public and private interf
 - Make `PluginContext.initContainer` checkered canvas background optional
 - Store URL of downloaded assets to detect zip/gzip based on extension
 - Add optional `operator.key`; can be referenced in `IndexPairBonds`
+- Add overpaint/transparency/substance theme strength to representations
 
 ## [v3.23.0] - 2022-10-19
 

+ 4 - 1
src/mol-geo/geometry/overpaint-data.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -21,6 +21,7 @@ export type OverpaintData = {
     uOverpaintGridDim: ValueCell<Vec3>,
     uOverpaintGridTransform: ValueCell<Vec4>,
     dOverpaintType: ValueCell<string>,
+    uOverpaintStrength: ValueCell<number>,
 }
 
 export function applyOverpaintColor(array: Uint8Array, start: number, end: number, color: Color) {
@@ -54,6 +55,7 @@ export function createOverpaint(count: number, type: OverpaintType, overpaintDat
             uOverpaintGridDim: ValueCell.create(Vec3.create(1, 1, 1)),
             uOverpaintGridTransform: ValueCell.create(Vec4.create(0, 0, 0, 1)),
             dOverpaintType: ValueCell.create(type),
+            uOverpaintStrength: ValueCell.create(1),
         };
     }
 }
@@ -74,6 +76,7 @@ export function createEmptyOverpaint(overpaintData?: OverpaintData): OverpaintDa
             uOverpaintGridDim: ValueCell.create(Vec3.create(1, 1, 1)),
             uOverpaintGridTransform: ValueCell.create(Vec4.create(0, 0, 0, 1)),
             dOverpaintType: ValueCell.create('groupInstance'),
+            uOverpaintStrength: ValueCell.create(1),
         };
     }
 }

+ 4 - 1
src/mol-geo/geometry/substance-data.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2021-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -21,6 +21,7 @@ export type SubstanceData = {
     uSubstanceGridDim: ValueCell<Vec3>,
     uSubstanceGridTransform: ValueCell<Vec4>,
     dSubstanceType: ValueCell<string>,
+    uSubstanceStrength: ValueCell<number>,
 }
 
 export function applySubstanceMaterial(array: Uint8Array, start: number, end: number, material: Material) {
@@ -54,6 +55,7 @@ export function createSubstance(count: number, type: SubstanceType, substanceDat
             uSubstanceGridDim: ValueCell.create(Vec3.create(1, 1, 1)),
             uSubstanceGridTransform: ValueCell.create(Vec4.create(0, 0, 0, 1)),
             dSubstanceType: ValueCell.create(type),
+            uSubstanceStrength: ValueCell.create(1),
         };
     }
 }
@@ -74,6 +76,7 @@ export function createEmptySubstance(substanceData?: SubstanceData): SubstanceDa
             uSubstanceGridDim: ValueCell.create(Vec3.create(1, 1, 1)),
             uSubstanceGridTransform: ValueCell.create(Vec4.create(0, 0, 0, 1)),
             dSubstanceType: ValueCell.create('groupInstance'),
+            uSubstanceStrength: ValueCell.create(1),
         };
     }
 }

+ 3 - 0
src/mol-geo/geometry/transparency-data.ts

@@ -21,6 +21,7 @@ export type TransparencyData = {
     uTransparencyGridDim: ValueCell<Vec3>,
     uTransparencyGridTransform: ValueCell<Vec4>,
     dTransparencyType: ValueCell<string>,
+    uTransparencyStrength: ValueCell<number>,
 }
 
 export function applyTransparencyValue(array: Uint8Array, start: number, end: number, value: number) {
@@ -63,6 +64,7 @@ export function createTransparency(count: number, type: TransparencyType, transp
             uTransparencyGridDim: ValueCell.create(Vec3.create(1, 1, 1)),
             uTransparencyGridTransform: ValueCell.create(Vec4.create(0, 0, 0, 1)),
             dTransparencyType: ValueCell.create(type),
+            uTransparencyStrength: ValueCell.create(1),
         };
     }
 }
@@ -84,6 +86,7 @@ export function createEmptyTransparency(transparencyData?: TransparencyData): Tr
             uTransparencyGridDim: ValueCell.create(Vec3.create(1, 1, 1)),
             uTransparencyGridTransform: ValueCell.create(Vec4.create(0, 0, 0, 1)),
             dTransparencyType: ValueCell.create('groupInstance'),
+            uTransparencyStrength: ValueCell.create(1),
         };
     }
 }

+ 4 - 1
src/mol-gl/renderable/schema.ts

@@ -229,6 +229,7 @@ export const OverpaintSchema = {
     uOverpaintGridTransform: UniformSpec('v4'),
     tOverpaintGrid: TextureSpec('texture', 'rgba', 'ubyte', 'linear'),
     dOverpaintType: DefineSpec('string', ['instance', 'groupInstance', 'volumeInstance']),
+    uOverpaintStrength: UniformSpec('f', 'material'),
 } as const;
 export type OverpaintSchema = typeof OverpaintSchema
 export type OverpaintValues = Values<OverpaintSchema>
@@ -242,7 +243,8 @@ export const TransparencySchema = {
     uTransparencyGridDim: UniformSpec('v3'),
     uTransparencyGridTransform: UniformSpec('v4'),
     tTransparencyGrid: TextureSpec('texture', 'alpha', 'ubyte', 'linear'),
-    dTransparencyType: DefineSpec('string', ['instance', 'groupInstance', 'volumeInstance'])
+    dTransparencyType: DefineSpec('string', ['instance', 'groupInstance', 'volumeInstance']),
+    uTransparencyStrength: UniformSpec('f', 'material'),
 } as const;
 export type TransparencySchema = typeof TransparencySchema
 export type TransparencyValues = Values<TransparencySchema>
@@ -256,6 +258,7 @@ export const SubstanceSchema = {
     uSubstanceGridTransform: UniformSpec('v4'),
     tSubstanceGrid: TextureSpec('texture', 'rgba', 'ubyte', 'linear'),
     dSubstanceType: DefineSpec('string', ['instance', 'groupInstance', 'volumeInstance']),
+    uSubstanceStrength: UniformSpec('f', 'material'),
 } as const;
 export type SubstanceSchema = typeof SubstanceSchema
 export type SubstanceValues = Values<SubstanceSchema>

+ 3 - 0
src/mol-gl/shader/chunks/assign-color-varying.glsl.ts

@@ -42,6 +42,7 @@ export const assign_color_varying = `
         #else
             vOverpaint.rgb = mix(vColor.rgb, vOverpaint.rgb, vOverpaint.a);
         #endif
+        vOverpaint *= uOverpaintStrength;
     #endif
 
     #ifdef dSubstance
@@ -58,6 +59,7 @@ export const assign_color_varying = `
 
         // pre-mix to avoid artifacts due to empty substance
         vSubstance.rgb = mix(vec3(uMetalness, uRoughness, uBumpiness), vSubstance.rgb, vSubstance.a);
+        vSubstance *= uSubstanceStrength;
     #endif
 #elif defined(dRenderVariant_pick)
     #ifdef requiredDrawBuffers
@@ -86,5 +88,6 @@ export const assign_color_varying = `
         vec3 tgridPos = (uTransparencyGridTransform.w * (vModelPosition - uTransparencyGridTransform.xyz)) / uTransparencyGridDim;
         vTransparency = texture3dFrom2dLinear(tTransparencyGrid, tgridPos, uTransparencyGridDim, uTransparencyTexDim).a;
     #endif
+    vTransparency *= uTransparencyStrength;
 #endif
 `;

+ 3 - 0
src/mol-gl/shader/chunks/color-vert-params.glsl.ts

@@ -39,6 +39,7 @@ uniform float uBumpiness;
             uniform vec4 uOverpaintGridTransform;
             uniform sampler2D tOverpaintGrid;
         #endif
+        uniform float uOverpaintStrength;
     #endif
 
     #ifdef dSubstance
@@ -53,6 +54,7 @@ uniform float uBumpiness;
             uniform vec4 uSubstanceGridTransform;
             uniform sampler2D tSubstanceGrid;
         #endif
+        uniform float uSubstanceStrength;
     #endif
 #elif defined(dRenderVariant_pick)
     #if __VERSION__ == 100 || !defined(dVaryingGroup)
@@ -86,5 +88,6 @@ uniform float uBumpiness;
         uniform vec4 uTransparencyGridTransform;
         uniform sampler2D tTransparencyGrid;
     #endif
+    uniform float uTransparencyStrength;
 #endif
 `;

+ 59 - 1
src/mol-plugin-state/transforms/representation.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -43,6 +43,7 @@ import { Box3D } from '../../mol-math/geometry';
 import { PlaneParams, PlaneRepresentation } from '../../mol-repr/shape/loci/plane';
 import { Substance } from '../../mol-theme/substance';
 import { Material } from '../../mol-util/material';
+import { lerp } from '../../mol-math/interpolate';
 
 export { StructureRepresentation3D };
 export { ExplodeStructureRepresentation3D };
@@ -56,6 +57,7 @@ export { SubstanceStructureRepresentation3DFromScript };
 export { SubstanceStructureRepresentation3DFromBundle };
 export { ClippingStructureRepresentation3DFromScript };
 export { ClippingStructureRepresentation3DFromBundle };
+export { ThemeStrengthRepresentation3D };
 export { VolumeRepresentation3D };
 
 type StructureRepresentation3D = typeof StructureRepresentation3D
@@ -745,6 +747,62 @@ const ClippingStructureRepresentation3DFromBundle = PluginStateTransform.BuiltIn
     }
 });
 
+type ThemeStrengthRepresentation3D = typeof ThemeStrengthRepresentation3D
+const ThemeStrengthRepresentation3D = PluginStateTransform.BuiltIn({
+    name: 'theme-strength-representation-3d',
+    display: 'Theme Strength 3D Representation',
+    from: SO.Molecule.Structure.Representation3D,
+    to: SO.Molecule.Structure.Representation3DState,
+    params: () => ({
+        overpaintStrength: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }),
+        transparencyStrength: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }),
+        substanceStrength: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }),
+    })
+})({
+    canAutoUpdate() {
+        return true;
+    },
+    apply({ a, params }) {
+        return new SO.Molecule.Structure.Representation3DState({
+            state: {
+                themeStrength: {
+                    overpaint: params.overpaintStrength,
+                    transparency: params.transparencyStrength,
+                    substance: params.substanceStrength
+                },
+            },
+            initialState: {
+                themeStrength: { overpaint: 1, transparency: 1, substance: 1 },
+            },
+            info: { },
+            repr: a.data.repr
+        }, { label: 'Theme Strength', description: `${params.overpaintStrength.toFixed(2)}, ${params.transparencyStrength.toFixed(2)}, ${params.substanceStrength.toFixed(2)}` });
+    },
+    update({ a, b, newParams, oldParams }) {
+        if (newParams.overpaintStrength === b.data.state.themeStrength?.overpaint &&
+            newParams.transparencyStrength === b.data.state.themeStrength?.transparency &&
+            newParams.substanceStrength === b.data.state.themeStrength?.substance
+        ) return StateTransformer.UpdateResult.Unchanged;
+
+        b.data.state.themeStrength = {
+            overpaint: newParams.overpaintStrength,
+            transparency: newParams.transparencyStrength,
+            substance: newParams.substanceStrength,
+        };
+        b.data.repr = a.data.repr;
+        b.label = 'Theme Strength';
+        b.description = `${newParams.overpaintStrength.toFixed(2)}, ${newParams.transparencyStrength.toFixed(2)}, ${newParams.substanceStrength.toFixed(2)}`;
+        return StateTransformer.UpdateResult.Updated;
+    },
+    interpolate(src, tar, t) {
+        return {
+            overpaintStrength: lerp(src.overpaintStrength, tar.overpaintStrength, t),
+            transparencyStrength: lerp(src.transparencyStrength, tar.transparencyStrength, t),
+            substanceStrength: lerp(src.substanceStrength, tar.substanceStrength, t),
+        };
+    }
+});
+
 //
 
 export namespace VolumeRepresentation3DHelpers {

+ 1 - 0
src/mol-plugin/spec.ts

@@ -104,6 +104,7 @@ export const DefaultPluginSpec = (): PluginSpec => ({
         PluginSpec.Action(StateTransforms.Representation.TransparencyStructureRepresentation3DFromScript),
         PluginSpec.Action(StateTransforms.Representation.ClippingStructureRepresentation3DFromScript),
         PluginSpec.Action(StateTransforms.Representation.SubstanceStructureRepresentation3DFromScript),
+        PluginSpec.Action(StateTransforms.Representation.ThemeStrengthRepresentation3D),
 
         PluginSpec.Action(AssignColorVolume),
         PluginSpec.Action(StateTransforms.Volume.VolumeFromCcp4),

+ 18 - 1
src/mol-repr/representation.ts

@@ -191,6 +191,8 @@ namespace Representation {
         substance: Substance
         /** Bit mask of per group clipping applied to the representation's renderobjects */
         clipping: Clipping
+        /** Strength of the representations overpaint, transparency, substance*/
+        themeStrength: { overpaint: number, transparency: number, substance: number }
         /** Controls if the representation's renderobjects are synced automatically with GPU or not */
         syncManually: boolean
         /** A transformation applied to the representation's renderobjects */
@@ -199,7 +201,20 @@ namespace Representation {
         markerActions: MarkerActions
     }
     export function createState(): State {
-        return { visible: true, alphaFactor: 1, pickable: true, colorOnly: false, syncManually: false, transform: Mat4.identity(), overpaint: Overpaint.Empty, transparency: Transparency.Empty, substance: Substance.Empty, clipping: Clipping.Empty, markerActions: MarkerActions.All };
+        return {
+            visible: true,
+            alphaFactor: 1,
+            pickable: true,
+            colorOnly: false,
+            syncManually: false,
+            transform: Mat4.identity(),
+            overpaint: Overpaint.Empty,
+            transparency: Transparency.Empty,
+            substance: Substance.Empty,
+            clipping: Clipping.Empty,
+            themeStrength: { overpaint: 1, transparency: 1, substance: 1 },
+            markerActions: MarkerActions.All
+        };
     }
     export function updateState(state: State, update: Partial<State>) {
         if (update.visible !== undefined) state.visible = update.visible;
@@ -210,6 +225,7 @@ namespace Representation {
         if (update.transparency !== undefined) state.transparency = update.transparency;
         if (update.substance !== undefined) state.substance = update.substance;
         if (update.clipping !== undefined) state.clipping = update.clipping;
+        if (update.themeStrength !== undefined) state.themeStrength = update.themeStrength;
         if (update.syncManually !== undefined) state.syncManually = update.syncManually;
         if (update.transform !== undefined) Mat4.copy(state.transform, update.transform);
         if (update.markerActions !== undefined) state.markerActions = update.markerActions;
@@ -432,6 +448,7 @@ namespace Representation {
                 if (state.substance !== undefined) {
                     // TODO
                 }
+                if (state.themeStrength !== undefined) Visual.setThemeStrength(renderObject, state.themeStrength);
                 if (state.transform !== undefined) Visual.setTransform(renderObject, state.transform);
 
                 Representation.updateState(currentState, state);

+ 1 - 0
src/mol-repr/structure/complex-representation.ts

@@ -127,6 +127,7 @@ export function ComplexRepresentation<P extends StructureParams>(label: string,
             const remappedClipping = Clipping.remap(state.clipping, _structure);
             visual.setClipping(remappedClipping);
         }
+        if (state.themeStrength !== undefined && visual) visual.setThemeStrength(state.themeStrength);
         if (state.transform !== undefined && visual) visual.setTransform(state.transform);
         if (state.unitTransforms !== undefined && visual) {
             // Since ComplexVisuals always renders geometries between units, the application

+ 3 - 0
src/mol-repr/structure/complex-visual.ts

@@ -299,6 +299,9 @@ export function ComplexVisual<G extends Geometry, P extends StructureParams & Ge
         setClipping(clipping: Clipping) {
             Visual.setClipping(renderObject, clipping, lociApply, true);
         },
+        setThemeStrength(strength: { overpaint: number, transparency: number, substance: number }) {
+            Visual.setThemeStrength(renderObject, strength);
+        },
         destroy() {
             dispose?.(geometry);
             if (renderObject) {

+ 4 - 2
src/mol-repr/structure/units-representation.ts

@@ -222,7 +222,7 @@ export function UnitsRepresentation<P extends StructureParams>(label: string, ct
     }
 
     function setVisualState(visual: UnitsVisual<P>, group: Unit.SymmetryGroup, state: Partial<StructureRepresentationState>) {
-        const { visible, alphaFactor, pickable, overpaint, transparency, substance, clipping, transform, unitTransforms } = state;
+        const { visible, alphaFactor, pickable, overpaint, transparency, substance, clipping, themeStrength, transform, unitTransforms } = state;
 
         if (visible !== undefined) visual.setVisibility(visible);
         if (alphaFactor !== undefined) visual.setAlphaFactor(alphaFactor);
@@ -231,6 +231,7 @@ export function UnitsRepresentation<P extends StructureParams>(label: string, ct
         if (transparency !== undefined) visual.setTransparency(transparency, webgl);
         if (substance !== undefined) visual.setSubstance(substance, webgl);
         if (clipping !== undefined) visual.setClipping(clipping);
+        if (themeStrength !== undefined) visual.setThemeStrength(themeStrength);
         if (transform !== undefined) visual.setTransform(transform);
         if (unitTransforms !== undefined) {
             if (unitTransforms) {
@@ -243,7 +244,7 @@ export function UnitsRepresentation<P extends StructureParams>(label: string, ct
     }
 
     function setState(state: Partial<StructureRepresentationState>) {
-        const { visible, alphaFactor, pickable, overpaint, transparency, substance, clipping, transform, unitTransforms, syncManually, markerActions } = state;
+        const { visible, alphaFactor, pickable, overpaint, transparency, substance, clipping, themeStrength, transform, unitTransforms, syncManually, markerActions } = state;
         const newState: Partial<StructureRepresentationState> = {};
 
         if (visible !== _state.visible) newState.visible = visible;
@@ -261,6 +262,7 @@ export function UnitsRepresentation<P extends StructureParams>(label: string, ct
         if (clipping !== undefined && _structure) {
             newState.clipping = Clipping.remap(clipping, _structure);
         }
+        if (themeStrength !== undefined) newState.themeStrength = themeStrength;
         if (transform !== undefined && !Mat4.areEqual(transform, _state.transform, EPSILON)) {
             newState.transform = transform;
         }

+ 3 - 0
src/mol-repr/structure/units-visual.ts

@@ -381,6 +381,9 @@ export function UnitsVisual<G extends Geometry, P extends StructureParams & Geom
         setClipping(clipping: Clipping) {
             Visual.setClipping(renderObject, clipping, lociApply, true);
         },
+        setThemeStrength(strength: { overpaint: number, transparency: number, substance: number }) {
+            Visual.setThemeStrength(renderObject, strength);
+        },
         destroy() {
             dispose?.(geometry);
             if (renderObject) {

+ 9 - 0
src/mol-repr/visual.ts

@@ -55,6 +55,7 @@ interface Visual<D, P extends PD.Params> {
     setTransparency: (transparency: Transparency, webgl?: WebGLContext) => void
     setSubstance: (substance: Substance, webgl?: WebGLContext) => void
     setClipping: (clipping: Clipping) => void
+    setThemeStrength: (strength: { overpaint: number, transparency: number, substance: number }) => void
     destroy: () => void
     mustRecreate?: (data: D, props: PD.Values<P>, webgl?: WebGLContext) => boolean
 }
@@ -349,6 +350,14 @@ namespace Visual {
         ValueCell.updateIfChanged(dClipping, clipping.layers.length > 0);
     }
 
+    export function setThemeStrength(renderObject: GraphicsRenderObject | undefined, strength: { overpaint: number, transparency: number, substance: number }) {
+        if (renderObject) {
+            ValueCell.updateIfChanged(renderObject.values.uOverpaintStrength, strength.overpaint);
+            ValueCell.updateIfChanged(renderObject.values.uTransparencyStrength, strength.transparency);
+            ValueCell.updateIfChanged(renderObject.values.uSubstanceStrength, strength.substance);
+        }
+    }
+
     export function setTransform(renderObject: GraphicsRenderObject | undefined, transform?: Mat4, instanceTransforms?: Float32Array | null) {
         if (!renderObject || (!transform && !instanceTransforms)) return;
 

+ 3 - 0
src/mol-repr/volume/representation.ts

@@ -252,6 +252,9 @@ export function VolumeVisual<G extends Geometry, P extends VolumeParams & Geomet
         setClipping(clipping: Clipping) {
             return Visual.setClipping(renderObject, clipping, lociApply, true);
         },
+        setThemeStrength(strength: { overpaint: number, transparency: number, substance: number }) {
+            Visual.setThemeStrength(renderObject, strength);
+        },
         destroy() {
             dispose?.(geometry);
             if (renderObject) {