Browse Source

Merge branch 'master' of https://github.com/molstar/molstar into volume-theme

dsehnal 2 years ago
parent
commit
c80f52d4bc
50 changed files with 1292 additions and 350 deletions
  1. 20 2
      CHANGELOG.md
  2. 360 228
      package-lock.json
  3. 30 29
      package.json
  4. 5 3
      src/apps/docking-viewer/viewport.tsx
  5. 7 4
      src/examples/lighting/index.ts
  6. 9 0
      src/extensions/cellpack/model.ts
  7. 200 27
      src/extensions/rcsb/graphql/types.ts
  8. 3 3
      src/mol-canvas3d/passes/draw.ts
  9. 140 8
      src/mol-canvas3d/passes/postprocessing.ts
  10. 4 1
      src/mol-geo/geometry/overpaint-data.ts
  11. 4 1
      src/mol-geo/geometry/substance-data.ts
  12. 3 0
      src/mol-geo/geometry/transparency-data.ts
  13. 4 1
      src/mol-gl/renderable/schema.ts
  14. 9 5
      src/mol-gl/renderer.ts
  15. 1 1
      src/mol-gl/shader/chunks/apply-light-color.glsl.ts
  16. 3 0
      src/mol-gl/shader/chunks/assign-color-varying.glsl.ts
  17. 3 0
      src/mol-gl/shader/chunks/color-vert-params.glsl.ts
  18. 15 1
      src/mol-gl/shader/postprocessing.frag.ts
  19. 131 0
      src/mol-gl/shader/shadows.frag.ts
  20. 122 0
      src/mol-gl/webgl/compat.ts
  21. 22 4
      src/mol-gl/webgl/extensions.ts
  22. 1 1
      src/mol-io/reader/cif/schema/bird.ts
  23. 1 1
      src/mol-io/reader/cif/schema/ccd.ts
  24. 1 1
      src/mol-io/reader/cif/schema/mmcif.ts
  25. 12 9
      src/mol-math/geometry/symmetry-operator.ts
  26. 30 2
      src/mol-model-formats/structure/property/bonds/index-pair.ts
  27. 4 0
      src/mol-model/structure/model/types.ts
  28. 0 0
      src/mol-model/structure/model/types/saccharides.ts
  29. 1 1
      src/mol-model/structure/query/context.ts
  30. 2 1
      src/mol-model/structure/structure/properties.ts
  31. 1 1
      src/mol-model/structure/structure/structure.ts
  32. 8 1
      src/mol-model/structure/structure/unit/bonds/inter-compute.ts
  33. 7 1
      src/mol-model/structure/structure/unit/bonds/intra-compute.ts
  34. 15 6
      src/mol-plugin-state/helpers/structure-selection-query.ts
  35. 59 1
      src/mol-plugin-state/transforms/representation.ts
  36. 1 1
      src/mol-plugin-ui/skin/base/components/viewport.scss
  37. 2 0
      src/mol-plugin-ui/structure/quick-styles.tsx
  38. 3 0
      src/mol-plugin-ui/viewport/simple-settings.tsx
  39. 1 0
      src/mol-plugin/spec.ts
  40. 18 1
      src/mol-repr/representation.ts
  41. 1 0
      src/mol-repr/structure/complex-representation.ts
  42. 3 0
      src/mol-repr/structure/complex-visual.ts
  43. 4 2
      src/mol-repr/structure/units-representation.ts
  44. 3 0
      src/mol-repr/structure/units-visual.ts
  45. 1 1
      src/mol-repr/util.ts
  46. 9 0
      src/mol-repr/visual.ts
  47. 3 0
      src/mol-repr/volume/representation.ts
  48. 1 0
      src/mol-script/language/symbol-table/structure-query.ts
  49. 1 0
      src/mol-script/runtime/query/table.ts
  50. 4 1
      src/mol-script/script/mol-script/symbols.ts

+ 20 - 2
CHANGELOG.md

@@ -6,15 +6,33 @@ Note that since we don't clearly distinguish between a public and private interf
 
 ## [Unreleased]
 
-- Make `PluginContext.initContainer` checkered canvas background optional
+- Excluded common protein caps `NME` and `ACE` from the ligand selection query
+- Add screen-space shadow post-processing effect
 - Add `external-volume` theme (coloring of arbitrary geometries by user-selected volume)
 
+## [v3.25.1] - 2022-11-20
+
+- Fix edge-case in `Structure.eachUnitPair` with single-element units
+- Fix 'auto' structure-quality for coarse models
+
+## [v3.25.0] - 2022-11-16
+
+- Fix handling of gzipped assets (reverts #615)
+
+## [v3.24.0] - 2022-11-13
+
+- Make `PluginContext.initContainer` checkered canvas background optional
+- Store URL of downloaded assets to detect zip/gzip based on extension (#615)
+- Add optional `operator.key`; can be referenced in `IndexPairBonds`
+- Add overpaint/transparency/substance theme strength to representations
+- Fix viewport color for transparent background
+
 ## [v3.23.0] - 2022-10-19
 
 - Add `PluginContext.initContainer/mount/unmount` methods; these should make it easier to reuse a plugin context with both custom and built-in UI
 - Add `PluginContext.canvas3dInitialized`
 - `createPluginUI` now resolves after the 3d canvas has been initialized
-- Change EM Volume Streaming default from `Whote Structure` to `Auto`
+- Change EM Volume Streaming default from `Whole Structure` to `Auto`
 
 ## [v3.22.0] - 2022-10-17
 

File diff suppressed because it is too large
+ 360 - 228
package-lock.json


+ 30 - 29
package.json

@@ -1,6 +1,6 @@
 {
   "name": "molstar",
-  "version": "3.23.0",
+  "version": "3.25.1",
   "description": "A comprehensive macromolecular library.",
   "homepage": "https://github.com/molstar/molstar#readme",
   "repository": {
@@ -92,57 +92,58 @@
     "Panagiotis Tourlas <panagiot_tourlov@hotmail.com>",
     "Adam Midlik <midlik@gmail.com>",
     "Koya Sakuma <koya.sakuma.work@gmail.com>",
-    "Gianluca Tomasello <giagitom@gmail.com>"
+    "Gianluca Tomasello <giagitom@gmail.com>",
+    "Jason Pattle <jpattle@exscientia.co.uk>"
   ],
   "license": "MIT",
   "devDependencies": {
     "@graphql-codegen/add": "^3.2.1",
-    "@graphql-codegen/cli": "^2.13.7",
+    "@graphql-codegen/cli": "^2.15.0",
     "@graphql-codegen/time": "^3.2.1",
-    "@graphql-codegen/typescript": "^2.7.4",
+    "@graphql-codegen/typescript": "^2.8.3",
     "@graphql-codegen/typescript-graphql-files-modules": "^2.2.1",
-    "@graphql-codegen/typescript-graphql-request": "^4.5.6",
-    "@graphql-codegen/typescript-operations": "^2.5.4",
+    "@graphql-codegen/typescript-graphql-request": "^4.5.8",
+    "@graphql-codegen/typescript-operations": "^2.5.8",
     "@types/cors": "^2.8.12",
-    "@types/gl": "^4.1.1",
-    "@types/jest": "^29.1.2",
-    "@types/react": "^18.0.21",
-    "@types/react-dom": "^18.0.6",
-    "@typescript-eslint/eslint-plugin": "^5.40.0",
-    "@typescript-eslint/parser": "^5.40.0",
+    "@types/gl": "^6.0.2",
+    "@types/jest": "^29.2.3",
+    "@types/react": "^18.0.26",
+    "@types/react-dom": "^18.0.9",
+    "@typescript-eslint/eslint-plugin": "^5.45.0",
+    "@typescript-eslint/parser": "^5.45.0",
     "benchmark": "^2.1.4",
-    "concurrently": "^7.4.0",
+    "concurrently": "^7.6.0",
     "cpx2": "^4.2.0",
     "crypto-browserify": "^3.12.0",
-    "css-loader": "^6.7.1",
-    "eslint": "^8.25.0",
+    "css-loader": "^6.7.2",
+    "eslint": "^8.29.0",
     "extra-watch-webpack-plugin": "^1.0.3",
     "file-loader": "^6.2.0",
-    "fs-extra": "^10.1.0",
+    "fs-extra": "^11.1.0",
     "graphql": "^16.6.0",
     "http-server": "^14.1.1",
-    "jest": "^29.2.0",
-    "mini-css-extract-plugin": "^2.6.1",
+    "jest": "^29.3.1",
+    "mini-css-extract-plugin": "^2.7.1",
     "path-browserify": "^1.0.1",
     "raw-loader": "^4.0.2",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
-    "sass": "^1.55.0",
-    "sass-loader": "^13.1.0",
-    "simple-git": "^3.14.1",
+    "sass": "^1.56.1",
+    "sass-loader": "^13.2.0",
+    "simple-git": "^3.15.1",
     "stream-browserify": "^3.0.0",
     "style-loader": "^3.3.1",
     "ts-jest": "^29.0.3",
-    "typescript": "^4.8.4",
-    "webpack": "^5.74.0",
-    "webpack-cli": "^4.10.0"
+    "typescript": "^4.9.3",
+    "webpack": "^5.75.0",
+    "webpack-cli": "^5.0.0"
   },
   "dependencies": {
     "@types/argparse": "^2.0.10",
     "@types/benchmark": "^2.1.2",
     "@types/compression": "1.7.2",
     "@types/express": "^4.17.14",
-    "@types/node": "^16.11.66",
+    "@types/node": "^16.18.4",
     "@types/node-fetch": "^2.6.2",
     "@types/swagger-ui-dist": "3.30.1",
     "argparse": "^2.0.1",
@@ -151,12 +152,12 @@
     "cors": "^2.8.5",
     "express": "^4.18.2",
     "h264-mp4-encoder": "^1.0.12",
-    "immer": "^9.0.15",
+    "immer": "^9.0.16",
     "immutable": "^4.1.0",
     "node-fetch": "^2.6.7",
-    "rxjs": "^7.5.7",
-    "swagger-ui-dist": "^4.14.3",
-    "tslib": "^2.4.0",
+    "rxjs": "^7.6.0",
+    "swagger-ui-dist": "^4.15.5",
+    "tslib": "^2.4.1",
     "util.promisify": "^1.1.1",
     "xhr2": "^0.2.1"
   },

+ 5 - 3
src/apps/docking-viewer/viewport.tsx

@@ -31,7 +31,8 @@ function shinyStyle(plugin: PluginContext) {
         postprocessing: {
             ...plugin.canvas3d!.props.postprocessing,
             occlusion: { name: 'off', params: {} },
-            outline: { name: 'off', params: {} }
+            shadow: { name: 'off', params: {} },
+            outline: { name: 'off', params: {} },
         }
     } });
 }
@@ -48,13 +49,14 @@ function occlusionStyle(plugin: PluginContext) {
                 blurKernelSize: 15,
                 radius: 5,
                 samples: 32,
-                resolutionScale: 1
+                resolutionScale: 1,
             } },
             outline: { name: 'on', params: {
                 scale: 1.0,
                 threshold: 0.33,
                 color: Color(0x0000),
-            } }
+            } },
+            shadow: { name: 'off', params: {} },
         }
     } });
 }

+ 7 - 4
src/examples/lighting/index.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>
  */
@@ -25,7 +25,8 @@ const Canvas3DPresets = {
         canvas3d: <Preset>{
             postprocessing: {
                 occlusion: { name: 'on', params: { samples: 32, radius: 6, bias: 1.4, blurKernelSize: 15, resolutionScale: 1 } },
-                outline: { name: 'on', params: { scale: 1, threshold: 0.33, color: Color(0x000000) } }
+                outline: { name: 'on', params: { scale: 1, threshold: 0.33, color: Color(0x000000) } },
+                shadow: { name: 'off', params: {} },
             },
             renderer: {
                 ambientIntensity: 1.0,
@@ -37,7 +38,8 @@ const Canvas3DPresets = {
         canvas3d: <Preset>{
             postprocessing: {
                 occlusion: { name: 'on', params: { samples: 32, radius: 6, bias: 1.4, blurKernelSize: 15, resolutionScale: 1 } },
-                outline: { name: 'off', params: {} }
+                outline: { name: 'off', params: {} },
+                shadow: { name: 'off', params: {} },
             },
             renderer: {
                 ambientIntensity: 0.4,
@@ -50,7 +52,8 @@ const Canvas3DPresets = {
         canvas3d: <Preset>{
             postprocessing: {
                 occlusion: { name: 'off', params: {} },
-                outline: { name: 'off', params: {} }
+                outline: { name: 'off', params: {} },
+                shadow: { name: 'off', params: {} },
             },
             renderer: {
                 ambientIntensity: 0.4,

+ 9 - 0
src/extensions/cellpack/model.ts

@@ -606,6 +606,15 @@ export const LoadCellPackModel = StateAction.build({
                         resolutionScale: 1,
                     }
                 },
+                shadow: {
+                    name: 'on',
+                    params: {
+                        bias: 0.6,
+                        maxDistance: 80,
+                        steps: 3,
+                        tolerance: 1.0,
+                    }
+                },
                 outline: {
                     name: 'on',
                     params: {

+ 200 - 27
src/extensions/rcsb/graphql/types.ts

@@ -4,7 +4,7 @@ export type InputMaybe<T> = Maybe<T>;
 export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
 export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
 export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
-// Generated on 2022-08-20T16:36:05-07:00
+// Generated on 2022-12-03T21:55:37-08:00
 
 /** All built-in and custom scalars, mapped to their actual values */
 export type Scalars = {
@@ -343,6 +343,14 @@ export type Citation = {
    *
    */
   readonly journal_abbrev?: Maybe<Scalars['String']>;
+  /**
+   * Full name of the cited journal; relevant for journal articles.
+   *
+   * Examples:
+   * Journal of Molecular Biology
+   *
+   */
+  readonly journal_full?: Maybe<Scalars['String']>;
   /**
    * The American Society for Testing and Materials (ASTM) code
    *  assigned to the journal cited (also referred to as the CODEN
@@ -550,6 +558,7 @@ export type CoreBranchedEntityInstance = {
   readonly rcsb_id: Scalars['String'];
   readonly rcsb_latest_revision?: Maybe<RcsbLatestRevision>;
   readonly rcsb_ligand_neighbors?: Maybe<ReadonlyArray<Maybe<RcsbLigandNeighbors>>>;
+  readonly struct_asym?: Maybe<StructAsym>;
 };
 
 export type CoreChemComp = {
@@ -661,6 +670,7 @@ export type CoreEntry = {
   readonly exptl?: Maybe<ReadonlyArray<Maybe<Exptl>>>;
   readonly exptl_crystal?: Maybe<ReadonlyArray<Maybe<ExptlCrystal>>>;
   readonly exptl_crystal_grow?: Maybe<ReadonlyArray<Maybe<ExptlCrystalGrow>>>;
+  readonly ma_data?: Maybe<ReadonlyArray<Maybe<MaData>>>;
   /** Get all non-polymer (non-solvent) entities for this PDB entry. */
   readonly nonpolymer_entities?: Maybe<ReadonlyArray<Maybe<CoreNonpolymerEntity>>>;
   readonly pdbx_SG_project?: Maybe<ReadonlyArray<Maybe<PdbxSgProject>>>;
@@ -701,6 +711,7 @@ export type CoreEntry = {
   /** The list of content types associated with this entry. */
   readonly rcsb_associated_holdings?: Maybe<CurrentEntry>;
   readonly rcsb_binding_affinity?: Maybe<ReadonlyArray<Maybe<RcsbBindingAffinity>>>;
+  readonly rcsb_comp_model_provenance?: Maybe<RcsbCompModelProvenance>;
   readonly rcsb_entry_container_identifiers: RcsbEntryContainerIdentifiers;
   readonly rcsb_entry_group_membership?: Maybe<ReadonlyArray<Maybe<RcsbEntryGroupMembership>>>;
   readonly rcsb_entry_info: RcsbEntryInfo;
@@ -713,6 +724,7 @@ export type CoreEntry = {
    *
    */
   readonly rcsb_id: Scalars['String'];
+  readonly rcsb_ma_qa_metric_global?: Maybe<ReadonlyArray<Maybe<RcsbMaQaMetricGlobal>>>;
   readonly rcsb_primary_citation?: Maybe<RcsbPrimaryCitation>;
   readonly refine?: Maybe<ReadonlyArray<Maybe<Refine>>>;
   readonly refine_analyze?: Maybe<ReadonlyArray<Maybe<RefineAnalyze>>>;
@@ -786,6 +798,7 @@ export type CoreNonpolymerEntityInstance = {
   readonly rcsb_nonpolymer_instance_validation_score?: Maybe<ReadonlyArray<Maybe<RcsbNonpolymerInstanceValidationScore>>>;
   readonly rcsb_nonpolymer_struct_conn?: Maybe<ReadonlyArray<Maybe<RcsbNonpolymerStructConn>>>;
   readonly rcsb_target_neighbors?: Maybe<ReadonlyArray<Maybe<RcsbTargetNeighbors>>>;
+  readonly struct_asym?: Maybe<StructAsym>;
 };
 
 export type CorePfam = {
@@ -919,6 +932,7 @@ export type CorePolymerEntityInstance = {
   readonly rcsb_polymer_instance_feature?: Maybe<ReadonlyArray<Maybe<RcsbPolymerInstanceFeature>>>;
   readonly rcsb_polymer_instance_feature_summary?: Maybe<ReadonlyArray<Maybe<RcsbPolymerInstanceFeatureSummary>>>;
   readonly rcsb_polymer_struct_conn?: Maybe<ReadonlyArray<Maybe<RcsbPolymerStructConn>>>;
+  readonly struct_asym?: Maybe<StructAsym>;
 };
 
 export type CorePubmed = {
@@ -1284,9 +1298,9 @@ export type Em2dCrystalEntity = {
   readonly id: Scalars['String'];
   /** pointer to _em_image_processing.id in the EM_IMAGE_PROCESSING category. */
   readonly image_processing_id: Scalars['String'];
-  /** Unit-cell length a in Angstroms. */
+  /** Unit-cell length a in angstroms. */
   readonly length_a?: Maybe<Scalars['Float']>;
-  /** Unit-cell length b in Angstroms. */
+  /** Unit-cell length b in angstroms. */
   readonly length_b?: Maybe<Scalars['Float']>;
   /** Thickness of 2D crystal */
   readonly length_c?: Maybe<Scalars['Float']>;
@@ -1317,11 +1331,11 @@ export type Em3dCrystalEntity = {
   readonly id: Scalars['String'];
   /** pointer to _em_image_processing.id in the EM_IMAGE_PROCESSING category. */
   readonly image_processing_id: Scalars['String'];
-  /** Unit-cell length a in Angstroms. */
+  /** Unit-cell length a in angstroms. */
   readonly length_a?: Maybe<Scalars['Float']>;
-  /** Unit-cell length b in Angstroms. */
+  /** Unit-cell length b in angstroms. */
   readonly length_b?: Maybe<Scalars['Float']>;
-  /** Unit-cell length c in Angstroms. */
+  /** Unit-cell length c in angstroms. */
   readonly length_c?: Maybe<Scalars['Float']>;
   /**
    * Space group name.
@@ -1482,7 +1496,7 @@ export type Em3dReconstruction = {
    */
   readonly refinement_type?: Maybe<Scalars['String']>;
   /**
-   * The final resolution (in Angstroms)of the 3D reconstruction.
+   * The final resolution (in angstroms)of the 3D reconstruction.
    *
    * Examples:
    * null, null
@@ -1558,7 +1572,7 @@ export type EmDiffractionShell = {
    */
   readonly fourier_space_coverage?: Maybe<Scalars['Float']>;
   /**
-   * High resolution limit for this shell (Angstroms)
+   * High resolution limit for this shell (angstroms)
    *
    * Examples:
    * null
@@ -1568,7 +1582,7 @@ export type EmDiffractionShell = {
   /** Unique identifier for the category em_diffraction_shell */
   readonly id: Scalars['String'];
   /**
-   * Low resolution limit for this shell (Angstroms)
+   * Low resolution limit for this shell (angstroms)
    *
    * Examples:
    * null
@@ -1614,7 +1628,7 @@ export type EmDiffractionStats = {
    */
   readonly fourier_space_coverage?: Maybe<Scalars['Float']>;
   /**
-   * High resolution limit of the structure factor data, in Angstroms
+   * High resolution limit of the structure factor data, in angstroms
    *
    * Examples:
    * null
@@ -1998,12 +2012,12 @@ export type EmImaging = {
   /** The magnification indicated by the microscope readout. */
   readonly nominal_magnification?: Maybe<Scalars['Int']>;
   /**
-   * The specimen temperature maximum (degrees Kelvin) for the duration
+   * The specimen temperature maximum (kelvin) for the duration
    *  of imaging.
    */
   readonly recording_temperature_maximum?: Maybe<Scalars['Float']>;
   /**
-   * The specimen temperature minimum (degrees Kelvin) for the duration
+   * The specimen temperature minimum (kelvin) for the duration
    *  of imaging.
    */
   readonly recording_temperature_minimum?: Maybe<Scalars['Float']>;
@@ -2028,7 +2042,7 @@ export type EmImaging = {
   /** Foreign key to the EM_SPECIMEN category */
   readonly specimen_id?: Maybe<Scalars['String']>;
   /**
-   * The mean specimen stage temperature (degrees Kelvin) during imaging
+   * The mean specimen stage temperature (in kelvin) during imaging
    *  in the microscope.
    */
   readonly temperature?: Maybe<Scalars['Float']>;
@@ -2215,7 +2229,7 @@ export type EmStaining = {
 };
 
 export type EmVitrification = {
-  /** The temperature (in degrees Kelvin) of the sample just prior to vitrification. */
+  /** The temperature (in kelvin) of the sample just prior to vitrification. */
   readonly chamber_temperature?: Maybe<Scalars['Float']>;
   /**
    * This is the name of the cryogen.
@@ -2259,7 +2273,7 @@ export type EmVitrification = {
   /** This data item is a pointer to _em_specimen.id */
   readonly specimen_id: Scalars['String'];
   /**
-   * The vitrification temperature (in degrees Kelvin), e.g.,
+   * The vitrification temperature (in kelvin), e.g.,
    *   temperature of the plunge instrument cryogen bath.
    */
   readonly temp?: Maybe<Scalars['Float']>;
@@ -2364,6 +2378,14 @@ export type EntityPoly = {
    *
    */
   readonly pdbx_seq_one_letter_code_can?: Maybe<Scalars['String']>;
+  /**
+   * Evidence for the assignment of the polymer sequence.
+   *
+   * Allowable values:
+   * depositor provided, derived from coordinates
+   *
+   */
+  readonly pdbx_sequence_evidence_code?: Maybe<Scalars['String']>;
   /**
    * The PDB strand/chain id(s) corresponding to this polymer entity.
    *
@@ -3005,6 +3027,8 @@ export type Entry = {
    *  identifier.
    */
   readonly id: Scalars['String'];
+  /** An identifier for the model collection associated with the entry. */
+  readonly ma_collection_id?: Maybe<Scalars['String']>;
 };
 
 export type Exptl = {
@@ -3263,6 +3287,29 @@ export type InterfacePartnerFeatureFeaturePositions = {
   readonly values?: Maybe<ReadonlyArray<Maybe<Scalars['Float']>>>;
 };
 
+export type MaData = {
+  /**
+   * The type of data held in the dataset.
+   *
+   * Allowable values:
+   * coevolution MSA, input structure, model coordinates, other, polymeric template library, spatial restraints, target, target-template alignment, template structure
+   *
+   */
+  readonly content_type?: Maybe<Scalars['String']>;
+  /** Details for other content types. */
+  readonly content_type_other_details?: Maybe<Scalars['String']>;
+  /** A unique identifier for the data. */
+  readonly id: Scalars['Int'];
+  /**
+   * An author-given name for the content held in the dataset.
+   *
+   * Examples:
+   * NMR NOE Distances, Target Template Alignment, Coevolution Data
+   *
+   */
+  readonly name?: Maybe<Scalars['String']>;
+};
+
 export type MethodDetails = {
   /** A description of special aspects of the clustering process */
   readonly description?: Maybe<Scalars['String']>;
@@ -4199,7 +4246,7 @@ export type PdbxNmrExptlSampleConditions = {
    */
   readonly pressure_units?: Maybe<Scalars['String']>;
   /**
-   * The temperature (in Kelvin) at which NMR data were
+   * The temperature (in kelvin) at which NMR data were
    *  collected.
    */
   readonly temperature?: Maybe<Scalars['String']>;
@@ -4453,7 +4500,7 @@ export type PdbxPrdAudit = {
    * An identifier for the wwPDB site creating or modifying the molecule.
    *
    * Allowable values:
-   * BMRB, PDBC, PDBJ, PDBe, RCSB
+   * BMRB, PDBC, PDBE, PDBJ, RCSB
    *
    */
   readonly processing_site?: Maybe<Scalars['String']>;
@@ -6817,7 +6864,7 @@ export type Query = {
   readonly assemblies?: Maybe<ReadonlyArray<Maybe<CoreAssembly>>>;
   /** Get an assembly given the PDB ID and ASSEMBLY ID. Here ASSEMBLY ID is '1', '2', '3', etc. or 'deposited' for deposited coordinates. */
   readonly assembly?: Maybe<CoreAssembly>;
-  /** Get a list of PDB branched entities given a list of ENTITY IDs. Here ENTITY ID is a compound identifier that includes entry_id and entity_id separated by '_', e.g. 1XXX_1. */
+  /** Get a list of PDB branched entities given a list of ENTITY IDs. Here ENTITY ID is a compound identifier that includes entry_id and entity_id separated by '_', e.g. 1XXX_1. Note that the ENTRY ID part must be upper case. */
   readonly branched_entities?: Maybe<ReadonlyArray<Maybe<CoreBranchedEntity>>>;
   /** Get a PDB branched entity, given the PDB ID and ENTITY ID. Here ENTITY ID is a '1', '2', '3', etc. */
   readonly branched_entity?: Maybe<CoreBranchedEntity>;
@@ -6841,9 +6888,9 @@ export type Query = {
   readonly group_provenance?: Maybe<GroupProvenance>;
   /** Get a pairwise polymeric interface given the PDB ID, ASSEMBLY ID and INTERFACE ID. */
   readonly interface?: Maybe<CoreInterface>;
-  /** Get a list of pairwise polymeric interfaces given a list of INTERFACE IDs. Here INTERFACE ID is a compound identifier that includes entry_id, assembly_id and interface_id e.g. 1XXX-1.1. */
+  /** Get a list of pairwise polymeric interfaces given a list of INTERFACE IDs. Here INTERFACE ID is a compound identifier that includes entry_id, assembly_id and interface_id e.g. 1XXX-1.1. Note that the ENTRY ID part must be upper case. */
   readonly interfaces?: Maybe<ReadonlyArray<Maybe<CoreInterface>>>;
-  /** Get a list of PDB non-polymer entities given a list of ENTITY IDs. Here ENTITY ID is a compound identifier that includes entry_id and entity_id separated by '_', e.g. 1XXX_1. */
+  /** Get a list of PDB non-polymer entities given a list of ENTITY IDs. Here ENTITY ID is a compound identifier that includes entry_id and entity_id separated by '_', e.g. 1XXX_1. Note that the ENTRY ID part must be upper case. */
   readonly nonpolymer_entities?: Maybe<ReadonlyArray<Maybe<CoreNonpolymerEntity>>>;
   /** Get a PDB non-polymer entity, given the PDB ID and ENTITY ID. Here ENTITY ID is a '1', '2', '3', etc. */
   readonly nonpolymer_entity?: Maybe<CoreNonpolymerEntity>;
@@ -6851,7 +6898,7 @@ export type Query = {
   readonly nonpolymer_entity_instance?: Maybe<CoreNonpolymerEntityInstance>;
   /** Get a list of PDB non-polymer entity instances (chains), given the list of ENTITY INSTANCE IDs. Here ENTITY INSTANCE ID identifies structural element in the asymmetric unit, e.g. 'A', 'B', etc. */
   readonly nonpolymer_entity_instances?: Maybe<ReadonlyArray<Maybe<CoreNonpolymerEntityInstance>>>;
-  /** Get a list of PDB polymer entities given a list of ENTITY IDs. Here ENTITY ID is a compound identifier that includes entry_id and entity_id separated by '_', e.g. 1XXX_1. */
+  /** Get a list of PDB polymer entities given a list of ENTITY IDs. Here ENTITY ID is a compound identifier that includes entry_id and entity_id separated by '_', e.g. 1XXX_1. Note that the ENTRY ID part must be upper case. */
   readonly polymer_entities?: Maybe<ReadonlyArray<Maybe<CorePolymerEntity>>>;
   /** Get a PDB polymer entity, given the PDB ID and ENTITY ID. Here ENTITY ID is a '1', '2', '3', etc. */
   readonly polymer_entity?: Maybe<CorePolymerEntity>;
@@ -8421,6 +8468,29 @@ export type RcsbClusterMembership = {
   readonly identity?: Maybe<Scalars['Int']>;
 };
 
+export type RcsbCompModelProvenance = {
+  /**
+   * Entry identifier corresponding to the computed structure model.
+   *
+   * Examples:
+   * AF-P60325-F1, ma-bak-cepc-0019
+   *
+   */
+  readonly entry_id: Scalars['String'];
+  /**
+   * Source database for the computed structure model.
+   *
+   * Allowable values:
+   * AlphaFoldDB, ModelArchive
+   *
+   */
+  readonly source_db?: Maybe<Scalars['String']>;
+  /** Source filename for the computed structure model. */
+  readonly source_filename?: Maybe<Scalars['String']>;
+  /** Source URL for computed structure model file. */
+  readonly source_url?: Maybe<Scalars['String']>;
+};
+
 export type RcsbEntityHostOrganism = {
   /**
    * The beginning polymer sequence position for the polymer section corresponding
@@ -8657,7 +8727,7 @@ export type RcsbEntitySourceOrganism = {
    * A code indicating the provenance of the source organism details for the entity
    *
    * Allowable values:
-   * PDB Primary Data
+   * PDB Primary Data, UniProt
    *
    */
   readonly provenance_source?: Maybe<Scalars['String']>;
@@ -8731,7 +8801,7 @@ export type RcsbEntryContainerIdentifiers = {
    * Entry identifier for the container.
    *
    * Examples:
-   * 1KIP, 4HHB
+   * 4HHB, AF_AFP60325F1, MA_MABAKCEPC0019
    *
    */
   readonly entry_id: Scalars['String'];
@@ -8879,6 +8949,15 @@ export type RcsbEntryInfo = {
    *
    */
   readonly na_polymer_entity_types?: Maybe<Scalars['String']>;
+  /**
+   * This data item identifies secondary structure
+   *  features of nucleic acids in the entry.
+   *
+   * Allowable values:
+   * a-form double helix, b-form double helix, bulge loop, double helix, four-way junction, hairpin loop, internal loop, mismatched base pair, other right-handed double helix, parallel strands, quadruple helix, tetraloop, three-way junction, triple helix, two-way junction, z-form double helix
+   *
+   */
+  readonly ndb_struct_conf_na_feature_combined?: Maybe<ReadonlyArray<Maybe<Scalars['String']>>>;
   /** Bound nonpolymer components in this entry. */
   readonly nonpolymer_bound_components?: Maybe<ReadonlyArray<Maybe<Scalars['String']>>>;
   /** The number of distinct non-polymer entities in the structure entry exclusive of solvent. */
@@ -8955,6 +9034,21 @@ export type RcsbEntryInfo = {
   readonly software_programs_combined?: Maybe<ReadonlyArray<Maybe<Scalars['String']>>>;
   /** The number of distinct solvent entities per deposited structure model. */
   readonly solvent_entity_count?: Maybe<Scalars['Int']>;
+  /**
+   * Indicates if the structure was determined using experimental or computational methods.
+   *
+   * Allowable values:
+   * computational, experimental
+   *
+   */
+  readonly structure_determination_methodology: Scalars['String'];
+  /**
+   * Indicates the priority of the value in _rcsb_entry_info.structure_determination_methodology.
+   *  The lower the number the higher the priority.
+   *  Priority values for "experimental" structures is currently set to 10 and
+   *  the values for "computational" structures is set to 100.
+   */
+  readonly structure_determination_methodology_priority?: Maybe<Scalars['Int']>;
 };
 
 export type RcsbEntryInfoDiffrnResolutionHigh = {
@@ -9294,6 +9388,49 @@ export type RcsbLigandNeighbors = {
   readonly seq_id?: Maybe<Scalars['Int']>;
 };
 
+export type RcsbMaQaMetricGlobal = {
+  readonly ma_qa_metric_global?: Maybe<ReadonlyArray<Maybe<RcsbMaQaMetricGlobalMaQaMetricGlobal>>>;
+  /** The model identifier. */
+  readonly model_id: Scalars['Int'];
+};
+
+export type RcsbMaQaMetricGlobalMaQaMetricGlobal = {
+  /**
+   * Description of the global QA metric.
+   *
+   * Examples:
+   * confidence score predicting accuracy according to the CA-only Local Distance Difference Test (lDDT-CA) in [0,100]
+   *
+   */
+  readonly description?: Maybe<Scalars['String']>;
+  /**
+   * Name of the global QA metric.
+   *
+   * Examples:
+   * pLDDT
+   *
+   */
+  readonly name: Scalars['String'];
+  /**
+   * The type of global QA metric.
+   *
+   * Allowable values:
+   * PAE, contact probability, distance, energy, ipTM, normalized score, other, pLDDT, pLDDT all-atom, pLDDT all-atom in [0,1], pLDDT in [0,1], pTM, zscore
+   *
+   */
+  readonly type: Scalars['String'];
+  /** Details for other type of global QA metric. */
+  readonly type_other_details?: Maybe<Scalars['String']>;
+  /**
+   * Value of the global QA metric.
+   *
+   * Examples:
+   * null
+   *
+   */
+  readonly value: Scalars['Float'];
+};
+
 export type RcsbMembraneLineage = {
   /** Hierarchy depth. */
   readonly depth?: Maybe<Scalars['Int']>;
@@ -10332,7 +10469,7 @@ export type RcsbPolymerEntityContainerIdentifiersReferenceSequenceIdentifiers =
    * Source of the reference database assignment
    *
    * Allowable values:
-   * PDB, RCSB, SIFTS
+   * PDB, RCSB, SIFTS, UniProt
    *
    */
   readonly provenance_source?: Maybe<Scalars['String']>;
@@ -10737,7 +10874,7 @@ export type RcsbPolymerInstanceFeature = {
    * A type or category of the feature.
    *
    * Allowable values:
-   * ANGLE_OUTLIER, BINDING_SITE, BOND_OUTLIER, C-MANNOSYLATION_SITE, CATH, CIS-PEPTIDE, ECOD, HELIX_P, MEMBRANE_SEGMENT, MOGUL_ANGLE_OUTLIER, MOGUL_BOND_OUTLIER, N-GLYCOSYLATION_SITE, O-GLYCOSYLATION_SITE, RAMACHANDRAN_OUTLIER, ROTAMER_OUTLIER, RSCC_OUTLIER, RSRZ_OUTLIER, S-GLYCOSYLATION_SITE, SABDAB_ANTIBODY_HEAVY_CHAIN_SUBCLASS, SABDAB_ANTIBODY_LIGHT_CHAIN_SUBCLASS, SABDAB_ANTIBODY_LIGHT_CHAIN_TYPE, SCOP, SCOP2B_SUPERFAMILY, SCOP2_FAMILY, SCOP2_SUPERFAMILY, SHEET, STEREO_OUTLIER, UNASSIGNED_SEC_STRUCT, UNOBSERVED_ATOM_XYZ, UNOBSERVED_RESIDUE_XYZ, ZERO_OCCUPANCY_ATOM_XYZ, ZERO_OCCUPANCY_RESIDUE_XYZ, ASA
+   * ANGLE_OUTLIER, BEND, BINDING_SITE, BOND_OUTLIER, C-MANNOSYLATION_SITE, CATH, CIS-PEPTIDE, ECOD, HELIX_P, HELX_LH_PP_P, HELX_RH_3T_P, HELX_RH_AL_P, HELX_RH_PI_P, MA_QA_METRIC_LOCAL_TYPE_CONTACT_PROBABILITY, MA_QA_METRIC_LOCAL_TYPE_DISTANCE, MA_QA_METRIC_LOCAL_TYPE_ENERGY, MA_QA_METRIC_LOCAL_TYPE_IPTM, MA_QA_METRIC_LOCAL_TYPE_NORMALIZED_SCORE, MA_QA_METRIC_LOCAL_TYPE_OTHER, MA_QA_METRIC_LOCAL_TYPE_PAE, MA_QA_METRIC_LOCAL_TYPE_PLDDT, MA_QA_METRIC_LOCAL_TYPE_PLDDT_ALL-ATOM, MA_QA_METRIC_LOCAL_TYPE_PLDDT_ALL-ATOM_[0,1], MA_QA_METRIC_LOCAL_TYPE_PLDDT_[0,1], MA_QA_METRIC_LOCAL_TYPE_PTM, MA_QA_METRIC_LOCAL_TYPE_ZSCORE, MEMBRANE_SEGMENT, MOGUL_ANGLE_OUTLIER, MOGUL_BOND_OUTLIER, N-GLYCOSYLATION_SITE, O-GLYCOSYLATION_SITE, RAMACHANDRAN_OUTLIER, ROTAMER_OUTLIER, RSCC_OUTLIER, RSRZ_OUTLIER, S-GLYCOSYLATION_SITE, SABDAB_ANTIBODY_HEAVY_CHAIN_SUBCLASS, SABDAB_ANTIBODY_LIGHT_CHAIN_SUBCLASS, SABDAB_ANTIBODY_LIGHT_CHAIN_TYPE, SCOP, SCOP2B_SUPERFAMILY, SCOP2_FAMILY, SCOP2_SUPERFAMILY, SHEET, STEREO_OUTLIER, STRN, TURN_TY1_P, UNASSIGNED_SEC_STRUCT, UNOBSERVED_ATOM_XYZ, UNOBSERVED_RESIDUE_XYZ, ZERO_OCCUPANCY_ATOM_XYZ, ZERO_OCCUPANCY_RESIDUE_XYZ, ASA
    *
    */
   readonly type?: Maybe<Scalars['String']>;
@@ -10748,7 +10885,7 @@ export type RcsbPolymerInstanceFeatureAdditionalProperties = {
    * The additional property name.
    *
    * Allowable values:
-   * CATH_DOMAIN_ID, CATH_NAME, ECOD_DOMAIN_ID, ECOD_FAMILY_NAME, OMEGA_ANGLE, PARTNER_ASYM_ID, PARTNER_BOND_DISTANCE, PARTNER_COMP_ID, SCOP2_DOMAIN_ID, SCOP2_FAMILY_ID, SCOP2_FAMILY_NAME, SCOP2_SUPERFAMILY_ID, SCOP2_SUPERFAMILY_NAME, SCOP_DOMAIN_ID, SCOP_NAME, SCOP_SUN_ID, SHEET_SENSE
+   * CATH_DOMAIN_ID, CATH_NAME, ECOD_DOMAIN_ID, ECOD_FAMILY_NAME, MODELCIF_MODEL_ID, OMEGA_ANGLE, PARTNER_ASYM_ID, PARTNER_BOND_DISTANCE, PARTNER_COMP_ID, SCOP2_DOMAIN_ID, SCOP2_FAMILY_ID, SCOP2_FAMILY_NAME, SCOP2_SUPERFAMILY_ID, SCOP2_SUPERFAMILY_NAME, SCOP_DOMAIN_ID, SCOP_NAME, SCOP_SUN_ID, SHEET_SENSE
    *
    */
   readonly name?: Maybe<Scalars['String']>;
@@ -10818,7 +10955,7 @@ export type RcsbPolymerInstanceFeatureSummary = {
    * Type or category of the feature.
    *
    * Allowable values:
-   * ANGLE_OUTLIER, BINDING_SITE, BOND_OUTLIER, C-MANNOSYLATION_SITE, CATH, CIS-PEPTIDE, ECOD, HELIX_P, MEMBRANE_SEGMENT, MOGUL_ANGLE_OUTLIER, MOGUL_BOND_OUTLIER, N-GLYCOSYLATION_SITE, O-GLYCOSYLATION_SITE, RAMACHANDRAN_OUTLIER, ROTAMER_OUTLIER, RSCC_OUTLIER, RSRZ_OUTLIER, S-GLYCOSYLATION_SITE, SABDAB_ANTIBODY_HEAVY_CHAIN_SUBCLASS, SABDAB_ANTIBODY_LIGHT_CHAIN_SUBCLASS, SABDAB_ANTIBODY_LIGHT_CHAIN_TYPE, SAbDab Antibody Heavy Chain Subclass, SAbDab Antibody Light Chain Subclass, SAbDab Antibody Light Chain Type, SCOP, SCOP2 Family, SCOP2 Superfamily, SCOP2B Superfamily, SCOP2B_SUPERFAMILY, SCOP2_FAMILY, SCOP2_SUPERFAMILY, SHEET, STEREO_OUTLIER, UNASSIGNED_SEC_STRUCT, UNOBSERVED_ATOM_XYZ, UNOBSERVED_RESIDUE_XYZ, ZERO_OCCUPANCY_ATOM_XYZ, ZERO_OCCUPANCY_RESIDUE_XYZ
+   * ANGLE_OUTLIER, BEND, BINDING_SITE, BOND_OUTLIER, C-MANNOSYLATION_SITE, CATH, CIS-PEPTIDE, ECOD, HELIX_P, HELX_LH_PP_P, HELX_RH_3T_P, HELX_RH_AL_P, HELX_RH_PI_P, MA_QA_METRIC_LOCAL_TYPE_CONTACT_PROBABILITY, MA_QA_METRIC_LOCAL_TYPE_DISTANCE, MA_QA_METRIC_LOCAL_TYPE_ENERGY, MA_QA_METRIC_LOCAL_TYPE_IPTM, MA_QA_METRIC_LOCAL_TYPE_NORMALIZED_SCORE, MA_QA_METRIC_LOCAL_TYPE_OTHER, MA_QA_METRIC_LOCAL_TYPE_PAE, MA_QA_METRIC_LOCAL_TYPE_PLDDT, MA_QA_METRIC_LOCAL_TYPE_PLDDT_ALL-ATOM, MA_QA_METRIC_LOCAL_TYPE_PLDDT_ALL-ATOM_[0,1], MA_QA_METRIC_LOCAL_TYPE_PLDDT_[0,1], MA_QA_METRIC_LOCAL_TYPE_PTM, MA_QA_METRIC_LOCAL_TYPE_ZSCORE, MEMBRANE_SEGMENT, MOGUL_ANGLE_OUTLIER, MOGUL_BOND_OUTLIER, N-GLYCOSYLATION_SITE, O-GLYCOSYLATION_SITE, RAMACHANDRAN_OUTLIER, ROTAMER_OUTLIER, RSCC_OUTLIER, RSRZ_OUTLIER, S-GLYCOSYLATION_SITE, SABDAB_ANTIBODY_HEAVY_CHAIN_SUBCLASS, SABDAB_ANTIBODY_LIGHT_CHAIN_SUBCLASS, SABDAB_ANTIBODY_LIGHT_CHAIN_TYPE, SCOP, SCOP2B_SUPERFAMILY, SCOP2_FAMILY, SCOP2_SUPERFAMILY, SHEET, STEREO_OUTLIER, STRN, TURN_TY1_P, UNASSIGNED_SEC_STRUCT, UNOBSERVED_ATOM_XYZ, UNOBSERVED_RESIDUE_XYZ, ZERO_OCCUPANCY_ATOM_XYZ, ZERO_OCCUPANCY_RESIDUE_XYZ
    *
    */
   readonly type?: Maybe<Scalars['String']>;
@@ -13377,6 +13514,11 @@ export type ReflnsShell = {
 };
 
 export type Software = {
+  /**
+   * This data item is a pointer to _citation.id in the CITATION
+   *  category.
+   */
+  readonly citation_id?: Maybe<Scalars['String']>;
   /**
    * The classification of the program according to its
    *  major function.
@@ -13527,6 +13669,37 @@ export type Struct = {
   readonly title?: Maybe<Scalars['String']>;
 };
 
+export type StructAsym = {
+  /**
+   * This data item is a pointer to _atom_site.pdbx_PDB_strand_id the
+   *  ATOM_SITE category.
+   *
+   * Examples:
+   * 1ABC
+   *
+   */
+  readonly pdbx_PDB_id?: Maybe<Scalars['String']>;
+  /**
+   * This data item is a pointer to _atom_site.ndb_alias_strand_id the
+   *  ATOM_SITE category.
+   */
+  readonly pdbx_alt_id?: Maybe<Scalars['String']>;
+  /**
+   * This data item gives the order of the structural elements in the
+   *  ATOM_SITE category.
+   */
+  readonly pdbx_order?: Maybe<Scalars['Int']>;
+  /**
+   * This data item describes the general type of the structural elements
+   *  in the ATOM_SITE category.
+   *
+   * Allowable values:
+   * ATOMN, ATOMP, ATOMS, HETAC, HETAD, HETAI, HETAIN, HETAS, HETIC
+   *
+   */
+  readonly pdbx_type?: Maybe<Scalars['String']>;
+};
+
 export type StructKeywords = {
   /**
    * Terms characterizing the macromolecular structure.

+ 3 - 3
src/mol-canvas3d/passes/draw.ts

@@ -150,7 +150,7 @@ export class DrawPass {
                 }
             }
 
-            this.postprocessing.render(camera, false, transparentBackground, renderer.props.backgroundColor, postprocessingProps);
+            this.postprocessing.render(camera, false, transparentBackground, renderer.props.backgroundColor, postprocessingProps, renderer.light);
         }
 
         this.depthTextureOpaque.detachFramebuffer(this.colorTarget.framebuffer, 'depth');
@@ -204,7 +204,7 @@ export class DrawPass {
                 }
             }
 
-            this.postprocessing.render(camera, false, transparentBackground, renderer.props.backgroundColor, postprocessingProps);
+            this.postprocessing.render(camera, false, transparentBackground, renderer.props.backgroundColor, postprocessingProps, renderer.light);
         }
 
         // render transparent primitives and volumes
@@ -268,7 +268,7 @@ export class DrawPass {
                     }
                 }
 
-                this.postprocessing.render(camera, false, transparentBackground, renderer.props.backgroundColor, postprocessingProps);
+                this.postprocessing.render(camera, false, transparentBackground, renderer.props.backgroundColor, postprocessingProps, renderer.light);
 
                 if (!this.packedDepth) {
                     this.depthTextureOpaque.attachFramebuffer(this.postprocessing.target.framebuffer, 'depth');

+ 140 - 8
src/mol-canvas3d/passes/postprocessing.ts

@@ -3,6 +3,7 @@
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  * @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
+ * @author Ludovic Autin <ludovic.autin@gmail.com>
  */
 
 import { CopyRenderable, createCopyRenderable, QuadSchema, QuadValues } from '../../mol-gl/compute/util';
@@ -30,6 +31,8 @@ import { SmaaParams, SmaaPass } from './smaa';
 import { isTimingMode } from '../../mol-util/debug';
 import { BackgroundParams, BackgroundPass } from './background';
 import { AssetManager } from '../../mol-util/assets';
+import { Light } from '../../mol-gl/renderer';
+import { shadows_frag } from '../../mol-gl/shader/shadows.frag';
 
 const OutlinesSchema = {
     ...QuadSchema,
@@ -69,6 +72,64 @@ function getOutlinesRenderable(ctx: WebGLContext, depthTextureOpaque: Texture, d
     return createComputeRenderable(renderItem, values);
 }
 
+const ShadowsSchema = {
+    ...QuadSchema,
+    tDepth: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
+    uTexSize: UniformSpec('v2'),
+
+    uProjection: UniformSpec('m4'),
+    uInvProjection: UniformSpec('m4'),
+    uBounds: UniformSpec('v4'),
+
+    dOrthographic: DefineSpec('number'),
+    uNear: UniformSpec('f'),
+    uFar: UniformSpec('f'),
+
+    dSteps: DefineSpec('number'),
+    uMaxDistance: UniformSpec('f'),
+    uTolerance: UniformSpec('f'),
+    uBias: UniformSpec('f'),
+
+    uLightDirection: UniformSpec('v3[]'),
+    uLightColor: UniformSpec('v3[]'),
+    dLightCount: DefineSpec('number'),
+};
+type ShadowsRenderable = ComputeRenderable<Values<typeof ShadowsSchema>>
+
+function getShadowsRenderable(ctx: WebGLContext, depthTexture: Texture): ShadowsRenderable {
+    const width = depthTexture.getWidth();
+    const height = depthTexture.getHeight();
+
+    const values: Values<typeof ShadowsSchema> = {
+        ...QuadValues,
+        tDepth: ValueCell.create(depthTexture),
+        uTexSize: ValueCell.create(Vec2.create(width, height)),
+
+        uProjection: ValueCell.create(Mat4.identity()),
+        uInvProjection: ValueCell.create(Mat4.identity()),
+        uBounds: ValueCell.create(Vec4()),
+
+        dOrthographic: ValueCell.create(0),
+        uNear: ValueCell.create(1),
+        uFar: ValueCell.create(10000),
+
+        dSteps: ValueCell.create(1),
+        uMaxDistance: ValueCell.create(3.0),
+        uTolerance: ValueCell.create(1.0),
+        uBias: ValueCell.create(0.6),
+
+        uLightDirection: ValueCell.create([]),
+        uLightColor: ValueCell.create([]),
+        dLightCount: ValueCell.create(0),
+    };
+
+    const schema = { ...ShadowsSchema };
+    const shaderCode = ShaderCode('shadows', quad_vert, shadows_frag);
+    const renderItem = createComputeRenderItem(ctx, 'triangles', shaderCode, schema, values);
+
+    return createComputeRenderable(renderItem, values);
+}
+
 const SsaoSchema = {
     ...QuadSchema,
     tDepth: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
@@ -204,6 +265,7 @@ const PostprocessingSchema = {
     tColor: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
     tDepthOpaque: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
     tDepthTransparent: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
+    tShadows: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
     tOutlines: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
     uTexSize: UniformSpec('v2'),
 
@@ -221,19 +283,22 @@ const PostprocessingSchema = {
     dOcclusionEnable: DefineSpec('boolean'),
     uOcclusionOffset: UniformSpec('v2'),
 
+    dShadowEnable: DefineSpec('boolean'),
+
     dOutlineEnable: DefineSpec('boolean'),
     dOutlineScale: DefineSpec('number'),
     uOutlineThreshold: UniformSpec('f'),
 };
 type PostprocessingRenderable = ComputeRenderable<Values<typeof PostprocessingSchema>>
 
-function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, depthTextureOpaque: Texture, depthTextureTransparent: Texture, outlinesTexture: Texture, ssaoDepthTexture: Texture): PostprocessingRenderable {
+function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, depthTextureOpaque: Texture, depthTextureTransparent: Texture, shadowsTexture: Texture, outlinesTexture: Texture, ssaoDepthTexture: Texture): PostprocessingRenderable {
     const values: Values<typeof PostprocessingSchema> = {
         ...QuadValues,
         tSsaoDepth: ValueCell.create(ssaoDepthTexture),
         tColor: ValueCell.create(colorTexture),
         tDepthOpaque: ValueCell.create(depthTextureOpaque),
         tDepthTransparent: ValueCell.create(depthTextureTransparent),
+        tShadows: ValueCell.create(shadowsTexture),
         tOutlines: ValueCell.create(outlinesTexture),
         uTexSize: ValueCell.create(Vec2.create(colorTexture.getWidth(), colorTexture.getHeight())),
 
@@ -251,6 +316,8 @@ function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, d
         dOcclusionEnable: ValueCell.create(true),
         uOcclusionOffset: ValueCell.create(Vec2.create(0, 0)),
 
+        dShadowEnable: ValueCell.create(false),
+
         dOutlineEnable: ValueCell.create(false),
         dOutlineScale: ValueCell.create(1),
         uOutlineThreshold: ValueCell.create(0.33),
@@ -274,6 +341,15 @@ export const PostprocessingParams = {
         }),
         off: PD.Group({})
     }, { cycle: true, description: 'Darken occluded crevices with the ambient occlusion effect' }),
+    shadow: PD.MappedStatic('off', {
+        on: PD.Group({
+            steps: PD.Numeric(1, { min: 1, max: 64, step: 1 }),
+            bias: PD.Numeric(0.6, { min: 0.0, max: 1.0, step: 0.01 }),
+            maxDistance: PD.Numeric(3, { min: 0, max: 256, step: 1 }),
+            tolerance: PD.Numeric(1.0, { min: 0.0, max: 10.0, step: 0.1 }),
+        }),
+        off: PD.Group({})
+    }, { cycle: true, description: 'Simplistic shadows' }),
     outline: PD.MappedStatic('off', {
         on: PD.Group({
             scale: PD.Numeric(1, { min: 1, max: 5, step: 1 }),
@@ -289,11 +365,12 @@ export const PostprocessingParams = {
     }, { options: [['fxaa', 'FXAA'], ['smaa', 'SMAA'], ['off', 'Off']], description: 'Smooth pixel edges' }),
     background: PD.Group(BackgroundParams, { isFlat: true }),
 };
+
 export type PostprocessingProps = PD.Values<typeof PostprocessingParams>
 
 export class PostprocessingPass {
     static isEnabled(props: PostprocessingProps) {
-        return props.occlusion.name === 'on' || props.outline.name === 'on' || props.background.variant.name !== 'off';
+        return props.occlusion.name === 'on' || props.shadow.name === 'on' || props.outline.name === 'on' || props.background.variant.name !== 'off';
     }
 
     static isOutlineEnabled(props: PostprocessingProps) {
@@ -305,6 +382,9 @@ export class PostprocessingPass {
     private readonly outlinesTarget: RenderTarget;
     private readonly outlinesRenderable: OutlinesRenderable;
 
+    private readonly shadowsTarget: RenderTarget;
+    private readonly shadowsRenderable: ShadowsRenderable;
+
     private readonly ssaoFramebuffer: Framebuffer;
     private readonly ssaoBlurFirstPassFramebuffer: Framebuffer;
     private readonly ssaoBlurSecondPassFramebuffer: Framebuffer;
@@ -350,6 +430,9 @@ export class PostprocessingPass {
         this.outlinesTarget = webgl.createRenderTarget(width, height, false);
         this.outlinesRenderable = getOutlinesRenderable(webgl, depthTextureOpaque, depthTextureTransparent);
 
+        this.shadowsTarget = webgl.createRenderTarget(width, height, false);
+        this.shadowsRenderable = getShadowsRenderable(webgl, depthTextureOpaque);
+
         this.ssaoFramebuffer = webgl.resources.framebuffer();
         this.ssaoBlurFirstPassFramebuffer = webgl.resources.framebuffer();
         this.ssaoBlurSecondPassFramebuffer = webgl.resources.framebuffer();
@@ -373,7 +456,7 @@ export class PostprocessingPass {
         this.ssaoRenderable = getSsaoRenderable(webgl, this.downsampleFactor === 1 ? depthTextureOpaque : this.downsampledDepthTarget.texture);
         this.ssaoBlurFirstPassRenderable = getSsaoBlurRenderable(webgl, this.ssaoDepthTexture, 'horizontal');
         this.ssaoBlurSecondPassRenderable = getSsaoBlurRenderable(webgl, this.ssaoDepthBlurProxyTexture, 'vertical');
-        this.renderable = getPostprocessingRenderable(webgl, colorTarget.texture, depthTextureOpaque, depthTextureTransparent, this.outlinesTarget.texture, this.ssaoDepthTexture);
+        this.renderable = getPostprocessingRenderable(webgl, colorTarget.texture, depthTextureOpaque, depthTextureTransparent, this.shadowsTarget.texture, this.outlinesTarget.texture, this.ssaoDepthTexture);
 
         this.background = new BackgroundPass(webgl, assetManager, width, height);
     }
@@ -389,12 +472,14 @@ export class PostprocessingPass {
             const sh = Math.floor(height * this.ssaoScale);
             this.target.setSize(width, height);
             this.outlinesTarget.setSize(width, height);
+            this.shadowsTarget.setSize(width, height);
             this.downsampledDepthTarget.setSize(sw, sh);
             this.ssaoDepthTexture.define(sw, sh);
             this.ssaoDepthBlurProxyTexture.define(sw, sh);
 
             ValueCell.update(this.renderable.values.uTexSize, Vec2.set(this.renderable.values.uTexSize.ref.value, width, height));
             ValueCell.update(this.outlinesRenderable.values.uTexSize, Vec2.set(this.outlinesRenderable.values.uTexSize.ref.value, width, height));
+            ValueCell.update(this.shadowsRenderable.values.uTexSize, Vec2.set(this.shadowsRenderable.values.uTexSize.ref.value, width, height));
             ValueCell.update(this.downsampleDepthRenderable.values.uTexSize, Vec2.set(this.downsampleDepthRenderable.values.uTexSize.ref.value, sw, sh));
             ValueCell.update(this.ssaoRenderable.values.uTexSize, Vec2.set(this.ssaoRenderable.values.uTexSize.ref.value, sw, sh));
             ValueCell.update(this.ssaoBlurFirstPassRenderable.values.uTexSize, Vec2.set(this.ssaoBlurFirstPassRenderable.values.uTexSize.ref.value, sw, sh));
@@ -404,25 +489,28 @@ export class PostprocessingPass {
         }
     }
 
-    private updateState(camera: ICamera, transparentBackground: boolean, backgroundColor: Color, props: PostprocessingProps) {
+    private updateState(camera: ICamera, transparentBackground: boolean, backgroundColor: Color, props: PostprocessingProps, light: Light) {
+        let needsUpdateShadows = false;
         let needsUpdateMain = false;
         let needsUpdateSsao = false;
         let needsUpdateSsaoBlur = false;
 
         const orthographic = camera.state.mode === 'orthographic' ? 1 : 0;
         const outlinesEnabled = props.outline.name === 'on';
+        const shadowsEnabled = props.shadow.name === 'on';
         const occlusionEnabled = props.occlusion.name === 'on';
 
         const invProjection = Mat4.identity();
         Mat4.invert(invProjection, camera.projection);
 
+        const [w, h] = this.renderable.values.uTexSize.ref.value;
+        const v = camera.viewport;
+
         if (props.occlusion.name === 'on') {
             ValueCell.update(this.ssaoRenderable.values.uProjection, camera.projection);
             ValueCell.update(this.ssaoRenderable.values.uInvProjection, invProjection);
 
-            const [w, h] = this.renderable.values.uTexSize.ref.value;
             const b = this.ssaoRenderable.values.uBounds;
-            const v = camera.viewport;
             const s = this.ssaoScale;
             Vec4.set(b.ref.value,
                 Math.floor(v.x * s) / (w * s),
@@ -494,6 +582,38 @@ export class PostprocessingPass {
             }
         }
 
+        if (props.shadow.name === 'on') {
+            ValueCell.update(this.shadowsRenderable.values.uProjection, camera.projection);
+            ValueCell.update(this.shadowsRenderable.values.uInvProjection, invProjection);
+
+            Vec4.set(this.shadowsRenderable.values.uBounds.ref.value,
+                v.x / w,
+                v.y / h,
+                (v.x + v.width) / w,
+                (v.y + v.height) / h
+            );
+            ValueCell.update(this.shadowsRenderable.values.uBounds, this.shadowsRenderable.values.uBounds.ref.value);
+
+            ValueCell.updateIfChanged(this.shadowsRenderable.values.uNear, camera.near);
+            ValueCell.updateIfChanged(this.shadowsRenderable.values.uFar, camera.far);
+            ValueCell.updateIfChanged(this.shadowsRenderable.values.dOrthographic, orthographic);
+
+            ValueCell.updateIfChanged(this.shadowsRenderable.values.uMaxDistance, props.shadow.params.maxDistance);
+            ValueCell.updateIfChanged(this.shadowsRenderable.values.uTolerance, props.shadow.params.tolerance);
+            ValueCell.updateIfChanged(this.shadowsRenderable.values.uBias, props.shadow.params.bias);
+            if (this.shadowsRenderable.values.dSteps.ref.value !== props.shadow.params.steps) {
+                ValueCell.update(this.shadowsRenderable.values.dSteps, props.shadow.params.steps);
+                needsUpdateShadows = true;
+            }
+
+            ValueCell.update(this.shadowsRenderable.values.uLightDirection, light.direction);
+            ValueCell.update(this.shadowsRenderable.values.uLightColor, light.color);
+            if (this.shadowsRenderable.values.dLightCount.ref.value !== light.count) {
+                ValueCell.update(this.shadowsRenderable.values.dLightCount, light.count);
+                needsUpdateShadows = true;
+            }
+        }
+
         if (props.outline.name === 'on') {
             let { threshold } = props.outline.params;
             // orthographic needs lower threshold
@@ -522,11 +642,18 @@ export class PostprocessingPass {
         ValueCell.updateIfChanged(this.renderable.values.uTransparentBackground, transparentBackground);
         if (this.renderable.values.dOrthographic.ref.value !== orthographic) { needsUpdateMain = true; }
         ValueCell.updateIfChanged(this.renderable.values.dOrthographic, orthographic);
+
         if (this.renderable.values.dOutlineEnable.ref.value !== outlinesEnabled) { needsUpdateMain = true; }
         ValueCell.updateIfChanged(this.renderable.values.dOutlineEnable, outlinesEnabled);
+        if (this.renderable.values.dShadowEnable.ref.value !== shadowsEnabled) { needsUpdateMain = true; }
+        ValueCell.updateIfChanged(this.renderable.values.dShadowEnable, shadowsEnabled);
         if (this.renderable.values.dOcclusionEnable.ref.value !== occlusionEnabled) { needsUpdateMain = true; }
         ValueCell.updateIfChanged(this.renderable.values.dOcclusionEnable, occlusionEnabled);
 
+        if (needsUpdateShadows) {
+            this.shadowsRenderable.update();
+        }
+
         if (needsUpdateSsao) {
             this.ssaoRenderable.update();
         }
@@ -564,15 +691,20 @@ export class PostprocessingPass {
         this.transparentBackground = value;
     }
 
-    render(camera: ICamera, toDrawingBuffer: boolean, transparentBackground: boolean, backgroundColor: Color, props: PostprocessingProps) {
+    render(camera: ICamera, toDrawingBuffer: boolean, transparentBackground: boolean, backgroundColor: Color, props: PostprocessingProps, light: Light) {
         if (isTimingMode) this.webgl.timer.mark('PostprocessingPass.render');
-        this.updateState(camera, transparentBackground, backgroundColor, props);
+        this.updateState(camera, transparentBackground, backgroundColor, props, light);
 
         if (props.outline.name === 'on') {
             this.outlinesTarget.bind();
             this.outlinesRenderable.render();
         }
 
+        if (props.shadow.name === 'on') {
+            this.shadowsTarget.bind();
+            this.shadowsRenderable.render();
+        }
+
         // don't render occlusion if offset is given,
         // which will reuse the existing occlusion
         if (props.occlusion.name === 'on' && this.occlusionOffset[0] === 0 && this.occlusionOffset[1] === 0) {

+ 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>

+ 9 - 5
src/mol-gl/renderer.ts

@@ -54,6 +54,7 @@ export const enum MarkingType {
 interface Renderer {
     readonly stats: RendererStats
     readonly props: Readonly<RendererProps>
+    readonly light: Readonly<Light>
 
     clear: (toBackgroundColor: boolean, ignoreTransparentBackground?: boolean) => void
     clearDepth: (packed?: boolean) => void
@@ -103,13 +104,13 @@ export const RendererParams = {
     xrayEdgeFalloff: PD.Numeric(1, { min: 0.0, max: 3.0, step: 0.1 }),
 
     light: PD.ObjectList({
-        inclination: PD.Numeric(180, { min: 0, max: 180, step: 1 }),
-        azimuth: PD.Numeric(0, { min: 0, max: 360, step: 1 }),
+        inclination: PD.Numeric(150, { min: 0, max: 180, step: 1 }),
+        azimuth: PD.Numeric(320, { min: 0, max: 360, step: 1 }),
         color: PD.Color(Color.fromNormalizedRgb(1.0, 1.0, 1.0)),
         intensity: PD.Numeric(0.6, { min: 0.0, max: 1.0, step: 0.01 }),
     }, o => Color.toHexString(o.color), { defaultValue: [{
-        inclination: 180,
-        azimuth: 0,
+        inclination: 150,
+        azimuth: 320,
         color: Color.fromNormalizedRgb(1.0, 1.0, 1.0),
         intensity: 0.6
     }] }),
@@ -118,7 +119,7 @@ export const RendererParams = {
 };
 export type RendererProps = PD.Values<typeof RendererParams>
 
-type Light = {
+export type Light = {
     count: number
     direction: number[]
     color: number[]
@@ -827,6 +828,9 @@ namespace Renderer {
                     instancedDrawCount: stats.instancedDrawCount,
                 };
             },
+            get light(): Light {
+                return light;
+            },
             dispose: () => {
                 // TODO
             }

+ 1 - 1
src/mol-gl/shader/chunks/apply-light-color.glsl.ts

@@ -57,7 +57,7 @@ export const apply_light_color = `
     RE_IndirectSpecular_Physical(radiance, iblIrradiance, clearcoatRadiance, geometry, physicalMaterial, reflectedLight);
 
     vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular;
-    outgoingLight = clamp(outgoingLight, 0.0, 1.0); // prevents black artifacts on specular highlight with transparent background
+    outgoingLight = clamp(outgoingLight, 0.01, 0.99); // prevents black artifacts on specular highlight with transparent background
 
     gl_FragColor = vec4(outgoingLight, color.a);
 #endif

+ 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
 `;

+ 15 - 1
src/mol-gl/shader/postprocessing.frag.ts

@@ -14,6 +14,7 @@ uniform sampler2D tSsaoDepth;
 uniform sampler2D tColor;
 uniform sampler2D tDepthOpaque;
 uniform sampler2D tDepthTransparent;
+uniform sampler2D tShadows;
 uniform sampler2D tOutlines;
 uniform vec2 uTexSize;
 
@@ -120,7 +121,20 @@ void main(void) {
         }
     #endif
 
-    // outline needs to be handled after occlusion to keep them clean
+    #ifdef dShadowEnable
+        if (!isBackground(opaqueDepth)) {
+            viewDist = abs(getViewZ(opaqueDepth));
+            fogFactor = smoothstep(uFogNear, uFogFar, viewDist);
+            vec4 shadow = texture2D(tShadows, coords);
+            if (!uTransparentBackground) {
+                color.rgb = mix(mix(vec3(0), uFogColor, fogFactor), color.rgb, shadow.a);
+            } else {
+                color.rgb = mix(vec3(0) * (1.0 - fogFactor), color.rgb, shadow.a);
+            }
+        }
+    #endif
+
+    // outline needs to be handled after occlusion and shadow to keep them clean
     #ifdef dOutlineEnable
         float closestTexel;
         float outline = getOutline(coords, opaqueDepth, closestTexel);

+ 131 - 0
src/mol-gl/shader/shadows.frag.ts

@@ -0,0 +1,131 @@
+/**
+ * Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Ludovic Autin <ludovic.autin@gmail.com>
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+export const shadows_frag = `
+precision highp float;
+precision highp int;
+precision highp sampler2D;
+
+#include common
+
+uniform sampler2D tDepth;
+uniform vec2 uTexSize;
+uniform vec4 uBounds;
+
+uniform float uNear;
+uniform float uFar;
+
+#if dLightCount != 0
+    uniform vec3 uLightDirection[dLightCount];
+    uniform vec3 uLightColor[dLightCount];
+#endif
+
+uniform mat4 uProjection;
+uniform mat4 uInvProjection;
+
+uniform float uMaxDistance;
+uniform float uTolerance;
+uniform float uBias;
+
+bool isBackground(const in float depth) {
+    return depth == 1.0;
+}
+
+bool outsideBounds(const in vec2 p) {
+    return p.x < uBounds.x || p.y < uBounds.y || p.x > uBounds.z || p.y > uBounds.w;
+}
+
+float getViewZ(const in float depth) {
+    #if dOrthographic == 1
+        return orthographicDepthToViewZ(depth, uNear, uFar);
+    #else
+        return perspectiveDepthToViewZ(depth, uNear, uFar);
+    #endif
+}
+
+float getDepth(const in vec2 coords) {
+    #ifdef depthTextureSupport
+        return texture2D(tDepth, coords).r;
+    #else
+        return unpackRGBAToDepth(texture2D(tDepth, coords));
+    #endif
+}
+
+float screenFade(const in vec2 coords) {
+    vec2 c = (coords - uBounds.xy) / (uBounds.zw - uBounds.xy);
+    vec2 fade = max(12.0 * abs(c - 0.5) - 5.0, vec2(0.0));
+    return saturate(1.0 - dot(fade, fade));
+}
+
+// based on https://panoskarabelas.com/posts/screen_space_shadows/
+float screenSpaceShadow(const in vec3 position, const in vec3 lightDirection, const in float stepLength) {
+    // Ray position and direction (in view-space)
+    vec3 rayPos = position;
+    vec3 rayDir = -lightDirection;
+
+    // Compute ray step
+    vec3 rayStep = rayDir * stepLength;
+
+    // Ray march towards the light
+    float occlusion = 0.0;
+    vec4 rayCoords = vec4(0.0);
+    for (int i = 0; i < dSteps; ++i) {
+        // Step the ray
+        rayPos += rayStep;
+
+        rayCoords = uProjection * vec4(rayPos, 1.0);
+        rayCoords.xyz = (rayCoords.xyz / rayCoords.w) * 0.5 + 0.5;
+
+        if (outsideBounds(rayCoords.xy))
+            return 1.0;
+
+        // Compute the difference between the ray's and the camera's depth
+        float depth = getDepth(rayCoords.xy);
+        float viewZ = getViewZ(depth);
+        float zDelta = rayPos.z - viewZ;
+
+        if (zDelta < uTolerance) {
+            occlusion = 1.0;
+
+            // Fade out as we approach the edges of the screen
+            occlusion *= screenFade(rayCoords.xy);
+
+            break;
+        }
+    }
+
+    return 1.0 - (uBias * occlusion);
+}
+
+void main(void) {
+    vec2 invTexSize = 1.0 / uTexSize;
+    vec2 selfCoords = gl_FragCoord.xy * invTexSize;
+
+    float selfDepth = getDepth(selfCoords);
+
+    if (isBackground(selfDepth)) {
+        gl_FragColor = vec4(0.0);
+        return;
+    }
+
+    vec3 selfViewPos = screenSpaceToViewSpace(vec3(selfCoords, selfDepth), uInvProjection);
+    float stepLength = uMaxDistance / float(dSteps);
+
+    float o = 1.0;
+    #if dLightCount != 0
+        float sh[dLightCount];
+        #pragma unroll_loop_start
+        for (int i = 0; i < dLightCount; ++i) {
+            sh[i] = screenSpaceShadow(selfViewPos, uLightDirection[i], stepLength);
+            o = min(o, sh[i]);
+        }
+        #pragma unroll_loop_end
+    #endif
+
+    gl_FragColor = vec4(o);
+}
+`;

+ 122 - 0
src/mol-gl/webgl/compat.ts

@@ -19,6 +19,9 @@ export function isWebGL2(gl: any): gl is WebGL2RenderingContext {
     return typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext;
 }
 
+/**
+ * See https://registry.khronos.org/webgl/extensions/ANGLE_instanced_arrays/
+ */
 export interface COMPAT_instanced_arrays {
     drawArraysInstanced(mode: number, first: number, count: number, primcount: number): void;
     drawElementsInstanced(mode: number, count: number, type: number, offset: number, primcount: number): void;
@@ -46,6 +49,9 @@ export function getInstancedArrays(gl: GLRenderingContext): COMPAT_instanced_arr
     }
 }
 
+/**
+ * See https://registry.khronos.org/webgl/extensions/OES_standard_derivatives/
+ */
 export interface COMPAT_standard_derivatives {
     readonly FRAGMENT_SHADER_DERIVATIVE_HINT: number;
 }
@@ -60,6 +66,9 @@ export function getStandardDerivatives(gl: GLRenderingContext): COMPAT_standard_
     }
 }
 
+/**
+ * See https://registry.khronos.org/webgl/extensions/OES_element_index_uint/
+ */
 export interface COMPAT_element_index_uint {
 }
 
@@ -67,6 +76,9 @@ export function getElementIndexUint(gl: GLRenderingContext): COMPAT_element_inde
     return isWebGL2(gl) ? {} : gl.getExtension('OES_element_index_uint');
 }
 
+/**
+ * See https://registry.khronos.org/webgl/extensions/OES_vertex_array_object/
+ */
 export interface COMPAT_vertex_array_object {
     readonly VERTEX_ARRAY_BINDING: number;
     bindVertexArray(arrayObject: WebGLVertexArrayObject | null): void;
@@ -132,6 +144,9 @@ export function getTextureHalfFloatLinear(gl: GLRenderingContext): COMPAT_textur
     return gl.getExtension('OES_texture_half_float_linear');
 }
 
+/**
+ * See https://registry.khronos.org/webgl/extensions/EXT_blend_minmax/
+ */
 export interface COMPAT_blend_minmax {
     readonly MIN: number
     readonly MAX: number
@@ -147,6 +162,9 @@ export function getBlendMinMax(gl: GLRenderingContext): COMPAT_blend_minmax | nu
     }
 }
 
+/**
+ * See https://registry.khronos.org/webgl/extensions/EXT_frag_depth/
+ */
 export interface COMPAT_frag_depth {
 }
 
@@ -196,6 +214,9 @@ export function getColorBufferHalfFloat(gl: GLRenderingContext): COMPAT_color_bu
     }
 }
 
+/**
+ * See https://registry.khronos.org/webgl/extensions/WEBGL_draw_buffers/
+ */
 export interface COMPAT_draw_buffers {
     drawBuffers(buffers: number[]): void;
     readonly COLOR_ATTACHMENT0: number;
@@ -268,6 +289,73 @@ export function getDrawBuffers(gl: GLRenderingContext): COMPAT_draw_buffers | nu
     }
 }
 
+/**
+ * See https://registry.khronos.org/webgl/extensions/OES_draw_buffers_indexed/
+ */
+export interface COMPAT_draw_buffers_indexed {
+    /**
+     * Enables blending for an individual draw buffer.
+     *
+     * @param target must be BLEND.
+     * @param index is an integer i specifying the draw buffer associated with the symbolic constant DRAW_BUFFERi.
+     */
+    enablei: (target: number, index: number) => void;
+    /**
+     * Disables  blending for an individual draw buffer.
+     *
+     * @param target must be BLEND.
+     * @param index is an integer i specifying the draw buffer associated with the symbolic constant DRAW_BUFFERi.
+     */
+    disablei: (buf: number, mode: number) => void;
+    /**
+     * The buf argument is an integer i that indicates that the blend equations should be modified for DRAW_BUFFERi.
+     *
+     * mode accepts the same tokens as mode in blendEquation.
+     */
+    blendEquationi: (target: number, index: number) => void;
+    /**
+     * The buf argument is an integer i that indicates that the blend equations should be modified for DRAW_BUFFERi.
+     *
+     * modeRGB and modeAlpha accept the same tokens as modeRGB and modeAlpha in blendEquationSeparate.
+     */
+    blendEquationSeparatei: (buf: number, modeRGB: number, modeAlpha: number) => void;
+    /**
+     * The buf argument is an integer i that indicates that the blend functions should be modified for DRAW_BUFFERi.
+     *
+     * src and dst accept the same tokens as src and dst in blendFunc.
+     */
+    blendFunci: (buf: number, src: number, dst: number) => void;
+    /**
+     * The buf argument is an integer i that indicates that the blend functions should be modified for DRAW_BUFFERi.
+     *
+     * srcRGB, dstRGB, srcAlpha, and dstAlpha accept the same tokens as srcRGB, dstRGB, srcAlpha, and dstAlpha parameters in blendEquationSeparate.
+     */
+    blendFuncSeparatei: (buf: number, srcRGB: number, dstRGB: number, srcAlpha: number, dstAlpha: number) => void;
+    /**
+     * The buf argument is an integer i that indicates that the write mask should be modified for DRAW_BUFFERi.
+     *
+     * r, g, b, and a indicate whether R, G, B, or A values, respectively, are written or not (a value of TRUE means that the corresponding value is written).
+     */
+    colorMaski: (buf: number, r: boolean, g: boolean, b: boolean, a: boolean) => void;
+}
+
+export function getDrawBuffersIndexed(gl: GLRenderingContext): COMPAT_draw_buffers_indexed | null {
+    const ext = gl.getExtension('OES_draw_buffers_indexed');
+    if (ext === null) return null;
+    return {
+        enablei: ext.enableiOES.bind(ext),
+        disablei: ext.disableiOES.bind(ext),
+        blendEquationi: ext.blendEquationiOES.bind(ext),
+        blendEquationSeparatei: ext.blendEquationSeparateiOES.bind(ext),
+        blendFunci: ext.blendFunciOES.bind(ext),
+        blendFuncSeparatei: ext.blendFuncSeparateiOES.bind(ext),
+        colorMaski: ext.colorMaskiOES.bind(ext),
+    };
+}
+
+/**
+ * See https://registry.khronos.org/webgl/extensions/EXT_shader_texture_lod/
+ */
 export interface COMPAT_shader_texture_lod {
 }
 
@@ -275,6 +363,9 @@ export function getShaderTextureLod(gl: GLRenderingContext): COMPAT_shader_textu
     return isWebGL2(gl) ? {} : gl.getExtension('EXT_shader_texture_lod');
 }
 
+/**
+ * See https://registry.khronos.org/webgl/extensions/WEBGL_depth_texture/
+ */
 export interface COMPAT_depth_texture {
     readonly UNSIGNED_INT_24_8: number;
 }
@@ -293,6 +384,9 @@ export function getDepthTexture(gl: GLRenderingContext): COMPAT_depth_texture |
     }
 }
 
+/**
+ * See https://registry.khronos.org/webgl/extensions/EXT_sRGB/
+ */
 export interface COMPAT_sRGB {
     readonly FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: number;
     readonly SRGB8_ALPHA8: number;
@@ -320,6 +414,9 @@ export function getSRGB(gl: GLRenderingContext): COMPAT_sRGB | null {
     }
 }
 
+/**
+ * See https://registry.khronos.org/webgl/extensions/EXT_disjoint_timer_query/ and https://registry.khronos.org/webgl/extensions/EXT_disjoint_timer_query_webgl2/
+ */
 export interface COMPAT_disjoint_timer_query {
     /** A GLint indicating the number of bits used to hold the query result for the given target. */
     QUERY_COUNTER_BITS: number
@@ -401,6 +498,31 @@ export function getDisjointTimerQuery(gl: GLRenderingContext): COMPAT_disjoint_t
     }
 }
 
+/**
+ * See https://registry.khronos.org/webgl/extensions/KHR_parallel_shader_compile/
+ */
+export interface COMPAT_parallel_shader_compile {
+    readonly COMPLETION_STATUS: number;
+}
+
+export function getParallelShaderCompile(gl: GLRenderingContext): COMPAT_parallel_shader_compile | null {
+    const ext = gl.getExtension('KHR_parallel_shader_compile');
+    if (ext === null) return null;
+    return {
+        COMPLETION_STATUS: ext.COMPLETION_STATUS_KHR,
+    };
+}
+
+/**
+ * See https://registry.khronos.org/webgl/extensions/OES_fbo_render_mipmap/
+ */
+export interface COMPAT_fboRenderMipmap {
+}
+
+export function getFboRenderMipmap(gl: GLRenderingContext): COMPAT_fboRenderMipmap | null {
+    return isWebGL2(gl) ? {} : gl.getExtension('OES_fbo_render_mipmap');
+}
+
 export function getNoNonInstancedActiveAttribs(gl: GLRenderingContext): boolean {
     if (!isWebGL2(gl)) return false;
 

+ 22 - 4
src/mol-gl/webgl/extensions.ts

@@ -4,14 +4,14 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { GLRenderingContext, COMPAT_instanced_arrays, COMPAT_standard_derivatives, COMPAT_vertex_array_object, getInstancedArrays, getStandardDerivatives, COMPAT_element_index_uint, getElementIndexUint, COMPAT_texture_float, getTextureFloat, COMPAT_texture_float_linear, getTextureFloatLinear, COMPAT_blend_minmax, getBlendMinMax, getFragDepth, COMPAT_frag_depth, COMPAT_color_buffer_float, getColorBufferFloat, COMPAT_draw_buffers, getDrawBuffers, getShaderTextureLod, COMPAT_shader_texture_lod, getDepthTexture, COMPAT_depth_texture, COMPAT_sRGB, getSRGB, getTextureHalfFloat, getTextureHalfFloatLinear, COMPAT_texture_half_float, COMPAT_texture_half_float_linear, COMPAT_color_buffer_half_float, getColorBufferHalfFloat, getVertexArrayObject, getDisjointTimerQuery, COMPAT_disjoint_timer_query, getNoNonInstancedActiveAttribs } from './compat';
+import { GLRenderingContext, COMPAT_instanced_arrays, COMPAT_standard_derivatives, COMPAT_vertex_array_object, getInstancedArrays, getStandardDerivatives, COMPAT_element_index_uint, getElementIndexUint, COMPAT_texture_float, getTextureFloat, COMPAT_texture_float_linear, getTextureFloatLinear, COMPAT_blend_minmax, getBlendMinMax, getFragDepth, COMPAT_frag_depth, COMPAT_color_buffer_float, getColorBufferFloat, COMPAT_draw_buffers, getDrawBuffers, getShaderTextureLod, COMPAT_shader_texture_lod, getDepthTexture, COMPAT_depth_texture, COMPAT_sRGB, getSRGB, getTextureHalfFloat, getTextureHalfFloatLinear, COMPAT_texture_half_float, COMPAT_texture_half_float_linear, COMPAT_color_buffer_half_float, getColorBufferHalfFloat, getVertexArrayObject, getDisjointTimerQuery, COMPAT_disjoint_timer_query, getNoNonInstancedActiveAttribs, getDrawBuffersIndexed, COMPAT_draw_buffers_indexed, getParallelShaderCompile, COMPAT_parallel_shader_compile, getFboRenderMipmap, COMPAT_fboRenderMipmap } from './compat';
 import { isDebugMode } from '../../mol-util/debug';
 
 export type WebGLExtensions = {
     instancedArrays: COMPAT_instanced_arrays
     elementIndexUint: COMPAT_element_index_uint
+    standardDerivatives: COMPAT_standard_derivatives
 
-    standardDerivatives: COMPAT_standard_derivatives | null
     textureFloat: COMPAT_texture_float | null
     textureFloatLinear: COMPAT_texture_float_linear | null
     textureHalfFloat: COMPAT_texture_half_float | null
@@ -23,9 +23,12 @@ export type WebGLExtensions = {
     colorBufferFloat: COMPAT_color_buffer_float | null
     colorBufferHalfFloat: COMPAT_color_buffer_half_float | null
     drawBuffers: COMPAT_draw_buffers | null
+    drawBuffersIndexed: COMPAT_draw_buffers_indexed | null
     shaderTextureLod: COMPAT_shader_texture_lod | null
     sRGB: COMPAT_sRGB | null
     disjointTimerQuery: COMPAT_disjoint_timer_query | null
+    parallelShaderCompile: COMPAT_parallel_shader_compile | null
+    fboRenderMipmap: COMPAT_fboRenderMipmap | null
 
     noNonInstancedActiveAttribs: boolean
 }
@@ -94,6 +97,10 @@ export function createExtensions(gl: GLRenderingContext): WebGLExtensions {
     if (isDebugMode && drawBuffers === null) {
         console.log('Could not find support for "draw_buffers"');
     }
+    const drawBuffersIndexed = getDrawBuffersIndexed(gl);
+    if (isDebugMode && drawBuffersIndexed === null) {
+        console.log('Could not find support for "draw_buffers_indexed"');
+    }
     const shaderTextureLod = getShaderTextureLod(gl);
     if (isDebugMode && shaderTextureLod === null) {
         console.log('Could not find support for "shader_texture_lod"');
@@ -106,28 +113,39 @@ export function createExtensions(gl: GLRenderingContext): WebGLExtensions {
     if (isDebugMode && disjointTimerQuery === null) {
         console.log('Could not find support for "disjoint_timer_query"');
     }
+    const parallelShaderCompile = getParallelShaderCompile(gl);
+    if (isDebugMode && parallelShaderCompile === null) {
+        console.log('Could not find support for "parallel_shader_compile"');
+    }
+    const fboRenderMipmap = getFboRenderMipmap(gl);
+    if (isDebugMode && fboRenderMipmap === null) {
+        console.log('Could not find support for "fbo_render_mipmap"');
+    }
 
     const noNonInstancedActiveAttribs = getNoNonInstancedActiveAttribs(gl);
 
     return {
         instancedArrays,
         standardDerivatives,
+        elementIndexUint,
+
         textureFloat,
         textureFloatLinear,
         textureHalfFloat,
         textureHalfFloatLinear,
-        elementIndexUint,
         depthTexture,
-
         blendMinMax,
         vertexArrayObject,
         fragDepth,
         colorBufferFloat,
         colorBufferHalfFloat,
         drawBuffers,
+        drawBuffersIndexed,
         shaderTextureLod,
         sRGB,
         disjointTimerQuery,
+        parallelShaderCompile,
+        fboRenderMipmap,
 
         noNonInstancedActiveAttribs,
     };

+ 1 - 1
src/mol-io/reader/cif/schema/bird.ts

@@ -1,7 +1,7 @@
 /**
  * Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
- * Code-generated 'BIRD' schema file. Dictionary versions: mmCIF 5.362, IHM 1.17, MA 1.4.3.
+ * Code-generated 'BIRD' schema file. Dictionary versions: mmCIF 5.363, IHM 1.17, MA 1.4.3.
  *
  * @author molstar/ciftools package
  */

+ 1 - 1
src/mol-io/reader/cif/schema/ccd.ts

@@ -1,7 +1,7 @@
 /**
  * Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
- * Code-generated 'CCD' schema file. Dictionary versions: mmCIF 5.362, IHM 1.17, MA 1.4.3.
+ * Code-generated 'CCD' schema file. Dictionary versions: mmCIF 5.363, IHM 1.17, MA 1.4.3.
  *
  * @author molstar/ciftools package
  */

+ 1 - 1
src/mol-io/reader/cif/schema/mmcif.ts

@@ -1,7 +1,7 @@
 /**
  * Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
- * Code-generated 'mmCIF' schema file. Dictionary versions: mmCIF 5.362, IHM 1.17, MA 1.4.3.
+ * Code-generated 'mmCIF' schema file. Dictionary versions: mmCIF 5.363, IHM 1.17, MA 1.4.3.
  *
  * @author molstar/ciftools package
  */

+ 12 - 9
src/mol-math/geometry/symmetry-operator.ts

@@ -1,11 +1,11 @@
 /**
- * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2017-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>
  */
 
 import { lerp as scalar_lerp } from '../../mol-math/interpolate';
-import { defaults } from '../../mol-util';
 import { Mat3 } from '../linear-algebra/3d/mat3';
 import { Mat4 } from '../linear-algebra/3d/mat4';
 import { Quat } from '../linear-algebra/3d/quat';
@@ -29,11 +29,13 @@ interface SymmetryOperator {
     readonly hkl: Vec3,
     /** spacegroup symmetry operator index, -1 if not applicable */
     readonly spgrOp: number,
+    /** unique (external) key, -1 if not available */
+    readonly key: number,
 
     readonly matrix: Mat4,
-    // cache the inverse of the transform
+    /** cache the inverse of the transform */
     readonly inverse: Mat4,
-    // optimize the identity case
+    /** optimize the identity case */
     readonly isIdentity: boolean,
 
     /**
@@ -51,19 +53,20 @@ namespace SymmetryOperator {
 
     export const RotationTranslationEpsilon = 0.005;
 
-    export type CreateInfo = { assembly?: SymmetryOperator['assembly'], ncsId?: number, hkl?: Vec3, spgrOp?: number }
+    export type CreateInfo = { assembly?: SymmetryOperator['assembly'], ncsId?: number, hkl?: Vec3, spgrOp?: number, key?: number }
     export function create(name: string, matrix: Mat4, info?: CreateInfo | SymmetryOperator): SymmetryOperator {
-        let { assembly, ncsId, hkl, spgrOp } = info || { };
+        let { assembly, ncsId, hkl, spgrOp, key } = info || { };
         const _hkl = hkl ? Vec3.clone(hkl) : Vec3();
-        spgrOp = defaults(spgrOp, -1);
+        spgrOp = spgrOp ?? -1;
+        key = key ?? -1;
         ncsId = ncsId || -1;
         const isIdentity = Mat4.isIdentity(matrix);
         const suffix = getSuffix(info, isIdentity);
-        if (isIdentity) return { name, assembly, matrix, inverse: Mat4.identity(), isIdentity: true, hkl: _hkl, spgrOp, ncsId, suffix };
+        if (isIdentity) return { name, assembly, matrix, inverse: Mat4.identity(), isIdentity: true, hkl: _hkl, spgrOp, ncsId, suffix, key };
         if (!Mat4.isRotationAndTranslation(matrix, RotationTranslationEpsilon)) {
             console.warn(`Symmetry operator (${name}) should be a composition of rotation and translation.`);
         }
-        return { name, assembly, matrix, inverse: Mat4.invert(Mat4(), matrix), isIdentity: false, hkl: _hkl, spgrOp, ncsId, suffix };
+        return { name, assembly, matrix, inverse: Mat4.invert(Mat4(), matrix), isIdentity: false, hkl: _hkl, spgrOp, key, ncsId, suffix };
     }
 
     function isSymmetryOperator(x: any): x is SymmetryOperator {

+ 30 - 2
src/mol-model-formats/structure/property/bonds/index-pair.ts

@@ -14,6 +14,8 @@ import { ElementIndex } from '../../../../mol-model/structure';
 
 export type IndexPairsProps = {
     readonly key: ArrayLike<number>
+    readonly operatorA: ArrayLike<number>
+    readonly operatorB: ArrayLike<number>
     readonly order: ArrayLike<number>
     readonly distance: ArrayLike<number>
     readonly flag: ArrayLike<BondType.Flag>
@@ -24,18 +26,22 @@ export type IndexPairBonds = { bonds: IndexPairs, maxDistance: number }
 function getGraph(indexA: ArrayLike<ElementIndex>, indexB: ArrayLike<ElementIndex>, props: Partial<IndexPairsProps>, count: number): IndexPairs {
     const builder = new IntAdjacencyGraph.EdgeBuilder(count, indexA, indexB);
     const key = new Int32Array(builder.slotCount);
+    const operatorA = new Array(builder.slotCount);
+    const operatorB = new Array(builder.slotCount);
     const order = new Int8Array(builder.slotCount);
     const distance = new Array(builder.slotCount);
     const flag = new Array(builder.slotCount);
     for (let i = 0, _i = builder.edgeCount; i < _i; i++) {
         builder.addNextEdge();
         builder.assignProperty(key, props.key ? props.key[i] : -1);
+        builder.assignProperty(operatorA, props.operatorA ? props.operatorA[i] : -1);
+        builder.assignProperty(operatorB, props.operatorB ? props.operatorB[i] : -1);
         builder.assignProperty(order, props.order ? props.order[i] : 1);
         builder.assignProperty(distance, props.distance ? props.distance[i] : -1);
         builder.assignProperty(flag, props.flag ? props.flag[i] : BondType.Flag.Covalent);
     }
 
-    return builder.createGraph({ key, order, distance, flag });
+    return builder.createGraph({ key, operatorA, operatorB, order, distance, flag });
 }
 
 export namespace IndexPairBonds {
@@ -50,6 +56,10 @@ export namespace IndexPairBonds {
             indexA: Column<number>,
             indexB: Column<number>,
             key?: Column<number>,
+            /** Operator key for indexA. Used in bond computation. */
+            operatorA?: Column<number>,
+            /** Operator key for indexB. Used in bond computation. */
+            operatorB?: Column<number>,
             order?: Column<number>,
             /**
              * Useful for bonds in periodic cells. That is, only bonds within the given
@@ -83,12 +93,30 @@ export namespace IndexPairBonds {
         const indexA = pairs.indexA.toArray() as ArrayLike<ElementIndex>;
         const indexB = pairs.indexB.toArray() as ArrayLike<ElementIndex>;
         const key = pairs.key && pairs.key.toArray();
+        const operatorA = pairs.operatorA && pairs.operatorA.toArray();
+        const operatorB = pairs.operatorB && pairs.operatorB.toArray();
         const order = pairs.order && pairs.order.toArray();
         const distance = pairs.distance && pairs.distance.toArray();
         const flag = pairs.flag && pairs.flag.toArray();
         return {
-            bonds: getGraph(indexA, indexB, { key, order, distance, flag }, count),
+            bonds: getGraph(indexA, indexB, { key, operatorA, operatorB, order, distance, flag }, count),
             maxDistance: p.maxDistance
         };
     }
+
+    /** Like `getEdgeIndex` but taking `edgeProps.operatorA` and `edgeProps.operatorB` into account */
+    export function getEdgeIndexForOperators(bonds: IndexPairs, i: ElementIndex, j: ElementIndex, opI: number, opJ: number): number {
+        let a, b, opA, opB;
+        if (i < j) {
+            a = i; b = j;
+            opA = opI; opB = opJ;
+        } else {
+            a = j; b = i;
+            opA = opJ; opB = opI;
+        }
+        for (let t = bonds.offset[a], _t = bonds.offset[a + 1]; t < _t; t++) {
+            if (bonds.b[t] === b && bonds.edgeProps.operatorA[t] === opA && bonds.edgeProps.operatorB[t] === opB) return t;
+        }
+        return -1;
+    }
 }

+ 4 - 0
src/mol-model/structure/model/types.ts

@@ -285,6 +285,10 @@ export const AminoAcidNamesD = new Set([
 ]);
 export const AminoAcidNames = SetUtils.unionMany(AminoAcidNamesL, AminoAcidNamesD);
 
+export const CommonProteinCaps = new Set([
+    'NME', 'ACE'
+]);
+
 export const RnaBaseNames = new Set([
     'A', 'C', 'T', 'G', 'I', 'U',
     'N' // unknown RNA base from CCD

File diff suppressed because it is too large
+ 0 - 0
src/mol-model/structure/model/types/saccharides.ts


+ 1 - 1
src/mol-model/structure/query/context.ts

@@ -155,6 +155,6 @@ class QueryContextBondInfo<U extends Unit = Unit> {
     }
 
     get length() {
-        return StructureElement.Location.distance(this.a, this. b);
+        return StructureElement.Location.distance(this.a, this.b);
     }
 }

+ 2 - 1
src/mol-model/structure/structure/properties.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2017-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2017-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>
@@ -173,6 +173,7 @@ const unit = {
     multiChain: p(l => Unit.Traits.is(l.unit.traits, Unit.Trait.MultiChain)),
     object_primitive: p(l => l.unit.objectPrimitive),
     operator_name: p(l => l.unit.conformation.operator.name),
+    operator_key: p(l => l.unit.conformation.operator.key),
     model_index: p(l => l.unit.model.modelNum),
     model_label: p(l => l.unit.model.label),
     model_entry_id: p(l => l.unit.model.entryId),

+ 1 - 1
src/mol-model/structure/structure/structure.ts

@@ -1224,7 +1224,7 @@ namespace Structure {
             const closeUnits = lookup.findUnitIndices(imageCenter[0], imageCenter[1], imageCenter[2], bs.radius + maxRadius);
             for (let i = 0; i < closeUnits.count; i++) {
                 const other = structure.units[closeUnits.indices[i]];
-                if (!Box3D.overlaps(bbox, other.boundary.box)) continue;
+                if (other.elements.length > 3 && !Box3D.overlaps(bbox, other.boundary.box)) continue;
                 if (!validUnit(other) || unit.id >= other.id || !validUnitPair(unit, other)) continue;
 
                 if (other.elements.length >= unit.elements.length) callback(unit, other);

+ 8 - 1
src/mol-model/structure/structure/unit/bonds/inter-compute.ts

@@ -71,6 +71,8 @@ function findPairBonds(unitA: Unit.Atomic, unitB: Unit.Atomic, props: BondComput
     const testDistanceSq = (bRadius + maxRadius) * (bRadius + maxRadius);
 
     builder.startUnitPair(unitA.id, unitB.id);
+    const opKeyA = unitA.conformation.operator.key;
+    const opKeyB = unitB.conformation.operator.key;
 
     for (let _aI = 0 as StructureElement.UnitIndex; _aI < atomCount; _aI++) {
         const aI = atomsA[_aI];
@@ -80,7 +82,7 @@ function findPairBonds(unitA: Unit.Atomic, unitB: Unit.Atomic, props: BondComput
 
         if (!props.forceCompute && indexPairs) {
             const { maxDistance } = indexPairs;
-            const { offset, b, edgeProps: { order, distance, flag, key } } = indexPairs.bonds;
+            const { offset, b, edgeProps: { order, distance, flag, key, operatorA, operatorB } } = indexPairs.bonds;
 
             const srcA = sourceIndex.value(aI);
             const aeI = getElementIdx(type_symbolA.value(aI));
@@ -90,6 +92,11 @@ function findPairBonds(unitA: Unit.Atomic, unitB: Unit.Atomic, props: BondComput
                 const _bI = SortedArray.indexOf(unitB.elements, bI) as StructureElement.UnitIndex;
                 if (_bI < 0) continue;
 
+                const opA = operatorA[i];
+                const opB = operatorB[i];
+                if ((opA >= 0 && opA !== opKeyA && opA !== opKeyB) ||
+                    (opB >= 0 && opB !== opKeyB && opB !== opKeyA)) continue;
+
                 const beI = getElementIdx(type_symbolA.value(bI));
 
                 const d = distance[i];

+ 7 - 1
src/mol-model/structure/structure/unit/bonds/intra-compute.ts

@@ -55,7 +55,7 @@ function findIndexPairBonds(unit: Unit.Atomic) {
     const { type_symbol } = unit.model.atomicHierarchy.atoms;
     const atomCount = unit.elements.length;
     const { maxDistance } = indexPairs;
-    const { offset, b, edgeProps: { order, distance, flag, key } } = indexPairs.bonds;
+    const { offset, b, edgeProps: { order, distance, flag, key, operatorA, operatorB } } = indexPairs.bonds;
 
     const { atomSourceIndex: sourceIndex } = unit.model.atomicHierarchy;
     const { invertedIndex } = Model.getInvertedAtomSourceIndex(unit.model);
@@ -66,6 +66,8 @@ function findIndexPairBonds(unit: Unit.Atomic) {
     const orders: number[] = [];
     const keys: number[] = [];
 
+    const opKey = unit.conformation.operator.key;
+
     for (let _aI = 0 as StructureElement.UnitIndex; _aI < atomCount; _aI++) {
         const aI = atoms[_aI];
         const aeI = getElementIdx(type_symbol.value(aI));
@@ -80,6 +82,10 @@ function findIndexPairBonds(unit: Unit.Atomic) {
             const _bI = SortedArray.indexOf(unit.elements, bI) as StructureElement.UnitIndex;
             if (_bI < 0) continue;
 
+            const opA = operatorA[i];
+            const opB = operatorB[i];
+            if ((opA >= 0 && opA !== opKey) || (opB >= 0 && opB !== opKey)) continue;
+
             const beI = getElementIdx(type_symbol.value(bI));
 
             const d = distance[i];

+ 15 - 6
src/mol-plugin-state/helpers/structure-selection-query.ts

@@ -7,7 +7,7 @@
 
 import { CustomProperty } from '../../mol-model-props/common/custom-property';
 import { QueryContext, Structure, StructureQuery, StructureSelection, StructureProperties, StructureElement } from '../../mol-model/structure';
-import { BondType, NucleicBackboneAtoms, ProteinBackboneAtoms, SecondaryStructureType, AminoAcidNamesL, RnaBaseNames, DnaBaseNames, WaterNames, ElementSymbol, PolymerNames } from '../../mol-model/structure/model/types';
+import { BondType, NucleicBackboneAtoms, ProteinBackboneAtoms, SecondaryStructureType, AminoAcidNamesL, RnaBaseNames, DnaBaseNames, WaterNames, ElementSymbol, PolymerNames, CommonProteinCaps } from '../../mol-model/structure/model/types';
 import { PluginContext } from '../../mol-plugin/context';
 import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
 import { Expression } from '../../mol-script/language/expression';
@@ -350,14 +350,23 @@ const ligand = StructureSelectionQuery('Ligand', MS.struct.modifier.union([
                 ])
             ]),
         ]),
-        by: MS.struct.modifier.union([
+        by: MS.struct.combinator.merge([
+            MS.struct.modifier.union([
+                MS.struct.generator.atomGroups({
+                    'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'polymer']),
+                    'chain-test': MS.core.rel.eq([MS.ammp('objectPrimitive'), 'atomistic']),
+                    'residue-test': MS.core.set.has([
+                        MS.set(...SetUtils.toArray(PolymerNames)), MS.ammp('label_comp_id')
+                    ])
+                }),
+            ]),
             MS.struct.generator.atomGroups({
-                'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'polymer']),
                 'chain-test': MS.core.rel.eq([MS.ammp('objectPrimitive'), 'atomistic']),
                 'residue-test': MS.core.set.has([
-                    MS.set(...SetUtils.toArray(PolymerNames)), MS.ammp('label_comp_id')
-                ])
-            })
+                    MS.set(...SetUtils.toArray(CommonProteinCaps)),
+                    MS.ammp('label_comp_id'),
+                ]),
+            }),
         ])
     })
 ]), { category: StructureSelectionCategory.Type });

+ 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 - 1
src/mol-plugin-ui/skin/base/components/viewport.scss

@@ -4,7 +4,7 @@
     top: 0;
     right: 0;
     bottom: 0;
-    background: black;
+    background: $default-background;
 
     .msp-btn-link {
         background: rgba(0,0,0,0.2);

+ 2 - 0
src/mol-plugin-ui/structure/quick-styles.tsx

@@ -62,6 +62,7 @@ export class QuickStyles extends PurePluginUIComponent {
                         name: 'on',
                         params: { bias: 0.8, blurKernelSize: 15, radius: 5, samples: 32, resolutionScale: 1 }
                     },
+                    shadow: { name: 'off', params: {} },
                 }
             });
         }
@@ -86,6 +87,7 @@ export class QuickStyles extends PurePluginUIComponent {
                             ? pp.occlusion.params
                             : { bias: 0.8, blurKernelSize: 15, radius: 5, samples: 32, resolutionScale: 1 }
                     },
+                    shadow: { name: 'off', params: {} },
                 }
             });
         }

+ 3 - 0
src/mol-plugin-ui/viewport/simple-settings.tsx

@@ -59,6 +59,7 @@ const SimpleSettingsParams = {
     }, { pivot: 'color' }),
     lighting: PD.Group({
         occlusion: Canvas3DParams.postprocessing.params.occlusion,
+        shadow: Canvas3DParams.postprocessing.params.shadow,
         outline: Canvas3DParams.postprocessing.params.outline,
         fog: Canvas3DParams.cameraFog,
     }, { isFlat: true }),
@@ -114,6 +115,7 @@ const SimpleSettingsMapping = ParamMapping({
             },
             lighting: {
                 occlusion: canvas.postprocessing.occlusion,
+                shadow: canvas.postprocessing.shadow,
                 outline: canvas.postprocessing.outline,
                 fog: canvas.cameraFog,
             },
@@ -129,6 +131,7 @@ const SimpleSettingsMapping = ParamMapping({
         canvas.transparentBackground = s.background.transparent;
         canvas.renderer.backgroundColor = s.background.color;
         canvas.postprocessing.occlusion = s.lighting.occlusion;
+        canvas.postprocessing.shadow = s.lighting.shadow;
         canvas.postprocessing.outline = s.lighting.outline;
         canvas.postprocessing.background = s.background.style;
         canvas.cameraFog = s.lighting.fog;

+ 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) {

+ 1 - 1
src/mol-repr/util.ts

@@ -75,7 +75,7 @@ export type QualityThresholds = typeof DefaultQualityThresholds
 export function getStructureQuality(structure: Structure, tresholds: Partial<QualityThresholds> = {}): VisualQuality {
     const t = { ...DefaultQualityThresholds, ...tresholds };
     let score = structure.elementCount * t.elementCountFactor;
-    if (structure.isCoarseGrained) score *= t.coarseGrainedFactor;
+    if (structure.isCoarseGrained || structure.isCoarse) score *= t.coarseGrainedFactor;
     if (score > t.lowestElementCount) {
         return 'lowest';
     } else if (score > t.lowerElementCount) {

+ 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) {

+ 1 - 0
src/mol-script/language/symbol-table/structure-query.ts

@@ -273,6 +273,7 @@ const atomProperty = {
 
         sourceIndex: atomProp(Type.Num, 'Index of the atom/element in the input file.'),
         operatorName: atomProp(Type.Str, 'Name of the symmetry operator applied to this element.'),
+        operatorKey: atomProp(Type.Num, 'Key of the symmetry operator applied to this element.'),
         modelIndex: atomProp(Type.Num, 'Index of the model in the input file.'),
         modelLabel: atomProp(Type.Str, 'Label/header of the model in the input file.')
     },

+ 1 - 0
src/mol-script/runtime/query/table.ts

@@ -299,6 +299,7 @@ const symbols = [
     D(MolScript.structureQuery.atomProperty.core.z, atomProp(StructureProperties.atom.z)),
     D(MolScript.structureQuery.atomProperty.core.sourceIndex, atomProp(StructureProperties.atom.sourceIndex)),
     D(MolScript.structureQuery.atomProperty.core.operatorName, atomProp(StructureProperties.unit.operator_name)),
+    D(MolScript.structureQuery.atomProperty.core.operatorKey, atomProp(StructureProperties.unit.operator_key)),
     D(MolScript.structureQuery.atomProperty.core.modelIndex, atomProp(StructureProperties.unit.model_index)),
     D(MolScript.structureQuery.atomProperty.core.modelLabel, atomProp(StructureProperties.unit.model_label)),
     D(MolScript.structureQuery.atomProperty.core.atomKey, (ctx, xs) => {

+ 4 - 1
src/mol-script/script/mol-script/symbols.ts

@@ -1,7 +1,8 @@
 /**
- * Copyright (c) 2018 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>
  */
 
 import { UniqueArray } from '../../../mol-data/generic';
@@ -205,6 +206,7 @@ export const SymbolTable = [
             Alias(MolScript.structureQuery.atomProperty.core.z, 'atom.z'),
             Alias(MolScript.structureQuery.atomProperty.core.sourceIndex, 'atom.src-index'),
             Alias(MolScript.structureQuery.atomProperty.core.operatorName, 'atom.op-name'),
+            Alias(MolScript.structureQuery.atomProperty.core.operatorKey, 'atom.op-key'),
             Alias(MolScript.structureQuery.atomProperty.core.modelIndex, 'atom.model-index'),
             Alias(MolScript.structureQuery.atomProperty.core.modelLabel, 'atom.model-label'),
             Alias(MolScript.structureQuery.atomProperty.core.atomKey, 'atom.key'),
@@ -253,6 +255,7 @@ export const SymbolTable = [
             'Bond Properties',
             Alias(MolScript.structureQuery.bondProperty.order, 'bond.order'),
             Alias(MolScript.structureQuery.bondProperty.length, 'bond.length'),
+            Alias(MolScript.structureQuery.bondProperty.key, 'bond.key'),
             Alias(MolScript.structureQuery.bondProperty.atomA, 'bond.atom-a'),
             Alias(MolScript.structureQuery.bondProperty.atomB, 'bond.atom-b'),
             Macro(MSymbol('bond.is', Arguments.List(StructureQueryTypes.BondFlag), Type.Bool,

Some files were not shown because too many files changed in this diff